mindroot 9.3.0__py3-none-any.whl → 9.5.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 +63 -0
  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-old.js +385 -0
  35. mindroot/coreplugins/admin/static/js/registry-manager-publish-old-delete.js +166 -0
  36. mindroot/coreplugins/admin/static/js/registry-manager.js +351 -0
  37. mindroot/coreplugins/admin/static/js/registry-publish-section.js +377 -0
  38. mindroot/coreplugins/admin/static/js/registry-search-section.js +400 -0
  39. mindroot/coreplugins/admin/static/js/registry-search-section.js.bak +3 -0
  40. mindroot/coreplugins/admin/static/js/registry-settings.js +69 -0
  41. mindroot/coreplugins/admin/static/js/registry-shared-services.js +857 -0
  42. mindroot/coreplugins/admin/static/js/registry-simple-sections.js +85 -0
  43. mindroot/coreplugins/admin/static/js/secure-widget-manager.js +438 -0
  44. mindroot/coreplugins/admin/static/logo.png +0 -0
  45. mindroot/coreplugins/admin/templates/admin.jinja2 +275 -110
  46. mindroot/coreplugins/agent/Assistant/agent.json +27 -11
  47. mindroot/coreplugins/agent/agent.py +2 -2
  48. mindroot/coreplugins/agent/command_parser.py +25 -10
  49. mindroot/coreplugins/agent/templates/system.jinja2 +0 -12
  50. mindroot/coreplugins/chat/__init__.py +4 -1
  51. mindroot/coreplugins/chat/router.py +132 -20
  52. mindroot/coreplugins/chat/router_dedup_patch.py +20 -0
  53. mindroot/coreplugins/chat/services.py +31 -1
  54. mindroot/coreplugins/chat/static/css/action-fix.css +32 -0
  55. mindroot/coreplugins/chat/static/css/admin-custom.css +5 -3
  56. mindroot/coreplugins/chat/static/css/dark.css +24 -3
  57. mindroot/coreplugins/chat/static/css/default.css +24 -3
  58. mindroot/coreplugins/chat/static/css/main.css +1 -0
  59. mindroot/coreplugins/chat/static/js/action.js +137 -60
  60. mindroot/coreplugins/chat/static/js/chat-history.js +3 -0
  61. mindroot/coreplugins/chat/static/js/chat.js +59 -16
  62. mindroot/coreplugins/chat/static/js/chat.js.diff +221 -0
  63. mindroot/coreplugins/chat/static/js/chatform.js +2 -2
  64. mindroot/coreplugins/chat/static/site.webmanifest +1 -1
  65. mindroot/coreplugins/chat/templates/chat.jinja2 +3 -3
  66. mindroot/coreplugins/chat/widget_manager.py +139 -0
  67. mindroot/coreplugins/chat/widget_routes.py +287 -0
  68. mindroot/coreplugins/check_list/inject/admin.jinja2 +1 -1
  69. mindroot/coreplugins/email/__init__.py +2 -0
  70. mindroot/coreplugins/email/email_provider.py +2 -2
  71. mindroot/coreplugins/email/mod.py +100 -0
  72. mindroot/coreplugins/email/services.py +5 -3
  73. mindroot/coreplugins/email/smtp_handler.py +9 -3
  74. mindroot/coreplugins/email/test_email_service.py +75 -0
  75. mindroot/coreplugins/env_manager/mod.py +61 -25
  76. mindroot/coreplugins/home/router.py +37 -2
  77. mindroot/coreplugins/home/static/imgs/logo.png +0 -0
  78. mindroot/coreplugins/home/static/imgs/logo.png.bak +0 -0
  79. mindroot/coreplugins/home/static/imgs/logo_teal.png +0 -0
  80. mindroot/coreplugins/home/static/imgs/logo_teal2.png +0 -0
  81. mindroot/coreplugins/home/static/imgs/logo_teal_detailed.png +0 -0
  82. mindroot/coreplugins/home/static/imgs/logo_teal_python.png +0 -0
  83. mindroot/coreplugins/home/templates/home.jinja2 +15 -6
  84. mindroot/coreplugins/index/indices/default/index.json +6 -6
  85. mindroot/coreplugins/jwt_auth/middleware.py +47 -2
  86. mindroot/coreplugins/jwt_auth/mod.py +40 -17
  87. mindroot/coreplugins/l8n/__init__.py +6 -0
  88. mindroot/coreplugins/l8n/debug_loader.py +85 -0
  89. mindroot/coreplugins/l8n/debug_middleware.py +74 -0
  90. mindroot/coreplugins/l8n/l8n_constants.py +19 -0
  91. mindroot/coreplugins/l8n/language_detection.py +183 -0
  92. mindroot/coreplugins/l8n/middleware.py +151 -0
  93. mindroot/coreplugins/l8n/mod.py +277 -0
  94. mindroot/coreplugins/l8n/monkey_patch_to_delete.py +186 -0
  95. mindroot/coreplugins/l8n/test_enhanced.py +298 -0
  96. mindroot/coreplugins/l8n/test_l8n.py +95 -0
  97. mindroot/coreplugins/l8n/test_l8n_standalone.py +251 -0
  98. mindroot/coreplugins/l8n/test_middleware.py +272 -0
  99. mindroot/coreplugins/l8n/utils.py +232 -0
  100. mindroot/coreplugins/mcp_/__init__.py +14 -0
  101. mindroot/coreplugins/mcp_/catalog_commands.py +328 -0
  102. mindroot/coreplugins/mcp_/catalog_manager.py +263 -0
  103. mindroot/coreplugins/mcp_/dynamic_commands.py +154 -0
  104. mindroot/coreplugins/mcp_/mcp_manager.py +1031 -0
  105. mindroot/coreplugins/mcp_/mod.py +367 -0
  106. mindroot/coreplugins/mcp_/oauth_storage.py +144 -0
  107. mindroot/coreplugins/mcp_/server_installer.py +79 -0
  108. mindroot/coreplugins/mcp_/setup.py +26 -0
  109. mindroot/coreplugins/mcp_/test_dynamic_commands.py +134 -0
  110. mindroot/coreplugins/mcp_/testmcpclient.py +92 -0
  111. mindroot/coreplugins/persona/mod.py +12 -7
  112. mindroot/coreplugins/signup/templates/signup.jinja2 +1 -1
  113. mindroot/coreplugins/subscriptions/__init__.py +1 -0
  114. mindroot/coreplugins/subscriptions/mod.py +14 -3
  115. mindroot/coreplugins/subscriptions/router.py +3 -0
  116. mindroot/coreplugins/user_service/__init__.py +1 -2
  117. mindroot/coreplugins/user_service/admin_init.py +1 -0
  118. mindroot/coreplugins/user_service/email_service.py +72 -17
  119. mindroot/coreplugins/user_service/mod.py +10 -2
  120. mindroot/coreplugins/user_service/router.py +2 -0
  121. mindroot/lib/auth/api_key.py +28 -0
  122. mindroot/lib/cli/plugins.py +94 -0
  123. mindroot/lib/plugins/default_plugin_manifest.json +20 -0
  124. mindroot/lib/plugins/installation.py +5 -5
  125. mindroot/lib/plugins/l8n_static_handler.py +225 -0
  126. mindroot/lib/plugins/loader.py +33 -3
  127. mindroot/lib/plugins/loader_with_l8n.py +281 -0
  128. mindroot/lib/plugins/manifest.py +236 -24
  129. mindroot/lib/providers/commands.py +3 -1
  130. mindroot/lib/route_decorators.py +5 -5
  131. mindroot/lib/templates.py +183 -11
  132. mindroot/lib/utils/merge_arrays.py +1 -1
  133. mindroot/migrate.py +39 -20
  134. mindroot/registry/data_access.py +1 -1
  135. mindroot/server.py +42 -13
  136. mindroot/server_missing_normal_args.py +197 -0
  137. mindroot/server_prev.py +173 -0
  138. {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/METADATA +7 -2
  139. {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/RECORD +144 -112
  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.5.0.dist-info}/WHEEL +0 -0
  181. {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/entry_points.txt +0 -0
  182. {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/licenses/LICENSE +0 -0
  183. {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,23 @@
1
+ from fastapi import APIRouter
2
+ from lib.route_decorators import requires_role
3
+
4
+ # Import the separate route modules
5
+ from .plugin_routes import router as plugin_routes
6
+ from .mcp_routes import router as mcp_routes
7
+ from .mcp_catalog_routes import router as mcp_catalog_routes
8
+ from .registry_settings_routes import router as registry_settings_routes
9
+ from .mcp_publish_routes import router as mcp_publish_routes
10
+ from .mcp_registry_routes import router as mcp_registry_routes
11
+
12
+ # Create main router with admin role requirement
13
+ router = APIRouter(
14
+ dependencies=[requires_role('admin')]
15
+ )
16
+
17
+ # Include all the sub-routers
18
+ router.include_router(plugin_routes, tags=["plugins"])
19
+ router.include_router(mcp_routes, tags=["mcp"])
20
+ router.include_router(mcp_catalog_routes, tags=["mcp-catalog"])
21
+ router.include_router(registry_settings_routes, tags=["registry-settings"])
22
+ router.include_router(mcp_publish_routes, tags=["mcp-publish"])
23
+ router.include_router(mcp_registry_routes, tags=["mcp-registry"])
@@ -0,0 +1,145 @@
1
+ from fastapi import APIRouter, HTTPException, Depends, Header
2
+ from pydantic import BaseModel
3
+ import json
4
+ from typing import Optional, List
5
+ from lib import plugins
6
+ from . import plugin_manager
7
+ from lib.auth.cognito import get_current_user
8
+ from lib.config import get_settings
9
+
10
+ # Import MCP components
11
+ try:
12
+ from mindroot.coreplugins.mcp.enhanced_mod import enhanced_mcp_manager
13
+ from mindroot.coreplugins.mcp.mod import MCPServer
14
+ except ImportError:
15
+ # Mock objects if MCP plugin is not fully installed, to prevent startup crash
16
+ enhanced_mcp_manager = None
17
+ MCPServer = None
18
+
19
+ router = APIRouter()
20
+
21
+ class PluginUpdateRequest(BaseModel):
22
+ plugins: dict
23
+
24
+ @router.post("/update-plugins")
25
+ def update_plugins(request: PluginUpdateRequest):
26
+ try:
27
+ with open('plugins.json', 'r') as file:
28
+ plugins_data = json.load(file)
29
+
30
+ for plugin in plugins_data:
31
+ if plugin['name'] in request.plugins:
32
+ plugin['enabled'] = request.plugins[plugin['name']]
33
+
34
+ with open('plugins.json', 'w') as file:
35
+ json.dump(plugins_data, file, indent=2)
36
+
37
+ plugins.load('data/plugin_manifest.json')
38
+
39
+ return {"message": "Plugins updated successfully"}
40
+ except Exception as e:
41
+ raise HTTPException(status_code=500, detail=str(e))
42
+
43
+ @router.get("/get-plugins")
44
+ async def get_plugins():
45
+ try:
46
+ return plugins.load_plugin_manifest()
47
+ except Exception as e:
48
+ raise HTTPException(status_code=500, detail=str(e))
49
+
50
+ class GithubPublishRequest(BaseModel):
51
+ repo: str
52
+ registry_url: str
53
+
54
+ @router.post("/plugins/publish_from_github")
55
+ async def publish_from_github(request: GithubPublishRequest, authorization: Optional[str] = Header(None), user: dict = Depends(get_current_user)):
56
+ if not user:
57
+ raise HTTPException(status_code=401, detail="Not authorized for Mindroot Admin")
58
+
59
+ registry_token = None
60
+ if authorization and authorization.startswith("Bearer "):
61
+ registry_token = authorization.split(" ")[1]
62
+
63
+ if not registry_token:
64
+ raise HTTPException(status_code=401, detail="Registry auth token not provided")
65
+
66
+ try:
67
+ result = await plugin_manager.publish_plugin_from_github(request.repo, registry_token, request.registry_url)
68
+ return {"message": f"Plugin '{result.get('title')}' published successfully!", "data": result}
69
+ except Exception as e:
70
+ import traceback
71
+ traceback.print_exc()
72
+ raise HTTPException(status_code=500, detail=str(e))
73
+
74
+ class GithubPublishRequest(BaseModel):
75
+ repo: str
76
+
77
+ @router.post("/plugins/publish_from_github")
78
+ async def publish_from_github(request: GithubPublishRequest, user: dict = Depends(get_current_user)):
79
+ if not user:
80
+ raise HTTPException(status_code=401, detail="Not authorized")
81
+ try:
82
+ result = await plugin_manager.publish_plugin_from_github(request.repo, user)
83
+ return {"message": "Plugin published successfully", "data": result}
84
+ except Exception as e:
85
+ raise HTTPException(status_code=500, detail=str(e))
86
+
87
+ # --- MCP Integration Routes ---
88
+
89
+ class McpServerRequest(BaseModel):
90
+ server_name: str
91
+
92
+ @router.get("/mcp/list")
93
+ async def list_mcp_servers(user: dict = Depends(get_current_user)):
94
+ if not enhanced_mcp_manager:
95
+ raise HTTPException(status_code=501, detail="MCP Plugin not available")
96
+ servers = [s.dict() for s in enhanced_mcp_manager.servers.values()]
97
+ return {"success": True, "data": servers}
98
+
99
+ @router.post("/mcp/add")
100
+ async def add_mcp_server(server_config: MCPServer, user: dict = Depends(get_current_user)):
101
+ if not enhanced_mcp_manager:
102
+ raise HTTPException(status_code=501, detail="MCP Plugin not available")
103
+ try:
104
+ # The server_config is a full MCPServer object from the registry
105
+ enhanced_mcp_manager.add_server(server_config.name, server_config)
106
+ return {"success": True, "message": f"Server '{server_config.name}' added."}
107
+ except Exception as e:
108
+ raise HTTPException(status_code=500, detail=str(e))
109
+
110
+ @router.post("/mcp/remove")
111
+ async def remove_mcp_server(request: McpServerRequest, user: dict = Depends(get_current_user)):
112
+ if not enhanced_mcp_manager:
113
+ raise HTTPException(status_code=501, detail="MCP Plugin not available")
114
+ try:
115
+ await enhanced_mcp_manager.remove_server(request.server_name)
116
+ return {"success": True, "message": f"Server '{request.server_name}' removed."}
117
+ except Exception as e:
118
+ raise HTTPException(status_code=500, detail=str(e))
119
+
120
+ @router.post("/mcp/connect")
121
+ async def connect_mcp_server(request: McpServerRequest, user: dict = Depends(get_current_user)):
122
+ if not enhanced_mcp_manager:
123
+ raise HTTPException(status_code=501, detail="MCP Plugin not available")
124
+ try:
125
+ success = await enhanced_mcp_manager.connect_server(request.server_name)
126
+ if success:
127
+ return {"success": True, "message": f"Server '{request.server_name}' connected."}
128
+ else:
129
+ raise HTTPException(status_code=500, detail=f"Failed to connect to server '{request.server_name}'. Check logs.")
130
+ except Exception as e:
131
+ raise HTTPException(status_code=500, detail=str(e))
132
+
133
+ @router.post("/mcp/disconnect")
134
+ async def disconnect_mcp_server(request: McpServerRequest, user: dict = Depends(get_current_user)):
135
+ if not enhanced_mcp_manager:
136
+ raise HTTPException(status_code=501, detail="MCP Plugin not available")
137
+ try:
138
+ success = await enhanced_mcp_manager.disconnect_server(request.server_name)
139
+ if success:
140
+ return {"success": True, "message": f"Server '{request.server_name}' disconnected."}
141
+ else:
142
+ raise HTTPException(status_code=500, detail=f"Failed to disconnect from server '{request.server_name}'.")
143
+ except Exception as e:
144
+ raise HTTPException(status_code=500, detail=str(e))
145
+
@@ -0,0 +1,114 @@
1
+ from fastapi import APIRouter, HTTPException, Header
2
+ from pydantic import BaseModel
3
+ import json
4
+ import os
5
+ from typing import Optional
6
+ from lib import plugins
7
+ from . import plugin_manager
8
+ from lib.route_decorators import requires_role
9
+
10
+ # Create router with admin role requirement
11
+ router = APIRouter(
12
+ dependencies=[requires_role('admin')]
13
+ )
14
+
15
+ class PluginUpdateRequest(BaseModel):
16
+ plugins: dict
17
+
18
+ class GithubPublishRequest(BaseModel):
19
+ repo: str
20
+ registry_url: Optional[str] = None
21
+
22
+ # --- Plugin Management Routes ---
23
+
24
+ @router.post("/update-plugins")
25
+ def update_plugins(request: PluginUpdateRequest):
26
+ """Update plugin enabled/disabled status."""
27
+ try:
28
+ with open('plugins.json', 'r') as file:
29
+ plugins_data = json.load(file)
30
+
31
+ for plugin in plugins_data:
32
+ if plugin['name'] in request.plugins:
33
+ plugin['enabled'] = request.plugins[plugin['name']]
34
+
35
+ with open('plugins.json', 'w') as file:
36
+ json.dump(plugins_data, file, indent=2)
37
+
38
+ plugins.load('data/plugin_manifest.json')
39
+
40
+ return {"message": "Plugins updated successfully"}
41
+ except Exception as e:
42
+ raise HTTPException(status_code=500, detail=str(e))
43
+
44
+ @router.get("/get-plugins")
45
+ async def get_plugins():
46
+ """Get list of all plugins."""
47
+ try:
48
+ return plugins.load_plugin_manifest()
49
+ except Exception as e:
50
+ raise HTTPException(status_code=500, detail=str(e))
51
+
52
+ @router.post("/plugins/publish_from_github")
53
+ async def publish_plugin_from_github(
54
+ request: GithubPublishRequest,
55
+ authorization: Optional[str] = Header(None)
56
+ ):
57
+ """Publish a plugin from GitHub repository to the registry.
58
+
59
+ This endpoint allows publishing a plugin by simply providing the GitHub
60
+ repository in the format 'username/repo'. It will fetch the plugin_info.json
61
+ from the repository and publish it to the configured registry.
62
+
63
+ The registry token can be provided via:
64
+ 1. Authorization header: "Bearer <token>"
65
+ 2. REGISTRY_TOKEN environment variable
66
+ 3. registry_token in data/registry_settings.json
67
+ """
68
+ try:
69
+ # Get registry token from multiple sources
70
+ registry_token = None
71
+
72
+ # 1. Try Authorization header
73
+ if authorization and authorization.startswith("Bearer "):
74
+ registry_token = authorization.split(" ")[1]
75
+
76
+ # 2. Try environment variable
77
+ if not registry_token:
78
+ registry_token = os.getenv('REGISTRY_TOKEN')
79
+
80
+ # 3. Try settings file
81
+ if not registry_token:
82
+ try:
83
+ settings_file = 'data/registry_settings.json'
84
+ if os.path.exists(settings_file):
85
+ with open(settings_file, 'r') as f:
86
+ settings = json.load(f)
87
+ registry_token = settings.get('registry_token')
88
+ except Exception as e:
89
+ print(f"Error reading registry settings: {e}")
90
+
91
+ if not registry_token:
92
+ raise HTTPException(
93
+ status_code=401,
94
+ detail="Registry authentication token not provided. Please provide via Authorization header, REGISTRY_TOKEN environment variable, or registry_settings.json file."
95
+ )
96
+
97
+ # Use the existing plugin_manager functionality
98
+ registry_url = request.registry_url or "https://registry.mindroot.io"
99
+
100
+ result = await plugin_manager.publish_plugin_from_github(
101
+ request.repo,
102
+ registry_token,
103
+ registry_url
104
+ )
105
+
106
+ return {
107
+ "success": True,
108
+ "message": f"Plugin '{result.get('title', request.repo)}' published successfully!",
109
+ "data": result
110
+ }
111
+ except Exception as e:
112
+ import traceback
113
+ traceback.print_exc()
114
+ raise HTTPException(status_code=500, detail=str(e))
@@ -0,0 +1,140 @@
1
+ from fastapi import APIRouter, HTTPException
2
+ import json
3
+ import os
4
+ from lib.route_decorators import requires_role
5
+
6
+ # Create router with admin role requirement
7
+ router = APIRouter(
8
+ dependencies=[requires_role('admin')]
9
+ )
10
+
11
+ # --- Registry Settings Routes ---
12
+
13
+ @router.get("/registry/settings")
14
+ async def get_registry_settings():
15
+ """Get registry settings including token status."""
16
+ try:
17
+ settings_file = 'data/registry_settings.json'
18
+ settings = {}
19
+
20
+ if os.path.exists(settings_file):
21
+ with open(settings_file, 'r') as f:
22
+ settings = json.load(f)
23
+
24
+ # Don't return the actual token, just indicate if it's set
25
+ return {
26
+ "success": True,
27
+ "data": {
28
+ "registry_url": settings.get("registry_url", "https://registry.mindroot.io"),
29
+ "has_token": bool(settings.get("registry_token")),
30
+ "token_source": "file" if settings.get("registry_token") else "env" if os.getenv('REGISTRY_TOKEN') else "none"
31
+ }
32
+ }
33
+ except Exception as e:
34
+ raise HTTPException(status_code=500, detail=str(e))
35
+
36
+ @router.post("/registry/settings")
37
+ async def update_registry_settings(settings_data: dict):
38
+ """Update registry settings."""
39
+ try:
40
+ settings_file = 'data/registry_settings.json'
41
+
42
+ # Ensure data directory exists
43
+ os.makedirs('data', exist_ok=True)
44
+
45
+ # Load existing settings
46
+ settings = {}
47
+ if os.path.exists(settings_file):
48
+ with open(settings_file, 'r') as f:
49
+ settings = json.load(f)
50
+
51
+ # Update with new data
52
+ settings.update(settings_data)
53
+
54
+ # Save updated settings
55
+ with open(settings_file, 'w') as f:
56
+ json.dump(settings, f, indent=2)
57
+
58
+ return {
59
+ "success": True,
60
+ "message": "Registry settings updated successfully.",
61
+ "data": {
62
+ "registry_url": settings.get("registry_url", "https://registry.mindroot.io"),
63
+ "has_token": bool(settings.get("registry_token")),
64
+ "token_source": "file" if settings.get("registry_token") else "env" if os.getenv('REGISTRY_TOKEN') else "none"
65
+ }
66
+ }
67
+ except Exception as e:
68
+ raise HTTPException(status_code=500, detail=str(e))
69
+
70
+ @router.delete("/registry/settings/token")
71
+ async def clear_registry_token():
72
+ """Clear the stored registry token."""
73
+ try:
74
+ settings_file = 'data/registry_settings.json'
75
+
76
+ if os.path.exists(settings_file):
77
+ with open(settings_file, 'r') as f:
78
+ settings = json.load(f)
79
+
80
+ # Remove token if it exists
81
+ if 'registry_token' in settings:
82
+ del settings['registry_token']
83
+
84
+ with open(settings_file, 'w') as f:
85
+ json.dump(settings, f, indent=2)
86
+
87
+ return {
88
+ "success": True,
89
+ "message": "Registry token cleared successfully."
90
+ }
91
+ except Exception as e:
92
+ raise HTTPException(status_code=500, detail=str(e))
93
+
94
+ @router.post("/registry/test-connection")
95
+ async def test_registry_connection():
96
+ """Test connection to the registry."""
97
+ try:
98
+ import httpx
99
+
100
+ settings_file = 'data/registry_settings.json'
101
+ registry_url = "https://registry.mindroot.io"
102
+
103
+ if os.path.exists(settings_file):
104
+ with open(settings_file, 'r') as f:
105
+ settings = json.load(f)
106
+ registry_url = settings.get("registry_url", registry_url)
107
+
108
+ # Test connection to registry
109
+ async with httpx.AsyncClient(timeout=10.0) as client:
110
+ response = await client.get(f"{registry_url}/stats")
111
+
112
+ if response.status_code == 200:
113
+ stats = response.json()
114
+ return {
115
+ "success": True,
116
+ "message": "Successfully connected to registry.",
117
+ "data": {
118
+ "registry_url": registry_url,
119
+ "stats": stats
120
+ }
121
+ }
122
+ else:
123
+ return {
124
+ "success": False,
125
+ "message": f"Registry returned status code {response.status_code}",
126
+ "data": {
127
+ "registry_url": registry_url,
128
+ "status_code": response.status_code
129
+ }
130
+ }
131
+
132
+ except Exception as e:
133
+ return {
134
+ "success": False,
135
+ "message": f"Failed to connect to registry: {str(e)}",
136
+ "data": {
137
+ "registry_url": registry_url,
138
+ "error": str(e)
139
+ }
140
+ }
@@ -1,47 +1,148 @@
1
1
  import nanoid
2
+ import os
3
+ import json
4
+ from pathlib import Path
2
5
  from fastapi import APIRouter, HTTPException, Request
3
- from fastapi.responses import HTMLResponse, RedirectResponse
6
+ from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse, Response
4
7
  from lib.plugins import list_enabled
5
8
  from lib.templates import render
6
9
  from .plugin_manager import router as plugin_manager_router
7
10
  from lib.route_decorators import requires_role
11
+ from .mod import get_git_version_info
8
12
 
9
- # Create admin router with role requirement for all routes under it
10
- router = APIRouter(
11
- dependencies=[requires_role('admin')]
12
- )
13
+ # Create separate routers for public and admin routes
14
+ public_router = APIRouter() # No dependencies - for OAuth callbacks etc.
15
+ admin_router = APIRouter(dependencies=[requires_role('admin')]) # Admin only
13
16
 
14
- router.include_router(plugin_manager_router, prefix="/plugin-manager", tags=["plugin-manager"])
17
+ # === PUBLIC ROUTES (no authentication required) ===
18
+ # Import and include the OAuth callback router in public router
19
+ from .oauth_callback_router import router as oauth_callback_router
20
+ public_router.include_router(oauth_callback_router)
15
21
 
16
- @router.get("/admin", response_class=HTMLResponse)
22
+ # === ADMIN ROUTES (authentication required) ===
23
+ admin_router.include_router(plugin_manager_router, prefix="/plugin-manager", tags=["plugin-manager"])
24
+
25
+ @admin_router.get("/admin", response_class=HTMLResponse)
17
26
  async def get_admin_html():
18
27
  log_id = nanoid.generate()
19
28
  plugins = list_enabled()
20
29
  html = await render('admin', {"log_id": log_id})
21
30
  return html
22
31
 
32
+ @admin_router.post("/admin/get-version-info")
33
+ async def get_version_info():
34
+ """Get version information, trying git first, then falling back to cached file."""
35
+ try:
36
+ # Get the path to this file to determine where to store version.txt
37
+ current_file = Path(__file__)
38
+ version_file = current_file.parent / "version.txt"
39
+
40
+ # Try to get fresh git info using the command
41
+ try:
42
+ if get_git_version_info:
43
+ git_info = await get_git_version_info()
44
+ if git_info:
45
+ # Write to version.txt
46
+ with open(version_file, 'w') as f:
47
+ json.dump(git_info, f, indent=2)
48
+ return JSONResponse(git_info)
49
+ except Exception as e:
50
+ print(f"Failed to get git info: {e}")
51
+
52
+ # Fall back to reading from version.txt
53
+ if version_file.exists():
54
+ with open(version_file, 'r') as f:
55
+ cached_info = json.load(f)
56
+ # Add note that this is cached
57
+ cached_info['note'] = 'Cached version (git not available)'
58
+ return JSONResponse(cached_info)
59
+
60
+ # No version info available
61
+ return JSONResponse({
62
+ 'commit_hash': 'Unknown',
63
+ 'commit_date': 'Unknown',
64
+ 'retrieved_at': 'Unknown',
65
+ 'note': 'Version information not available'
66
+ })
67
+
68
+ except Exception as e:
69
+ raise HTTPException(status_code=500, detail=f"Error getting version info: {str(e)}")
23
70
 
24
71
  from lib.logging.log_router import router as log_router
25
- router.include_router(log_router)
72
+ admin_router.include_router(log_router)
26
73
 
27
74
  from .command_router import router as command_router
28
- router.include_router(command_router)
75
+ admin_router.include_router(command_router)
29
76
 
30
77
  from .settings_router import router as settings_router
31
- router.include_router(settings_router)
78
+ admin_router.include_router(settings_router)
79
+
80
+ # Use the fixed plugin router instead of the old one
81
+ from .plugin_router_fixed import router as plugin_router_fixed
82
+ admin_router.include_router(plugin_router_fixed, prefix="/admin", tags=["plugins", "mcp"])
32
83
 
84
+ # Keep the old plugin router for backward compatibility if needed
33
85
  from .plugin_router import router as plugin_router
34
- router.include_router(plugin_router)
86
+ admin_router.include_router(plugin_router, prefix="/admin/legacy", tags=["legacy-plugins"])
35
87
 
36
88
  from .persona_router import router as persona_router
37
- router.include_router(persona_router)
89
+ admin_router.include_router(persona_router)
38
90
 
39
91
  from .agent_router import router as agent_router
40
- router.include_router(agent_router)
92
+ admin_router.include_router(agent_router)
41
93
 
42
94
  from .server_router import router as server_router
43
- router.include_router(server_router, prefix="/admin/server", tags=["server"])
95
+ admin_router.include_router(server_router, prefix="/admin/server", tags=["server"])
44
96
 
45
97
  # Import and include the env_manager router
46
98
  from coreplugins.env_manager.router import router as env_manager_router
47
- router.include_router(env_manager_router)
99
+ admin_router.include_router(env_manager_router)
100
+
101
+ @admin_router.post("/admin/update-mindroot")
102
+ async def update_mindroot():
103
+ """Update MindRoot using pip install --upgrade mindroot"""
104
+ import subprocess
105
+ import sys
106
+
107
+ try:
108
+ # Run pip install --upgrade mindroot in the current environment
109
+ result = subprocess.run(
110
+ [sys.executable, "-m", "pip", "install", "--upgrade", "mindroot"],
111
+ capture_output=True,
112
+ text=True,
113
+ timeout=300 # 5 minute timeout
114
+ )
115
+
116
+ if result.returncode == 0:
117
+ return JSONResponse({
118
+ "success": True,
119
+ "message": "MindRoot updated successfully",
120
+ "output": result.stdout,
121
+ "note": "Restart the application to use the updated version"
122
+ })
123
+ else:
124
+ return JSONResponse({
125
+ "success": False,
126
+ "message": "Failed to update MindRoot",
127
+ "error": result.stderr,
128
+ "output": result.stdout
129
+ })
130
+
131
+ except subprocess.TimeoutExpired:
132
+ return JSONResponse({
133
+ "success": False,
134
+ "message": "Update timed out after 5 minutes",
135
+ "error": "Process timed out"
136
+ })
137
+ except Exception as e:
138
+ return JSONResponse({
139
+ "success": False,
140
+ "message": "Error during update",
141
+ "error": str(e)
142
+ })
143
+
144
+ # === MAIN ROUTER COMBINING PUBLIC AND ADMIN ===
145
+ # Create main router that combines both public and admin routes
146
+ router = APIRouter()
147
+ router.include_router(public_router) # Public routes first (no auth)
148
+ router.include_router(admin_router) # Admin routes (with auth)
@@ -71,7 +71,7 @@ async def get_service_models_from_providers(timeout: float = 500.0, context=None
71
71
  print(f"Querying {len(providers)} providers for service models...")
72
72
  provider_tasks = [query_provider(provider) for provider in providers]
73
73
  provider_results = await asyncio.gather(*provider_tasks)
74
- print(provider_results)
74
+ #print(provider_results)
75
75
  # Organize results by service, then by provider
76
76
  service_models: Dict[str, Dict[str, List[str]]] = {}
77
77
 
@@ -110,6 +110,7 @@ async def get_providers():
110
110
  @router.get('/commands', response_model=Dict)
111
111
  async def get_commands():
112
112
  print("retrieving commands")
113
+ print(f"settings_router: command_manager instance ID: {id(command_manager)}")
113
114
  #funcs = command_manager.get_functions()
114
115
  funcs = command_manager.get_detailed_functions()
115
116
  print("funcs is")