mindroot 9.3.0__py3-none-any.whl → 9.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. mindroot/coreplugins/admin/__init__.py +3 -1
  2. mindroot/coreplugins/admin/agent_router.py +250 -7
  3. mindroot/coreplugins/admin/asset_manager.py +164 -0
  4. mindroot/coreplugins/admin/command_router.py +236 -1
  5. mindroot/coreplugins/admin/mcp_catalog_routes.py +156 -0
  6. mindroot/coreplugins/admin/mcp_publish_routes.py +450 -0
  7. mindroot/coreplugins/admin/mcp_registry_routes.py +495 -0
  8. mindroot/coreplugins/admin/mcp_routes.py +216 -0
  9. mindroot/coreplugins/admin/mod.py +62 -0
  10. mindroot/coreplugins/admin/oauth_callback_router.py +84 -0
  11. mindroot/coreplugins/admin/persona_handler.py +15 -6
  12. mindroot/coreplugins/admin/persona_router.py +158 -2
  13. mindroot/coreplugins/admin/plugin_manager.py +105 -9
  14. mindroot/coreplugins/admin/plugin_router_fixed.py +23 -0
  15. mindroot/coreplugins/admin/plugin_router_new_not_working.py +145 -0
  16. mindroot/coreplugins/admin/plugin_routes.py +114 -0
  17. mindroot/coreplugins/admin/registry_settings_routes.py +140 -0
  18. mindroot/coreplugins/admin/router.py +116 -15
  19. mindroot/coreplugins/admin/service_models.py +1 -1
  20. mindroot/coreplugins/admin/settings_router.py +1 -0
  21. mindroot/coreplugins/admin/static/css/admin-custom.css +357 -2
  22. mindroot/coreplugins/admin/static/css/dark.css +1 -0
  23. mindroot/coreplugins/admin/static/css/default.css +4 -0
  24. mindroot/coreplugins/admin/static/js/about-info.js +367 -0
  25. mindroot/coreplugins/admin/static/js/agent-form.js +83 -3
  26. mindroot/coreplugins/admin/static/js/api-key-script.js +307 -0
  27. mindroot/coreplugins/admin/static/js/mcp-manager.js +348 -0
  28. mindroot/coreplugins/admin/static/js/mcp-publisher.js +780 -0
  29. mindroot/coreplugins/admin/static/js/persona-editor.js +34 -5
  30. mindroot/coreplugins/admin/static/js/plugin-toggle.js +1 -1
  31. mindroot/coreplugins/admin/static/js/recommended-plugin-install.js +63 -0
  32. mindroot/coreplugins/admin/static/js/registry-auth-section.js +132 -0
  33. mindroot/coreplugins/admin/static/js/registry-manager-base.js +613 -0
  34. mindroot/coreplugins/admin/static/js/registry-manager-publish-old-delete.js +166 -0
  35. mindroot/coreplugins/admin/static/js/registry-manager.js +351 -0
  36. mindroot/coreplugins/admin/static/js/registry-publish-section.js +377 -0
  37. mindroot/coreplugins/admin/static/js/registry-search-section.js +400 -0
  38. mindroot/coreplugins/admin/static/js/registry-search-section.js.bak +3 -0
  39. mindroot/coreplugins/admin/static/js/registry-settings.js +69 -0
  40. mindroot/coreplugins/admin/static/js/registry-shared-services.js +903 -0
  41. mindroot/coreplugins/admin/static/js/registry-simple-sections.js +85 -0
  42. mindroot/coreplugins/admin/static/js/secure-widget-manager.js +438 -0
  43. mindroot/coreplugins/admin/static/logo.png +0 -0
  44. mindroot/coreplugins/admin/templates/admin.jinja2 +275 -110
  45. mindroot/coreplugins/agent/Assistant/agent.json +27 -11
  46. mindroot/coreplugins/agent/agent.py +2 -2
  47. mindroot/coreplugins/agent/command_parser.py +25 -10
  48. mindroot/coreplugins/agent/templates/system.jinja2 +0 -12
  49. mindroot/coreplugins/chat/__init__.py +4 -1
  50. mindroot/coreplugins/chat/router.py +132 -20
  51. mindroot/coreplugins/chat/router_dedup_patch.py +20 -0
  52. mindroot/coreplugins/chat/services.py +31 -1
  53. mindroot/coreplugins/chat/static/css/action-fix.css +32 -0
  54. mindroot/coreplugins/chat/static/css/admin-custom.css +5 -3
  55. mindroot/coreplugins/chat/static/css/dark.css +24 -3
  56. mindroot/coreplugins/chat/static/css/default.css +24 -3
  57. mindroot/coreplugins/chat/static/css/main.css +1 -0
  58. mindroot/coreplugins/chat/static/js/action.js +137 -60
  59. mindroot/coreplugins/chat/static/js/chat-history.js +3 -0
  60. mindroot/coreplugins/chat/static/js/chat.js +59 -16
  61. mindroot/coreplugins/chat/static/js/chat.js.diff +221 -0
  62. mindroot/coreplugins/chat/static/js/chatform.js +2 -2
  63. mindroot/coreplugins/chat/static/site.webmanifest +1 -1
  64. mindroot/coreplugins/chat/templates/chat.jinja2 +3 -3
  65. mindroot/coreplugins/chat/widget_manager.py +139 -0
  66. mindroot/coreplugins/chat/widget_routes.py +287 -0
  67. mindroot/coreplugins/check_list/inject/admin.jinja2 +1 -1
  68. mindroot/coreplugins/email/__init__.py +2 -0
  69. mindroot/coreplugins/email/email_provider.py +2 -2
  70. mindroot/coreplugins/email/mod.py +100 -0
  71. mindroot/coreplugins/email/services.py +5 -3
  72. mindroot/coreplugins/email/smtp_handler.py +9 -3
  73. mindroot/coreplugins/email/test_email_service.py +75 -0
  74. mindroot/coreplugins/env_manager/mod.py +61 -25
  75. mindroot/coreplugins/home/router.py +37 -2
  76. mindroot/coreplugins/home/static/imgs/logo.png +0 -0
  77. mindroot/coreplugins/home/static/imgs/logo.png.bak +0 -0
  78. mindroot/coreplugins/home/static/imgs/logo_teal.png +0 -0
  79. mindroot/coreplugins/home/static/imgs/logo_teal2.png +0 -0
  80. mindroot/coreplugins/home/static/imgs/logo_teal_detailed.png +0 -0
  81. mindroot/coreplugins/home/static/imgs/logo_teal_python.png +0 -0
  82. mindroot/coreplugins/home/templates/home.jinja2 +15 -6
  83. mindroot/coreplugins/index/indices/default/index.json +39 -6
  84. mindroot/coreplugins/jwt_auth/middleware.py +47 -2
  85. mindroot/coreplugins/jwt_auth/mod.py +40 -17
  86. mindroot/coreplugins/l8n/__init__.py +6 -0
  87. mindroot/coreplugins/l8n/debug_loader.py +85 -0
  88. mindroot/coreplugins/l8n/debug_middleware.py +74 -0
  89. mindroot/coreplugins/l8n/l8n_constants.py +19 -0
  90. mindroot/coreplugins/l8n/language_detection.py +183 -0
  91. mindroot/coreplugins/l8n/middleware.py +151 -0
  92. mindroot/coreplugins/l8n/mod.py +277 -0
  93. mindroot/coreplugins/l8n/monkey_patch_to_delete.py +186 -0
  94. mindroot/coreplugins/l8n/test_enhanced.py +298 -0
  95. mindroot/coreplugins/l8n/test_l8n.py +95 -0
  96. mindroot/coreplugins/l8n/test_l8n_standalone.py +251 -0
  97. mindroot/coreplugins/l8n/test_middleware.py +272 -0
  98. mindroot/coreplugins/l8n/utils.py +232 -0
  99. mindroot/coreplugins/mcp_/__init__.py +14 -0
  100. mindroot/coreplugins/mcp_/catalog_commands.py +328 -0
  101. mindroot/coreplugins/mcp_/catalog_manager.py +263 -0
  102. mindroot/coreplugins/mcp_/dynamic_commands.py +154 -0
  103. mindroot/coreplugins/mcp_/mcp_manager.py +1031 -0
  104. mindroot/coreplugins/mcp_/mod.py +367 -0
  105. mindroot/coreplugins/mcp_/oauth_storage.py +144 -0
  106. mindroot/coreplugins/mcp_/server_installer.py +79 -0
  107. mindroot/coreplugins/mcp_/setup.py +26 -0
  108. mindroot/coreplugins/mcp_/test_dynamic_commands.py +134 -0
  109. mindroot/coreplugins/mcp_/testmcpclient.py +92 -0
  110. mindroot/coreplugins/persona/mod.py +12 -7
  111. mindroot/coreplugins/signup/templates/signup.jinja2 +1 -1
  112. mindroot/coreplugins/subscriptions/__init__.py +1 -0
  113. mindroot/coreplugins/subscriptions/mod.py +14 -3
  114. mindroot/coreplugins/subscriptions/router.py +3 -0
  115. mindroot/coreplugins/user_service/__init__.py +1 -2
  116. mindroot/coreplugins/user_service/admin_init.py +1 -0
  117. mindroot/coreplugins/user_service/email_service.py +72 -17
  118. mindroot/coreplugins/user_service/mod.py +10 -2
  119. mindroot/coreplugins/user_service/router.py +2 -0
  120. mindroot/lib/auth/api_key.py +28 -0
  121. mindroot/lib/cli/plugins.py +94 -0
  122. mindroot/lib/plugins/default_plugin_manifest.json +20 -0
  123. mindroot/lib/plugins/installation.py +5 -5
  124. mindroot/lib/plugins/l8n_static_handler.py +225 -0
  125. mindroot/lib/plugins/loader.py +33 -3
  126. mindroot/lib/plugins/loader_with_l8n.py +281 -0
  127. mindroot/lib/plugins/manifest.py +236 -24
  128. mindroot/lib/providers/commands.py +3 -1
  129. mindroot/lib/route_decorators.py +5 -5
  130. mindroot/lib/templates.py +183 -11
  131. mindroot/lib/utils/merge_arrays.py +1 -1
  132. mindroot/migrate.py +39 -20
  133. mindroot/registry/data_access.py +1 -1
  134. mindroot/server.py +42 -13
  135. mindroot/server_missing_normal_args.py +197 -0
  136. mindroot/server_prev.py +173 -0
  137. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/METADATA +7 -2
  138. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/RECORD +143 -113
  139. mindroot/coreplugins/admin/plugin_manager_backup.py +0 -615
  140. mindroot/coreplugins/admin/static/favicon/about.txt +0 -6
  141. mindroot/coreplugins/admin/static/favicon/android-chrome-512x512.png +0 -0
  142. mindroot/coreplugins/admin/static/favicon/apple-touch-icon.png +0 -0
  143. mindroot/coreplugins/admin/static/favicon/favicon-16x16.png +0 -0
  144. mindroot/coreplugins/admin/static/favicon/favicon-32x32.png +0 -0
  145. mindroot/coreplugins/admin/static/favicon/favicon.ico +0 -0
  146. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/about.txt +0 -6
  147. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
  148. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
  149. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
  150. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
  151. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
  152. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon.ico +0 -0
  153. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/site.webmanifest +0 -1
  154. mindroot/coreplugins/admin/static/favicon/logo.png +0 -0
  155. mindroot/coreplugins/admin/static/favicon/site.webmanifest +0 -1
  156. mindroot/coreplugins/admin/static/js/backup/agent-editor.js +0 -186
  157. mindroot/coreplugins/admin/static/js/backup/agent-form.js +0 -1133
  158. mindroot/coreplugins/admin/static/js/backup/agent-list.js +0 -94
  159. mindroot/coreplugins/chat/static/favicon/about.txt +0 -6
  160. mindroot/coreplugins/chat/static/favicon/android-chrome-192x192.png +0 -0
  161. mindroot/coreplugins/chat/static/favicon/android-chrome-512x512.png +0 -0
  162. mindroot/coreplugins/chat/static/favicon/apple-touch-icon.png +0 -0
  163. mindroot/coreplugins/chat/static/favicon/favicon-16x16.png +0 -0
  164. mindroot/coreplugins/chat/static/favicon/favicon-32x32.png +0 -0
  165. mindroot/coreplugins/chat/static/favicon/favicon.ico +0 -0
  166. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/about.txt +0 -6
  167. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
  168. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
  169. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
  170. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
  171. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
  172. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon.ico +0 -0
  173. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/site.webmanifest +0 -1
  174. mindroot/coreplugins/chat/static/favicon/logo.png +0 -0
  175. mindroot/coreplugins/chat/static/favicon/site.webmanifest +0 -1
  176. mindroot/coreplugins/index/default.json +0 -76
  177. mindroot/coreplugins/user_service/file_trigger_service.py +0 -12
  178. mindroot/coreplugins/user_service/hooks.py +0 -23
  179. /mindroot/coreplugins/{admin/static/favicon/android-chrome-192x192.png → home/static/imgs/backuplogo.png} +0 -0
  180. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/WHEEL +0 -0
  181. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/entry_points.txt +0 -0
  182. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/licenses/LICENSE +0 -0
  183. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/top_level.txt +0 -0
mindroot/migrate.py CHANGED
@@ -1,30 +1,49 @@
1
1
  import os
2
- import shutil
3
- from pathlib import Path
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
- root_manifest = 'plugin_manifest.json'
8
- data_manifest = 'data/plugin_manifest.json'
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
- # If the new location already exists, no migration needed
11
- if os.path.exists(data_manifest):
12
- print(f"Plugin manifest already exists at {data_manifest}")
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
- # If old location exists, move it to new location
16
- if os.path.exists(root_manifest):
17
- print(f"Migrating plugin manifest from {root_manifest} to {data_manifest}")
18
- # Ensure data directory exists
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
- print("No existing plugin manifest found to migrate")
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
- print("Running MindRoot migrations...")
29
- migrate_plugin_manifest()
30
- print("Migrations complete")
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")
@@ -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
- response.headers["X-Frame-Options"] = "SAMEORIGIN"
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
- cmd_args = parse_args()
112
- # save ALL parsed args in app state
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
- #app.add_middleware(
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()
@@ -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.0
3
+ Version: 9.6.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