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.
- mindroot/coreplugins/admin/__init__.py +3 -1
- mindroot/coreplugins/admin/agent_router.py +250 -7
- mindroot/coreplugins/admin/asset_manager.py +164 -0
- mindroot/coreplugins/admin/command_router.py +236 -1
- mindroot/coreplugins/admin/mcp_catalog_routes.py +156 -0
- mindroot/coreplugins/admin/mcp_publish_routes.py +450 -0
- mindroot/coreplugins/admin/mcp_registry_routes.py +495 -0
- mindroot/coreplugins/admin/mcp_routes.py +216 -0
- mindroot/coreplugins/admin/mod.py +62 -0
- mindroot/coreplugins/admin/oauth_callback_router.py +84 -0
- mindroot/coreplugins/admin/persona_handler.py +15 -6
- mindroot/coreplugins/admin/persona_router.py +158 -2
- mindroot/coreplugins/admin/plugin_manager.py +63 -0
- mindroot/coreplugins/admin/plugin_router_fixed.py +23 -0
- mindroot/coreplugins/admin/plugin_router_new_not_working.py +145 -0
- mindroot/coreplugins/admin/plugin_routes.py +114 -0
- mindroot/coreplugins/admin/registry_settings_routes.py +140 -0
- mindroot/coreplugins/admin/router.py +116 -15
- mindroot/coreplugins/admin/service_models.py +1 -1
- mindroot/coreplugins/admin/settings_router.py +1 -0
- mindroot/coreplugins/admin/static/css/admin-custom.css +357 -2
- mindroot/coreplugins/admin/static/css/dark.css +1 -0
- mindroot/coreplugins/admin/static/css/default.css +4 -0
- mindroot/coreplugins/admin/static/js/about-info.js +367 -0
- mindroot/coreplugins/admin/static/js/agent-form.js +83 -3
- mindroot/coreplugins/admin/static/js/api-key-script.js +307 -0
- mindroot/coreplugins/admin/static/js/mcp-manager.js +348 -0
- mindroot/coreplugins/admin/static/js/mcp-publisher.js +780 -0
- mindroot/coreplugins/admin/static/js/persona-editor.js +34 -5
- mindroot/coreplugins/admin/static/js/plugin-toggle.js +1 -1
- mindroot/coreplugins/admin/static/js/recommended-plugin-install.js +63 -0
- mindroot/coreplugins/admin/static/js/registry-auth-section.js +132 -0
- mindroot/coreplugins/admin/static/js/registry-manager-base.js +613 -0
- mindroot/coreplugins/admin/static/js/registry-manager-old.js +385 -0
- mindroot/coreplugins/admin/static/js/registry-manager-publish-old-delete.js +166 -0
- mindroot/coreplugins/admin/static/js/registry-manager.js +351 -0
- mindroot/coreplugins/admin/static/js/registry-publish-section.js +377 -0
- mindroot/coreplugins/admin/static/js/registry-search-section.js +400 -0
- mindroot/coreplugins/admin/static/js/registry-search-section.js.bak +3 -0
- mindroot/coreplugins/admin/static/js/registry-settings.js +69 -0
- mindroot/coreplugins/admin/static/js/registry-shared-services.js +857 -0
- mindroot/coreplugins/admin/static/js/registry-simple-sections.js +85 -0
- mindroot/coreplugins/admin/static/js/secure-widget-manager.js +438 -0
- mindroot/coreplugins/admin/static/logo.png +0 -0
- mindroot/coreplugins/admin/templates/admin.jinja2 +275 -110
- mindroot/coreplugins/agent/Assistant/agent.json +27 -11
- mindroot/coreplugins/agent/agent.py +2 -2
- mindroot/coreplugins/agent/command_parser.py +25 -10
- mindroot/coreplugins/agent/templates/system.jinja2 +0 -12
- mindroot/coreplugins/chat/__init__.py +4 -1
- mindroot/coreplugins/chat/router.py +132 -20
- mindroot/coreplugins/chat/router_dedup_patch.py +20 -0
- mindroot/coreplugins/chat/services.py +31 -1
- mindroot/coreplugins/chat/static/css/action-fix.css +32 -0
- mindroot/coreplugins/chat/static/css/admin-custom.css +5 -3
- mindroot/coreplugins/chat/static/css/dark.css +24 -3
- mindroot/coreplugins/chat/static/css/default.css +24 -3
- mindroot/coreplugins/chat/static/css/main.css +1 -0
- mindroot/coreplugins/chat/static/js/action.js +137 -60
- mindroot/coreplugins/chat/static/js/chat-history.js +3 -0
- mindroot/coreplugins/chat/static/js/chat.js +59 -16
- mindroot/coreplugins/chat/static/js/chat.js.diff +221 -0
- mindroot/coreplugins/chat/static/js/chatform.js +2 -2
- mindroot/coreplugins/chat/static/site.webmanifest +1 -1
- mindroot/coreplugins/chat/templates/chat.jinja2 +3 -3
- mindroot/coreplugins/chat/widget_manager.py +139 -0
- mindroot/coreplugins/chat/widget_routes.py +287 -0
- mindroot/coreplugins/check_list/inject/admin.jinja2 +1 -1
- mindroot/coreplugins/email/__init__.py +2 -0
- mindroot/coreplugins/email/email_provider.py +2 -2
- mindroot/coreplugins/email/mod.py +100 -0
- mindroot/coreplugins/email/services.py +5 -3
- mindroot/coreplugins/email/smtp_handler.py +9 -3
- mindroot/coreplugins/email/test_email_service.py +75 -0
- mindroot/coreplugins/env_manager/mod.py +61 -25
- mindroot/coreplugins/home/router.py +37 -2
- mindroot/coreplugins/home/static/imgs/logo.png +0 -0
- mindroot/coreplugins/home/static/imgs/logo.png.bak +0 -0
- mindroot/coreplugins/home/static/imgs/logo_teal.png +0 -0
- mindroot/coreplugins/home/static/imgs/logo_teal2.png +0 -0
- mindroot/coreplugins/home/static/imgs/logo_teal_detailed.png +0 -0
- mindroot/coreplugins/home/static/imgs/logo_teal_python.png +0 -0
- mindroot/coreplugins/home/templates/home.jinja2 +15 -6
- mindroot/coreplugins/index/indices/default/index.json +6 -6
- mindroot/coreplugins/jwt_auth/middleware.py +47 -2
- mindroot/coreplugins/jwt_auth/mod.py +40 -17
- mindroot/coreplugins/l8n/__init__.py +6 -0
- mindroot/coreplugins/l8n/debug_loader.py +85 -0
- mindroot/coreplugins/l8n/debug_middleware.py +74 -0
- mindroot/coreplugins/l8n/l8n_constants.py +19 -0
- mindroot/coreplugins/l8n/language_detection.py +183 -0
- mindroot/coreplugins/l8n/middleware.py +151 -0
- mindroot/coreplugins/l8n/mod.py +277 -0
- mindroot/coreplugins/l8n/monkey_patch_to_delete.py +186 -0
- mindroot/coreplugins/l8n/test_enhanced.py +298 -0
- mindroot/coreplugins/l8n/test_l8n.py +95 -0
- mindroot/coreplugins/l8n/test_l8n_standalone.py +251 -0
- mindroot/coreplugins/l8n/test_middleware.py +272 -0
- mindroot/coreplugins/l8n/utils.py +232 -0
- mindroot/coreplugins/mcp_/__init__.py +14 -0
- mindroot/coreplugins/mcp_/catalog_commands.py +328 -0
- mindroot/coreplugins/mcp_/catalog_manager.py +263 -0
- mindroot/coreplugins/mcp_/dynamic_commands.py +154 -0
- mindroot/coreplugins/mcp_/mcp_manager.py +1031 -0
- mindroot/coreplugins/mcp_/mod.py +367 -0
- mindroot/coreplugins/mcp_/oauth_storage.py +144 -0
- mindroot/coreplugins/mcp_/server_installer.py +79 -0
- mindroot/coreplugins/mcp_/setup.py +26 -0
- mindroot/coreplugins/mcp_/test_dynamic_commands.py +134 -0
- mindroot/coreplugins/mcp_/testmcpclient.py +92 -0
- mindroot/coreplugins/persona/mod.py +12 -7
- mindroot/coreplugins/signup/templates/signup.jinja2 +1 -1
- mindroot/coreplugins/subscriptions/__init__.py +1 -0
- mindroot/coreplugins/subscriptions/mod.py +14 -3
- mindroot/coreplugins/subscriptions/router.py +3 -0
- mindroot/coreplugins/user_service/__init__.py +1 -2
- mindroot/coreplugins/user_service/admin_init.py +1 -0
- mindroot/coreplugins/user_service/email_service.py +72 -17
- mindroot/coreplugins/user_service/mod.py +10 -2
- mindroot/coreplugins/user_service/router.py +2 -0
- mindroot/lib/auth/api_key.py +28 -0
- mindroot/lib/cli/plugins.py +94 -0
- mindroot/lib/plugins/default_plugin_manifest.json +20 -0
- mindroot/lib/plugins/installation.py +5 -5
- mindroot/lib/plugins/l8n_static_handler.py +225 -0
- mindroot/lib/plugins/loader.py +33 -3
- mindroot/lib/plugins/loader_with_l8n.py +281 -0
- mindroot/lib/plugins/manifest.py +236 -24
- mindroot/lib/providers/commands.py +3 -1
- mindroot/lib/route_decorators.py +5 -5
- mindroot/lib/templates.py +183 -11
- mindroot/lib/utils/merge_arrays.py +1 -1
- mindroot/migrate.py +39 -20
- mindroot/registry/data_access.py +1 -1
- mindroot/server.py +42 -13
- mindroot/server_missing_normal_args.py +197 -0
- mindroot/server_prev.py +173 -0
- {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/METADATA +7 -2
- {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/RECORD +144 -112
- mindroot/coreplugins/admin/static/favicon/about.txt +0 -6
- mindroot/coreplugins/admin/static/favicon/android-chrome-512x512.png +0 -0
- mindroot/coreplugins/admin/static/favicon/apple-touch-icon.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon-16x16.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon-32x32.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon.ico +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/about.txt +0 -6
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon.ico +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/site.webmanifest +0 -1
- mindroot/coreplugins/admin/static/favicon/logo.png +0 -0
- mindroot/coreplugins/admin/static/favicon/site.webmanifest +0 -1
- mindroot/coreplugins/admin/static/js/backup/agent-editor.js +0 -186
- mindroot/coreplugins/admin/static/js/backup/agent-form.js +0 -1133
- mindroot/coreplugins/admin/static/js/backup/agent-list.js +0 -94
- mindroot/coreplugins/chat/static/favicon/about.txt +0 -6
- mindroot/coreplugins/chat/static/favicon/android-chrome-192x192.png +0 -0
- mindroot/coreplugins/chat/static/favicon/android-chrome-512x512.png +0 -0
- mindroot/coreplugins/chat/static/favicon/apple-touch-icon.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon-16x16.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon-32x32.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon.ico +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/about.txt +0 -6
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon.ico +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/site.webmanifest +0 -1
- mindroot/coreplugins/chat/static/favicon/logo.png +0 -0
- mindroot/coreplugins/chat/static/favicon/site.webmanifest +0 -1
- mindroot/coreplugins/index/default.json +0 -76
- mindroot/coreplugins/user_service/file_trigger_service.py +0 -12
- mindroot/coreplugins/user_service/hooks.py +0 -23
- /mindroot/coreplugins/{admin/static/favicon/android-chrome-192x192.png → home/static/imgs/backuplogo.png} +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/WHEEL +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/entry_points.txt +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
+
admin_router.include_router(log_router)
|
|
26
73
|
|
|
27
74
|
from .command_router import router as command_router
|
|
28
|
-
|
|
75
|
+
admin_router.include_router(command_router)
|
|
29
76
|
|
|
30
77
|
from .settings_router import router as settings_router
|
|
31
|
-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
+
admin_router.include_router(persona_router)
|
|
38
90
|
|
|
39
91
|
from .agent_router import router as agent_router
|
|
40
|
-
|
|
92
|
+
admin_router.include_router(agent_router)
|
|
41
93
|
|
|
42
94
|
from .server_router import router as server_router
|
|
43
|
-
|
|
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
|
-
|
|
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")
|