mindroot 7.7.0__py3-none-any.whl → 8.1.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.

Potentially problematic release.


This version of mindroot might be problematic. Click here for more details.

@@ -42,4 +42,6 @@ router.include_router(agent_router)
42
42
  from .server_router import router as server_router
43
43
  router.include_router(server_router, prefix="/admin/server", tags=["server"])
44
44
 
45
-
45
+ # Import and include the env_manager router
46
+ from coreplugins.env_manager.router import router as env_manager_router
47
+ router.include_router(env_manager_router)
@@ -0,0 +1,3 @@
1
+ # Import required modules for plugin to load properly
2
+ from .mod import *
3
+ from .router import router
@@ -0,0 +1,16 @@
1
+ {% block head %}
2
+ <link rel="stylesheet" href="/env_manager/static/css/env-manager.css">
3
+ <script type="module" src="/env_manager/static/js/env-manager.js"></script>
4
+ {% endblock %}
5
+
6
+ {% block content %}
7
+ <details>
8
+ <summary>
9
+ <span class="material-icons">settings_applications</span>
10
+ <span>Environment Variables</span>
11
+ </summary>
12
+ <div class="details-content">
13
+ <env-manager theme="dark" scope="local"></env-manager>
14
+ </div>
15
+ </details>
16
+ {% endblock %}
@@ -0,0 +1,228 @@
1
+ import os
2
+ import re
3
+ import json
4
+ import subprocess
5
+ from pathlib import Path
6
+ from lib.providers.services import service
7
+ from lib.plugins import list_enabled, get_plugin_path
8
+
9
+
10
+ def should_skip_directory(directory):
11
+ """Check if a directory should be skipped during scanning.
12
+
13
+ Args:
14
+ directory (str): Path to check
15
+
16
+ Returns:
17
+ bool: True if the directory should be skipped, False otherwise
18
+ """
19
+ # Directories to skip
20
+ skip_dirs = [
21
+ '__pycache__',
22
+ 'node_modules',
23
+ 'static/js',
24
+ 'static/css',
25
+ 'venv',
26
+ 'env',
27
+ '.env',
28
+ 'virtualenv',
29
+ 'site-packages',
30
+ 'dist-packages',
31
+ '.git',
32
+ '.idea',
33
+ '.vscode',
34
+ 'build',
35
+ 'dist',
36
+ 'egg-info'
37
+ ]
38
+
39
+ # Check if any part of the path contains a directory to skip
40
+ path_parts = Path(directory).parts
41
+ for skip_dir in skip_dirs:
42
+ if skip_dir in path_parts:
43
+ return True
44
+
45
+ return False
46
+
47
+
48
+ def scan_directory_for_env_vars(directory):
49
+ """Scan a directory for environment variable references using grep.
50
+
51
+ Args:
52
+ directory (str): Path to the directory to scan
53
+
54
+ Returns:
55
+ set: Set of environment variable names found
56
+ """
57
+ env_vars = set()
58
+
59
+ # Skip if this is a directory we should ignore
60
+ if should_skip_directory(directory):
61
+ return env_vars
62
+
63
+ try:
64
+ # Use grep to find os.environ references - much faster than parsing each file
65
+ cmd = [
66
+ 'grep', '-r',
67
+ '-E', "(os\.environ\[\"|'|os\.environ\.get\(\"|')",
68
+ '--include=*.py',
69
+ '--exclude-dir=venv',
70
+ '--exclude-dir=env',
71
+ '--exclude-dir=.env',
72
+ '--exclude-dir=site-packages',
73
+ '--exclude-dir=dist-packages',
74
+ '--exclude-dir=__pycache__',
75
+ '--exclude-dir=node_modules',
76
+ directory
77
+ ]
78
+
79
+ result = subprocess.run(cmd, capture_output=True, text=True)
80
+
81
+ if result.returncode not in [0, 1]: # 0 = matches found, 1 = no matches
82
+ print(f"Error running grep on {directory}: {result.stderr}")
83
+ return env_vars
84
+
85
+ # Extract variable names using regex
86
+ pattern1 = r"os\.environ\.get\(['\"]([A-Za-z0-9_]+)['\"]" # os.environ.get('VAR_NAME')
87
+ pattern2 = r"os\.environ\[['\"]([A-Za-z0-9_]+)['\"]\]" # os.environ['VAR_NAME']
88
+
89
+ for line in result.stdout.splitlines():
90
+ for pattern in [pattern1, pattern2]:
91
+ for match in re.finditer(pattern, line):
92
+ var_name = match.group(1)
93
+ env_vars.add(var_name)
94
+
95
+ except Exception as e:
96
+ print(f"Error scanning directory {directory}: {e}")
97
+
98
+ return env_vars
99
+
100
+ @service()
101
+ async def scan_env_vars(params=None, context=None):
102
+ """Scan all enabled plugins for environment variable references.
103
+
104
+ Returns:
105
+ dict: Dictionary with plugin names as keys and environment variable info as values
106
+ """
107
+ results = {}
108
+ all_env_vars = set()
109
+
110
+ # Get all enabled plugins
111
+ enabled_plugins = list_enabled()
112
+
113
+ for plugin_name, category in enabled_plugins:
114
+ plugin_path = get_plugin_path(plugin_name)
115
+ if plugin_path:
116
+ # If plugin_path is a file, get its directory
117
+ if os.path.isfile(plugin_path):
118
+ plugin_path = os.path.dirname(plugin_path)
119
+
120
+ # Skip scanning if this is a directory we should ignore
121
+ if should_skip_directory(plugin_path):
122
+ continue
123
+
124
+ # Scan the plugin directory for environment variable references
125
+ env_vars = scan_directory_for_env_vars(plugin_path)
126
+
127
+ if env_vars:
128
+ results[plugin_name] = {
129
+ 'plugin_name': plugin_name,
130
+ 'category': category,
131
+ 'env_vars': list(env_vars)
132
+ }
133
+ all_env_vars.update(env_vars)
134
+
135
+ # Get current environment variables
136
+ current_env = {}
137
+ for var_name in all_env_vars:
138
+ if var_name in os.environ:
139
+ # Mask sensitive values
140
+ if any(sensitive in var_name.lower() for sensitive in ['key', 'secret', 'password', 'token']):
141
+ current_env[var_name] = '********'
142
+ else:
143
+ current_env[var_name] = os.environ[var_name]
144
+ else:
145
+ # Include variables not in environment with empty value
146
+ current_env[var_name] = ''
147
+
148
+ # Add current environment variables to results
149
+ results['current_env'] = current_env
150
+
151
+ return results
152
+
153
+
154
+ def load_env_file():
155
+ """Load environment variables from .env file.
156
+
157
+ Returns:
158
+ dict: Dictionary of environment variables from .env file
159
+ """
160
+ env_vars = {}
161
+ env_file = '.env'
162
+
163
+ if os.path.exists(env_file):
164
+ with open(env_file, 'r') as f:
165
+ for line in f:
166
+ line = line.strip()
167
+ if line and not line.startswith('#'):
168
+ key, value = line.split('=', 1)
169
+ env_vars[key.strip()] = value.strip().strip('"\'')
170
+
171
+ return env_vars
172
+
173
+
174
+ def save_env_file(env_vars):
175
+ """Save environment variables to .env file.
176
+
177
+ Args:
178
+ env_vars (dict): Dictionary of environment variables to save
179
+
180
+ Returns:
181
+ bool: True if successful, False otherwise
182
+ """
183
+ try:
184
+ # Load existing .env file to preserve variables not being updated
185
+ existing_vars = load_env_file()
186
+
187
+ # Update with new values
188
+ existing_vars.update(env_vars)
189
+
190
+ # Write to .env file
191
+ with open('.env', 'w') as f:
192
+ for key, value in existing_vars.items():
193
+ f.write(f"{key}={value}\n")
194
+
195
+ return True
196
+ except Exception as e:
197
+ print(f"Error saving .env file: {e}")
198
+ return False
199
+
200
+
201
+ @service()
202
+ async def update_env_var(var_name, var_value, context=None):
203
+ """Update an environment variable.
204
+
205
+ Args:
206
+ var_name (str): Name of the environment variable
207
+ var_value (str): Value to set
208
+
209
+ Returns:
210
+ dict: Status of the operation
211
+ """
212
+ try:
213
+ # Update the environment variable in the current process
214
+ os.environ[var_name] = var_value
215
+
216
+ # Save to .env file for persistence across restarts
217
+ save_success = save_env_file({var_name: var_value})
218
+
219
+ return {
220
+ 'success': True,
221
+ 'message': f"Environment variable {var_name} updated successfully" +
222
+ (" and saved to .env file" if save_success else "")
223
+ }
224
+ except Exception as e:
225
+ return {
226
+ 'success': False,
227
+ 'message': f"Error updating environment variable: {str(e)}"
228
+ }
@@ -0,0 +1,40 @@
1
+ from fastapi import APIRouter, Request, HTTPException, Body
2
+ from fastapi.responses import JSONResponse
3
+ from lib.route_decorators import requires_role
4
+ from .mod import scan_env_vars, update_env_var
5
+
6
+ # Create router with admin role requirement
7
+ router = APIRouter(
8
+ dependencies=[requires_role('admin')]
9
+ )
10
+
11
+ @router.get("/env_vars/scan")
12
+ async def get_env_vars(request: Request):
13
+ """Scan all enabled plugins for environment variable references."""
14
+ try:
15
+ results = await scan_env_vars()
16
+ return JSONResponse({
17
+ "success": True,
18
+ "data": results
19
+ })
20
+ except Exception as e:
21
+ return JSONResponse({
22
+ "success": False,
23
+ "error": str(e)
24
+ }, status_code=500)
25
+
26
+ @router.post("/env_vars/update")
27
+ async def update_environment_var(
28
+ request: Request,
29
+ var_name: str = Body(...),
30
+ var_value: str = Body(...)
31
+ ):
32
+ """Update an environment variable."""
33
+ try:
34
+ result = await update_env_var(var_name, var_value)
35
+ return JSONResponse(result)
36
+ except Exception as e:
37
+ return JSONResponse({
38
+ "success": False,
39
+ "error": str(e)
40
+ }, status_code=500)
@@ -0,0 +1,263 @@
1
+ :host {
2
+ display: block;
3
+ width: 100%;
4
+ color: var(--text-color);
5
+ }
6
+
7
+ .env-manager {
8
+ display: flex;
9
+ flex-direction: column;
10
+ width: 100%;
11
+ max-width: 100%; /* Adjust to fit container */
12
+ margin: 0 auto;
13
+ gap: 20px;
14
+ }
15
+
16
+ .section {
17
+ background: rgb(10, 10, 25);
18
+ border-radius: 8px;
19
+ padding: 1rem;
20
+ border: 1px solid rgba(255, 255, 255, 0.1);
21
+ }
22
+
23
+ h3 {
24
+ margin-top: 0;
25
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
26
+ padding-bottom: 0.5rem;
27
+ display: flex;
28
+ justify-content: space-between;
29
+ align-items: center;
30
+ }
31
+
32
+ .controls {
33
+ display: flex;
34
+ gap: 10px;
35
+ align-items: center;
36
+ }
37
+
38
+ .search-box {
39
+ background: rgba(0, 0, 0, 0.2);
40
+ border: 1px solid rgba(255, 255, 255, 0.1);
41
+ color: var(--text-color);
42
+ padding: 0.5rem;
43
+ border-radius: 4px;
44
+ width: 200px;
45
+ }
46
+
47
+ .env-table {
48
+ width: 100%;
49
+ border-collapse: collapse;
50
+ margin-top: 1rem;
51
+ table-layout: fixed; /* Fixed layout for better control */
52
+ }
53
+
54
+ .env-table th,
55
+ .env-table td {
56
+ text-align: left;
57
+ padding: 0.75rem;
58
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
59
+ }
60
+
61
+ /* Column widths */
62
+ .env-table th:nth-child(1),
63
+ .env-table td:nth-child(1) {
64
+ width: 40%; /* Variable Name */
65
+ }
66
+
67
+ .env-table th:nth-child(2),
68
+ .env-table td:nth-child(2) {
69
+ width: 45%; /* Value */
70
+ }
71
+
72
+ .env-table th:nth-child(3),
73
+ .env-table td:nth-child(3) {
74
+ width: 15%; /* Actions */
75
+ text-align: center;
76
+ }
77
+
78
+ .env-table th {
79
+ background: rgba(0, 0, 0, 0.2);
80
+ font-weight: 500;
81
+ }
82
+
83
+ .env-table tr:hover {
84
+ background: rgba(255, 255, 255, 0.03);
85
+ }
86
+
87
+ .plugin-header {
88
+ background: rgba(0, 0, 0, 0.2);
89
+ }
90
+
91
+ .plugin-name {
92
+ font-weight: 600;
93
+ padding: 0.75rem;
94
+ color: #4a90e2;
95
+ font-size: 1.1em;
96
+ }
97
+
98
+ .plugin-var td {
99
+ padding-left: 1.5rem;
100
+ }
101
+
102
+ .var-name {
103
+ font-family: monospace;
104
+ background: rgba(0, 0, 0, 0.2);
105
+ padding: 0.25rem 0.5rem;
106
+ border-radius: 4px;
107
+ display: inline-block;
108
+ max-width: 100%;
109
+ word-break: break-all;
110
+ }
111
+
112
+ .var-value {
113
+ font-family: monospace;
114
+ max-width: 100%;
115
+ overflow: hidden;
116
+ text-overflow: ellipsis;
117
+ white-space: nowrap;
118
+ display: inline-block;
119
+ }
120
+
121
+ .var-value.masked {
122
+ color: #888;
123
+ }
124
+
125
+ .actions {
126
+ display: flex;
127
+ gap: 5px;
128
+ justify-content: center;
129
+ }
130
+
131
+ button {
132
+ background: #2a2a40;
133
+ color: #fff;
134
+ border: 1px solid rgba(255, 255, 255, 0.1);
135
+ padding: 0.5rem 1rem;
136
+ border-radius: 4px;
137
+ cursor: pointer;
138
+ transition: background 0.2s;
139
+ }
140
+
141
+ button:hover {
142
+ background: #3a3a50;
143
+ }
144
+
145
+ button.primary {
146
+ background: #4a90e2;
147
+ }
148
+
149
+ button.primary:hover {
150
+ background: #5aa0f2;
151
+ }
152
+
153
+ button.small {
154
+ padding: 0.25rem 0.5rem;
155
+ font-size: 0.8rem;
156
+ }
157
+
158
+ .edit-form {
159
+ display: flex;
160
+ gap: 10px;
161
+ align-items: center;
162
+ margin-top: 0.5rem;
163
+ width: 100%;
164
+ }
165
+
166
+ .edit-form input {
167
+ background: rgba(0, 0, 0, 0.2);
168
+ border: 1px solid rgba(255, 255, 255, 0.1);
169
+ color: var(--text-color);
170
+ padding: 0.5rem;
171
+ border-radius: 4px;
172
+ flex-grow: 1;
173
+ width: 100%;
174
+ }
175
+
176
+ .add-form {
177
+ display: flex;
178
+ flex-direction: column;
179
+ gap: 10px;
180
+ margin-top: 1rem;
181
+ padding: 1rem;
182
+ background: rgba(0, 0, 0, 0.1);
183
+ border-radius: 4px;
184
+ }
185
+
186
+ .form-row {
187
+ display: flex;
188
+ gap: 10px;
189
+ }
190
+
191
+ .form-row input {
192
+ background: rgba(0, 0, 0, 0.2);
193
+ border: 1px solid rgba(255, 255, 255, 0.1);
194
+ color: var(--text-color);
195
+ padding: 0.5rem;
196
+ border-radius: 4px;
197
+ flex-grow: 1;
198
+ }
199
+
200
+ .form-actions {
201
+ display: flex;
202
+ justify-content: flex-end;
203
+ gap: 10px;
204
+ margin-top: 0.5rem;
205
+ }
206
+
207
+ .plugin-refs {
208
+ display: flex;
209
+ flex-wrap: wrap;
210
+ gap: 0.5rem;
211
+ max-width: 100%;
212
+ margin-top: 0.5rem;
213
+ }
214
+
215
+ .plugin-tag {
216
+ font-family: monospace;
217
+ background: rgba(0, 0, 0, 0.2);
218
+ padding: 0.25rem 0.5rem;
219
+ border-radius: 4px;
220
+ font-size: 0.8rem;
221
+ display: inline-block;
222
+ white-space: nowrap;
223
+ margin-bottom: 0.25rem;
224
+ }
225
+
226
+ .loading {
227
+ display: flex;
228
+ justify-content: center;
229
+ align-items: center;
230
+ padding: 2rem;
231
+ font-style: italic;
232
+ color: #888;
233
+ }
234
+
235
+ .empty-state {
236
+ text-align: center;
237
+ padding: 2rem;
238
+ color: #888;
239
+ }
240
+
241
+ .material-icons {
242
+ font-size: 1.2rem;
243
+ }
244
+
245
+ /* Responsive adjustments */
246
+ @media (max-width: 768px) {
247
+ .env-table {
248
+ table-layout: auto;
249
+ }
250
+
251
+ .env-table th:nth-child(1),
252
+ .env-table td:nth-child(1),
253
+ .env-table th:nth-child(2),
254
+ .env-table td:nth-child(2),
255
+ .env-table th:nth-child(3),
256
+ .env-table td:nth-child(3) {
257
+ width: auto;
258
+ }
259
+
260
+ .plugin-var td {
261
+ padding-left: 0.75rem;
262
+ }
263
+ }