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
mindroot/migrate.py
CHANGED
|
@@ -1,30 +1,49 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import
|
|
3
|
-
from
|
|
2
|
+
import logging
|
|
3
|
+
from .lib.plugins.manifest import create_default_plugin_manifest, _get_absolute_paths, _validate_manifest
|
|
4
|
+
|
|
5
|
+
# Setup logging
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
4
7
|
|
|
5
8
|
def migrate_plugin_manifest():
|
|
6
|
-
"""Migrate plugin_manifest.json from root to data/ directory if needed.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
"""Migrate plugin_manifest.json from root to data/ directory if needed.
|
|
10
|
+
|
|
11
|
+
This function now delegates to the consolidated manifest handling logic.
|
|
12
|
+
"""
|
|
13
|
+
manifest_abs_path, root_manifest_abs_path, _ = _get_absolute_paths()
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
logger.info("Checking plugin manifest migration...")
|
|
16
|
+
logger.debug(f"Target manifest path: {manifest_abs_path}")
|
|
17
|
+
logger.debug(f"Source manifest path: {root_manifest_abs_path}")
|
|
18
|
+
|
|
19
|
+
# Check if target manifest already exists and is valid
|
|
20
|
+
is_valid, _ = _validate_manifest(manifest_abs_path)
|
|
21
|
+
if is_valid:
|
|
22
|
+
logger.info(f"Valid plugin manifest already exists at {manifest_abs_path}")
|
|
13
23
|
return
|
|
14
24
|
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
os.makedirs('data', exist_ok=True)
|
|
20
|
-
# Move the file
|
|
21
|
-
shutil.move(root_manifest, data_manifest)
|
|
22
|
-
print(f"Plugin manifest migration complete")
|
|
25
|
+
# Check if source manifest exists
|
|
26
|
+
source_exists = os.path.exists(root_manifest_abs_path)
|
|
27
|
+
if source_exists:
|
|
28
|
+
logger.info(f"Found manifest to migrate from {root_manifest_abs_path}")
|
|
23
29
|
else:
|
|
24
|
-
|
|
30
|
+
logger.info("No existing plugin manifest found to migrate")
|
|
31
|
+
|
|
32
|
+
# Use the consolidated manifest creation logic which handles migration
|
|
33
|
+
try:
|
|
34
|
+
create_default_plugin_manifest()
|
|
35
|
+
logger.info("Plugin manifest migration/creation completed successfully")
|
|
36
|
+
except Exception as e:
|
|
37
|
+
logger.error(f"Plugin manifest migration failed: {e}")
|
|
38
|
+
raise
|
|
25
39
|
|
|
26
40
|
def run_migrations():
|
|
27
41
|
"""Run all necessary migrations."""
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
42
|
+
logger.info("Running MindRoot migrations...")
|
|
43
|
+
try:
|
|
44
|
+
migrate_plugin_manifest()
|
|
45
|
+
logger.info("Migrations completed successfully")
|
|
46
|
+
except Exception as e:
|
|
47
|
+
logger.error(f"Migration failed: {e}")
|
|
48
|
+
# Don't raise here - let the system continue with default manifest
|
|
49
|
+
logger.warning("Continuing with default manifest due to migration failure")
|
mindroot/registry/data_access.py
CHANGED
|
@@ -6,7 +6,7 @@ class DataAccess:
|
|
|
6
6
|
self.data_dir = 'data'
|
|
7
7
|
self.models_file = os.path.join(self.data_dir, 'models.json')
|
|
8
8
|
self.providers_file = os.path.join(self.data_dir, 'providers.json')
|
|
9
|
-
self.plugins_file = 'plugin_manifest.json'
|
|
9
|
+
self.plugins_file = 'data/plugin_manifest.json'
|
|
10
10
|
self.equivalent_flags_file = os.path.join(self.data_dir, 'equivalent_flags.json')
|
|
11
11
|
self.preferred_models_file = os.path.join(self.data_dir, 'preferred_models.json')
|
|
12
12
|
|
mindroot/server.py
CHANGED
|
@@ -8,25 +8,44 @@ from .lib.chatcontext import ChatContext
|
|
|
8
8
|
from .lib.providers.hooks import hook_manager
|
|
9
9
|
from .lib.utils.debug import debug_box
|
|
10
10
|
import asyncio
|
|
11
|
+
import sys
|
|
11
12
|
import uvicorn
|
|
12
13
|
from termcolor import colored
|
|
13
14
|
import socket
|
|
14
15
|
from fastapi.middleware.cors import CORSMiddleware
|
|
15
16
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
17
|
+
from .lib.cli.plugins import install_plugins_from_cli
|
|
16
18
|
from dotenv import load_dotenv
|
|
17
19
|
from .migrate import run_migrations
|
|
18
20
|
|
|
21
|
+
# import for file copy
|
|
22
|
+
from shutil import copyfile
|
|
23
|
+
|
|
19
24
|
# Load environment variables from .env file at the start
|
|
20
25
|
# Set override=True to make .env variables override existing environment variables
|
|
21
26
|
load_dotenv(override=True)
|
|
22
27
|
|
|
23
28
|
def parse_args():
|
|
24
29
|
import argparse
|
|
25
|
-
parser = argparse.ArgumentParser(description="Run the server")
|
|
30
|
+
parser = argparse.ArgumentParser(description="Run the MindRoot server or manage plugins.", allow_abbrev=False)
|
|
31
|
+
|
|
32
|
+
# Server arguments are top-level
|
|
26
33
|
parser.add_argument("-p", "--port", type=int, help="Port to run the server on")
|
|
27
|
-
# need to include optional admin-user and admin-password
|
|
28
34
|
parser.add_argument("-u", "--admin-user", type=str, help="Admin username")
|
|
29
35
|
parser.add_argument("-pw", "--admin-password", type=str, help="Admin password")
|
|
36
|
+
|
|
37
|
+
subparsers = parser.add_subparsers(dest='command', help='sub-command help')
|
|
38
|
+
|
|
39
|
+
# Explicit 'server' command for clarity in help, but it's the default action
|
|
40
|
+
server_parser = subparsers.add_parser('server', help='Run the web server (default action)')
|
|
41
|
+
|
|
42
|
+
# Plugin command group
|
|
43
|
+
plugin_parser = subparsers.add_parser('plugin', help='Manage plugins')
|
|
44
|
+
plugin_subparsers = plugin_parser.add_subparsers(dest='plugin_command', required=True)
|
|
45
|
+
install_parser = plugin_subparsers.add_parser('install', help='Install or update one or more plugins')
|
|
46
|
+
install_parser.add_argument('plugins', nargs='+', help='List of plugins to install (e.g., runvnc/plugin-name)')
|
|
47
|
+
install_parser.add_argument('--reinstall', action='store_true', help='Force reinstall of the plugin if it already exists.')
|
|
48
|
+
|
|
30
49
|
return parser.parse_args()
|
|
31
50
|
|
|
32
51
|
def get_project_root():
|
|
@@ -68,10 +87,13 @@ async def setup_app_internal(app_):
|
|
|
68
87
|
app = app_
|
|
69
88
|
|
|
70
89
|
root = get_project_root()
|
|
90
|
+
source_root = Path(__file__).parent
|
|
71
91
|
await plugins.load(app=app)
|
|
72
92
|
app.mount("/static", StaticFiles(directory=str(root / "static"), follow_symlink=True), name="static")
|
|
73
93
|
app.mount("/imgs", StaticFiles(directory=str(root / "imgs"), follow_symlink=True), name="imgs")
|
|
74
|
-
|
|
94
|
+
if not os.path.exists(root / "imgs/logo.png"):
|
|
95
|
+
print(colored("No logo found, copying default logo from coreplugins", "yellow"))
|
|
96
|
+
copyfile(str(source_root / "coreplugins/home/static/imgs/logo.png"), str(root / "imgs/logo.png"))
|
|
75
97
|
return app
|
|
76
98
|
|
|
77
99
|
def is_port_in_use(port):
|
|
@@ -97,7 +119,11 @@ class HeaderMiddleware(BaseHTTPMiddleware):
|
|
|
97
119
|
|
|
98
120
|
# Add security headers
|
|
99
121
|
response.headers["X-Content-Type-Options"] = "nosniff"
|
|
100
|
-
|
|
122
|
+
|
|
123
|
+
#chat widgets don't work if we do this
|
|
124
|
+
if os.environ.get('MR_X_FRAME_SAMEORIGIN', 'false').lower() == 'true':
|
|
125
|
+
response.headers["X-Frame-Options"] = "SAMEORIGIN"
|
|
126
|
+
|
|
101
127
|
response.headers["X-XSS-Protection"] = "1; mode=block"
|
|
102
128
|
|
|
103
129
|
return response
|
|
@@ -108,20 +134,23 @@ def main():
|
|
|
108
134
|
# Run migrations first, before anything else
|
|
109
135
|
run_migrations()
|
|
110
136
|
|
|
111
|
-
|
|
112
|
-
|
|
137
|
+
args = parse_args()
|
|
138
|
+
|
|
139
|
+
# If the command is 'plugin', handle it and exit.
|
|
140
|
+
if args.command == 'plugin':
|
|
141
|
+
if args.plugin_command == 'install':
|
|
142
|
+
asyncio.run(install_plugins_from_cli(args.plugins, reinstall=args.reinstall))
|
|
143
|
+
sys.exit(0)
|
|
144
|
+
|
|
145
|
+
# Default action: run the server. The server arguments are on the main 'args' object.
|
|
146
|
+
cmd_args = args
|
|
113
147
|
port = 8010
|
|
114
148
|
if cmd_args.port:
|
|
115
149
|
port = cmd_args.port
|
|
116
150
|
else:
|
|
117
|
-
cmd_args.port = port
|
|
118
|
-
|
|
119
|
-
app = FastAPI()
|
|
151
|
+
cmd_args.port = port
|
|
120
152
|
|
|
121
|
-
|
|
122
|
-
# CORSMiddleware,
|
|
123
|
-
# allow_origins=["*"] # This one line is all you need to allow all origins
|
|
124
|
-
#)
|
|
153
|
+
app = FastAPI()
|
|
125
154
|
|
|
126
155
|
app.state.cmd_args = cmd_args
|
|
127
156
|
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
from fastapi import FastAPI, Response, Request
|
|
2
|
+
from fastapi.staticfiles import StaticFiles
|
|
3
|
+
from fastapi.templating import Jinja2Templates
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from .lib import plugins
|
|
7
|
+
from .lib.chatcontext import ChatContext
|
|
8
|
+
from .lib.providers.hooks import hook_manager
|
|
9
|
+
from .lib.utils.debug import debug_box
|
|
10
|
+
import asyncio
|
|
11
|
+
import uvicorn
|
|
12
|
+
import sys
|
|
13
|
+
from termcolor import colored
|
|
14
|
+
import socket
|
|
15
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
16
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
17
|
+
from .lib.cli.plugins import install_plugins_from_cli
|
|
18
|
+
from dotenv import load_dotenv
|
|
19
|
+
from .migrate import run_migrations
|
|
20
|
+
|
|
21
|
+
# import for file copy
|
|
22
|
+
from shutil import copyfile
|
|
23
|
+
|
|
24
|
+
# Load environment variables from .env file at the start
|
|
25
|
+
# Set override=True to make .env variables override existing environment variables
|
|
26
|
+
load_dotenv(override=True)
|
|
27
|
+
|
|
28
|
+
def parse_args():
|
|
29
|
+
import argparse
|
|
30
|
+
parser = argparse.ArgumentParser(description="Run the server")
|
|
31
|
+
subparsers = parser.add_subparsers(dest='command', help='sub-command help')
|
|
32
|
+
|
|
33
|
+
# Server command (default)
|
|
34
|
+
server_parser = subparsers.add_parser('server', help='Run the web server (default)')
|
|
35
|
+
server_parser.add_argument("-p", "--port", type=int, help="Port to run the server on")
|
|
36
|
+
server_parser.add_argument("-u", "--admin-user", type=str, help="Admin username")
|
|
37
|
+
server_parser.add_argument("-pw", "--admin-password", type=str, help="Admin password")
|
|
38
|
+
|
|
39
|
+
# Plugin command
|
|
40
|
+
plugin_parser = subparsers.add_parser('plugin', help='Manage plugins')
|
|
41
|
+
plugin_subparsers = plugin_parser.add_subparsers(dest='plugin_command', required=True)
|
|
42
|
+
install_parser = plugin_subparsers.add_parser('install', help='Install one or more plugins')
|
|
43
|
+
install_parser.add_argument('plugins', nargs='+', help='List of plugins to install (e.g., runvnc/plugin-name or pypi-package-name)')
|
|
44
|
+
|
|
45
|
+
return parser.parse_args()
|
|
46
|
+
|
|
47
|
+
def get_project_root():
|
|
48
|
+
return Path(os.getcwd())
|
|
49
|
+
#return Path(__file__).parent
|
|
50
|
+
|
|
51
|
+
def create_directories():
|
|
52
|
+
root = get_project_root()
|
|
53
|
+
directories = [
|
|
54
|
+
"data",
|
|
55
|
+
"imgs",
|
|
56
|
+
"models",
|
|
57
|
+
"models/face",
|
|
58
|
+
"models/llm",
|
|
59
|
+
"static/personas",
|
|
60
|
+
"personas",
|
|
61
|
+
"personas/local",
|
|
62
|
+
"personas/shared",
|
|
63
|
+
"data/sessions"
|
|
64
|
+
]
|
|
65
|
+
chatlog_dir = os.environ.get('CHATLOG_DIR', 'data/chat')
|
|
66
|
+
directories.append(chatlog_dir)
|
|
67
|
+
|
|
68
|
+
for directory in directories:
|
|
69
|
+
(root / directory).mkdir(parents=True, exist_ok=True)
|
|
70
|
+
|
|
71
|
+
create_directories()
|
|
72
|
+
|
|
73
|
+
import mimetypes
|
|
74
|
+
mimetypes.add_type('application/javascript', '.js')
|
|
75
|
+
mimetypes.add_type('text/css', '.css')
|
|
76
|
+
|
|
77
|
+
templates = None
|
|
78
|
+
app = None
|
|
79
|
+
failed_plugins = []
|
|
80
|
+
|
|
81
|
+
async def setup_app_internal(app_):
|
|
82
|
+
global app, templates
|
|
83
|
+
app = app_
|
|
84
|
+
|
|
85
|
+
root = get_project_root()
|
|
86
|
+
source_root = Path(__file__).parent
|
|
87
|
+
await plugins.load(app=app)
|
|
88
|
+
app.mount("/static", StaticFiles(directory=str(root / "static"), follow_symlink=True), name="static")
|
|
89
|
+
app.mount("/imgs", StaticFiles(directory=str(root / "imgs"), follow_symlink=True), name="imgs")
|
|
90
|
+
if not os.path.exists(root / "imgs/logo.png"):
|
|
91
|
+
print(colored("No logo found, copying default logo from coreplugins", "yellow"))
|
|
92
|
+
copyfile(str(source_root / "coreplugins/home/static/imgs/logo.png"), str(root / "imgs/logo.png"))
|
|
93
|
+
return app
|
|
94
|
+
|
|
95
|
+
def is_port_in_use(port):
|
|
96
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
97
|
+
try:
|
|
98
|
+
s.bind(('0.0.0.0', port))
|
|
99
|
+
return False
|
|
100
|
+
except socket.error:
|
|
101
|
+
return True
|
|
102
|
+
|
|
103
|
+
def find_available_port(start_port=8010, max_attempts=100):
|
|
104
|
+
port = start_port
|
|
105
|
+
while port < start_port + max_attempts:
|
|
106
|
+
if not is_port_in_use(port):
|
|
107
|
+
return port
|
|
108
|
+
port += 1
|
|
109
|
+
raise RuntimeError(f"Could not find an available port after {max_attempts} attempts")
|
|
110
|
+
|
|
111
|
+
class HeaderMiddleware(BaseHTTPMiddleware):
|
|
112
|
+
async def dispatch(self, request: Request, call_next):
|
|
113
|
+
# First get the response from other middleware and routes
|
|
114
|
+
response = await call_next(request)
|
|
115
|
+
|
|
116
|
+
# Add security headers
|
|
117
|
+
response.headers["X-Content-Type-Options"] = "nosniff"
|
|
118
|
+
|
|
119
|
+
#chat widgets don't work if we do this
|
|
120
|
+
if os.environ.get('MR_X_FRAME_SAMEORIGIN', 'false').lower() == 'true':
|
|
121
|
+
response.headers["X-Frame-Options"] = "SAMEORIGIN"
|
|
122
|
+
|
|
123
|
+
response.headers["X-XSS-Protection"] = "1; mode=block"
|
|
124
|
+
|
|
125
|
+
return response
|
|
126
|
+
|
|
127
|
+
def main():
|
|
128
|
+
global app
|
|
129
|
+
|
|
130
|
+
# Run migrations first, before anything else
|
|
131
|
+
run_migrations()
|
|
132
|
+
|
|
133
|
+
args = parse_args()
|
|
134
|
+
|
|
135
|
+
# If no command is specified, default to 'server'
|
|
136
|
+
if args.command is None:
|
|
137
|
+
args.command = 'server'
|
|
138
|
+
|
|
139
|
+
if args.command == 'plugin' and args.plugin_command == 'install':
|
|
140
|
+
asyncio.run(install_plugins_from_cli(args.plugins))
|
|
141
|
+
sys.exit(0)
|
|
142
|
+
|
|
143
|
+
# Proceed with server startup
|
|
144
|
+
cmd_args = args
|
|
145
|
+
port = 8010
|
|
146
|
+
if cmd_args.port:
|
|
147
|
+
port = cmd_args.port
|
|
148
|
+
else:
|
|
149
|
+
# Add a default port to the args namespace if not provided
|
|
150
|
+
# to avoid AttributeError later.
|
|
151
|
+
setattr(cmd_args, 'port', port)
|
|
152
|
+
|
|
153
|
+
app = FastAPI()
|
|
154
|
+
|
|
155
|
+
#app.add_middleware(
|
|
156
|
+
# CORSMiddleware,
|
|
157
|
+
# allow_origins=["*"] # This one line is all you need to allow all origins
|
|
158
|
+
#)
|
|
159
|
+
|
|
160
|
+
app.state.cmd_args = cmd_args
|
|
161
|
+
|
|
162
|
+
debug_box("pre_load")
|
|
163
|
+
loop = asyncio.get_event_loop()
|
|
164
|
+
loop.run_until_complete(plugins.pre_load(app)) # middleware
|
|
165
|
+
|
|
166
|
+
debug_box("finished with pre_load, now calling uvicorn.run")
|
|
167
|
+
|
|
168
|
+
@app.on_event("startup")
|
|
169
|
+
async def setup_app():
|
|
170
|
+
global app
|
|
171
|
+
await setup_app_internal(app)
|
|
172
|
+
print(colored("Plugin setup complete", "green"))
|
|
173
|
+
|
|
174
|
+
@app.on_event("shutdown")
|
|
175
|
+
async def shutdown_event():
|
|
176
|
+
print("Shutting down MindRoot")
|
|
177
|
+
hook_manager.eject()
|
|
178
|
+
|
|
179
|
+
app.add_middleware(
|
|
180
|
+
CORSMiddleware,
|
|
181
|
+
allow_origins=["*"], # Replace with your specific origins for production
|
|
182
|
+
allow_credentials=True,
|
|
183
|
+
allow_methods=["*"], # Or specify: ["GET", "POST", "PUT", "DELETE", etc.]
|
|
184
|
+
allow_headers=["*"], # Or specify required headers
|
|
185
|
+
expose_headers=["*"] # Headers that browsers are allowed to access
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
app.add_middleware(HeaderMiddleware)
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
print(colored(f"Starting server on port {port}", "green"))
|
|
192
|
+
uvicorn.run(app, host="0.0.0.0", port=port, lifespan="on", timeout_graceful_shutdown=2)
|
|
193
|
+
except Exception as e:
|
|
194
|
+
print(colored(f"Error starting server: {str(e)}", "red"))
|
|
195
|
+
|
|
196
|
+
if __name__ == "__main__":
|
|
197
|
+
main()
|
mindroot/server_prev.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
from fastapi import FastAPI, Response, Request
|
|
2
|
+
from fastapi.staticfiles import StaticFiles
|
|
3
|
+
from fastapi.templating import Jinja2Templates
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from .lib import plugins
|
|
7
|
+
from .lib.chatcontext import ChatContext
|
|
8
|
+
from .lib.providers.hooks import hook_manager
|
|
9
|
+
from .lib.utils.debug import debug_box
|
|
10
|
+
import asyncio
|
|
11
|
+
import uvicorn
|
|
12
|
+
from termcolor import colored
|
|
13
|
+
import socket
|
|
14
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
15
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
16
|
+
from dotenv import load_dotenv
|
|
17
|
+
from .migrate import run_migrations
|
|
18
|
+
|
|
19
|
+
# import for file copy
|
|
20
|
+
from shutil import copyfile
|
|
21
|
+
|
|
22
|
+
# Load environment variables from .env file at the start
|
|
23
|
+
# Set override=True to make .env variables override existing environment variables
|
|
24
|
+
load_dotenv(override=True)
|
|
25
|
+
|
|
26
|
+
def parse_args():
|
|
27
|
+
import argparse
|
|
28
|
+
parser = argparse.ArgumentParser(description="Run the server")
|
|
29
|
+
parser.add_argument("-p", "--port", type=int, help="Port to run the server on")
|
|
30
|
+
# need to include optional admin-user and admin-password
|
|
31
|
+
parser.add_argument("-u", "--admin-user", type=str, help="Admin username")
|
|
32
|
+
parser.add_argument("-pw", "--admin-password", type=str, help="Admin password")
|
|
33
|
+
return parser.parse_args()
|
|
34
|
+
|
|
35
|
+
def get_project_root():
|
|
36
|
+
return Path(os.getcwd())
|
|
37
|
+
#return Path(__file__).parent
|
|
38
|
+
|
|
39
|
+
def create_directories():
|
|
40
|
+
root = get_project_root()
|
|
41
|
+
directories = [
|
|
42
|
+
"data",
|
|
43
|
+
"imgs",
|
|
44
|
+
"models",
|
|
45
|
+
"models/face",
|
|
46
|
+
"models/llm",
|
|
47
|
+
"static/personas",
|
|
48
|
+
"personas",
|
|
49
|
+
"personas/local",
|
|
50
|
+
"personas/shared",
|
|
51
|
+
"data/sessions"
|
|
52
|
+
]
|
|
53
|
+
chatlog_dir = os.environ.get('CHATLOG_DIR', 'data/chat')
|
|
54
|
+
directories.append(chatlog_dir)
|
|
55
|
+
|
|
56
|
+
for directory in directories:
|
|
57
|
+
(root / directory).mkdir(parents=True, exist_ok=True)
|
|
58
|
+
|
|
59
|
+
create_directories()
|
|
60
|
+
|
|
61
|
+
import mimetypes
|
|
62
|
+
mimetypes.add_type('application/javascript', '.js')
|
|
63
|
+
mimetypes.add_type('text/css', '.css')
|
|
64
|
+
|
|
65
|
+
templates = None
|
|
66
|
+
app = None
|
|
67
|
+
failed_plugins = []
|
|
68
|
+
|
|
69
|
+
async def setup_app_internal(app_):
|
|
70
|
+
global app, templates
|
|
71
|
+
app = app_
|
|
72
|
+
|
|
73
|
+
root = get_project_root()
|
|
74
|
+
source_root = Path(__file__).parent
|
|
75
|
+
await plugins.load(app=app)
|
|
76
|
+
app.mount("/static", StaticFiles(directory=str(root / "static"), follow_symlink=True), name="static")
|
|
77
|
+
app.mount("/imgs", StaticFiles(directory=str(root / "imgs"), follow_symlink=True), name="imgs")
|
|
78
|
+
if not os.path.exists(root / "imgs/logo.png"):
|
|
79
|
+
print(colored("No logo found, copying default logo from coreplugins", "yellow"))
|
|
80
|
+
copyfile(str(source_root / "coreplugins/home/static/imgs/logo.png"), str(root / "imgs/logo.png"))
|
|
81
|
+
return app
|
|
82
|
+
|
|
83
|
+
def is_port_in_use(port):
|
|
84
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
85
|
+
try:
|
|
86
|
+
s.bind(('0.0.0.0', port))
|
|
87
|
+
return False
|
|
88
|
+
except socket.error:
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
def find_available_port(start_port=8010, max_attempts=100):
|
|
92
|
+
port = start_port
|
|
93
|
+
while port < start_port + max_attempts:
|
|
94
|
+
if not is_port_in_use(port):
|
|
95
|
+
return port
|
|
96
|
+
port += 1
|
|
97
|
+
raise RuntimeError(f"Could not find an available port after {max_attempts} attempts")
|
|
98
|
+
|
|
99
|
+
class HeaderMiddleware(BaseHTTPMiddleware):
|
|
100
|
+
async def dispatch(self, request: Request, call_next):
|
|
101
|
+
# First get the response from other middleware and routes
|
|
102
|
+
response = await call_next(request)
|
|
103
|
+
|
|
104
|
+
# Add security headers
|
|
105
|
+
response.headers["X-Content-Type-Options"] = "nosniff"
|
|
106
|
+
|
|
107
|
+
#chat widgets don't work if we do this
|
|
108
|
+
if os.environ.get('MR_X_FRAME_SAMEORIGIN', 'false').lower() == 'true':
|
|
109
|
+
response.headers["X-Frame-Options"] = "SAMEORIGIN"
|
|
110
|
+
|
|
111
|
+
response.headers["X-XSS-Protection"] = "1; mode=block"
|
|
112
|
+
|
|
113
|
+
return response
|
|
114
|
+
|
|
115
|
+
def main():
|
|
116
|
+
global app
|
|
117
|
+
|
|
118
|
+
# Run migrations first, before anything else
|
|
119
|
+
run_migrations()
|
|
120
|
+
|
|
121
|
+
cmd_args = parse_args()
|
|
122
|
+
# save ALL parsed args in app state
|
|
123
|
+
port = 8010
|
|
124
|
+
if cmd_args.port:
|
|
125
|
+
port = cmd_args.port
|
|
126
|
+
else:
|
|
127
|
+
cmd_args.port = port
|
|
128
|
+
|
|
129
|
+
app = FastAPI()
|
|
130
|
+
|
|
131
|
+
#app.add_middleware(
|
|
132
|
+
# CORSMiddleware,
|
|
133
|
+
# allow_origins=["*"] # This one line is all you need to allow all origins
|
|
134
|
+
#)
|
|
135
|
+
|
|
136
|
+
app.state.cmd_args = cmd_args
|
|
137
|
+
|
|
138
|
+
debug_box("pre_load")
|
|
139
|
+
loop = asyncio.get_event_loop()
|
|
140
|
+
loop.run_until_complete(plugins.pre_load(app)) # middleware
|
|
141
|
+
|
|
142
|
+
debug_box("finished with pre_load, now calling uvicorn.run")
|
|
143
|
+
|
|
144
|
+
@app.on_event("startup")
|
|
145
|
+
async def setup_app():
|
|
146
|
+
global app
|
|
147
|
+
await setup_app_internal(app)
|
|
148
|
+
print(colored("Plugin setup complete", "green"))
|
|
149
|
+
|
|
150
|
+
@app.on_event("shutdown")
|
|
151
|
+
async def shutdown_event():
|
|
152
|
+
print("Shutting down MindRoot")
|
|
153
|
+
hook_manager.eject()
|
|
154
|
+
|
|
155
|
+
app.add_middleware(
|
|
156
|
+
CORSMiddleware,
|
|
157
|
+
allow_origins=["*"], # Replace with your specific origins for production
|
|
158
|
+
allow_credentials=True,
|
|
159
|
+
allow_methods=["*"], # Or specify: ["GET", "POST", "PUT", "DELETE", etc.]
|
|
160
|
+
allow_headers=["*"], # Or specify required headers
|
|
161
|
+
expose_headers=["*"] # Headers that browsers are allowed to access
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
app.add_middleware(HeaderMiddleware)
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
print(colored(f"Starting server on port {port}", "green"))
|
|
168
|
+
uvicorn.run(app, host="0.0.0.0", port=port, lifespan="on", timeout_graceful_shutdown=2)
|
|
169
|
+
except Exception as e:
|
|
170
|
+
print(colored(f"Error starting server: {str(e)}", "red"))
|
|
171
|
+
|
|
172
|
+
if __name__ == "__main__":
|
|
173
|
+
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mindroot
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.5.0
|
|
4
4
|
Summary: MindRoot AI Agent Framework
|
|
5
5
|
Requires-Python: >=3.9
|
|
6
6
|
License-File: LICENSE
|
|
@@ -18,7 +18,6 @@ Requires-Dist: PyJWT
|
|
|
18
18
|
Requires-Dist: pluggy
|
|
19
19
|
Requires-Dist: sse_starlette
|
|
20
20
|
Requires-Dist: partial-json-parser
|
|
21
|
-
Requires-Dist: pydantic
|
|
22
21
|
Requires-Dist: setuptools
|
|
23
22
|
Requires-Dist: requests
|
|
24
23
|
Requires-Dist: aiohttp
|
|
@@ -33,4 +32,10 @@ Requires-Dist: python-dotenv
|
|
|
33
32
|
Requires-Dist: google-auth
|
|
34
33
|
Requires-Dist: google-auth-oauthlib
|
|
35
34
|
Requires-Dist: google-auth-httplib2
|
|
35
|
+
Requires-Dist: chardet
|
|
36
|
+
Requires-Dist: mcp>=1.12.3
|
|
37
|
+
Requires-Dist: httpx>=0.24.0
|
|
38
|
+
Requires-Dist: pydantic>=2.0.0
|
|
39
|
+
Requires-Dist: aiofiles>=23.0.0
|
|
40
|
+
Requires-Dist: uv>=0.7.0
|
|
36
41
|
Dynamic: license-file
|