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.
- 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 +105 -9
- 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-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 +903 -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 +39 -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.6.0.dist-info}/METADATA +7 -2
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/RECORD +143 -113
- mindroot/coreplugins/admin/plugin_manager_backup.py +0 -615
- 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.6.0.dist-info}/WHEEL +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/entry_points.txt +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/licenses/LICENSE +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,903 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry Shared Services
|
|
3
|
+
*
|
|
4
|
+
* Contains all business logic, API calls, and utility functions
|
|
5
|
+
* that are shared across different registry manager sections.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
class RegistrySharedServices {
|
|
9
|
+
constructor(sharedState, mainComponent) {
|
|
10
|
+
this.state = sharedState;
|
|
11
|
+
this.main = mainComponent;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// === Authentication Services ===
|
|
15
|
+
|
|
16
|
+
async checkAuthStatus() {
|
|
17
|
+
if (this.state.authToken) {
|
|
18
|
+
try {
|
|
19
|
+
const response = await fetch(`${this.state.registryUrl}/stats`, {
|
|
20
|
+
headers: {
|
|
21
|
+
'Authorization': `Bearer ${this.state.authToken}`
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (response.ok) {
|
|
26
|
+
try {
|
|
27
|
+
const userResponse = await fetch(`${this.state.registryUrl}/me`, {
|
|
28
|
+
headers: {
|
|
29
|
+
'Authorization': `Bearer ${this.state.authToken}`
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
if (userResponse.ok) {
|
|
33
|
+
this.state.currentUser = await userResponse.json();
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
this.state.currentUser = { username: 'User' };
|
|
37
|
+
}
|
|
38
|
+
this.state.isLoggedIn = true;
|
|
39
|
+
} else {
|
|
40
|
+
this.logout();
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error('Error checking auth status:', error);
|
|
44
|
+
this.logout();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async login(username, password) {
|
|
50
|
+
this.state.loading = true;
|
|
51
|
+
this.main.error = '';
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const formData = new FormData();
|
|
55
|
+
formData.append('username', username);
|
|
56
|
+
formData.append('password', password);
|
|
57
|
+
|
|
58
|
+
const response = await fetch(`${this.state.registryUrl}/token`, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
body: formData
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (response.ok) {
|
|
64
|
+
const data = await response.json();
|
|
65
|
+
this.state.authToken = data.access_token;
|
|
66
|
+
localStorage.setItem('registry_token', this.state.authToken);
|
|
67
|
+
this.state.isLoggedIn = true;
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
await this.checkAuthStatus();
|
|
71
|
+
} catch (e) {
|
|
72
|
+
this.state.currentUser = { username };
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
} else {
|
|
76
|
+
const errorData = await response.json();
|
|
77
|
+
this.showToast(errorData.detail || 'Login failed', 'error');
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
this.showToast('Network error: ' + error.message, 'error');
|
|
82
|
+
return false;
|
|
83
|
+
} finally {
|
|
84
|
+
this.state.loading = false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async register(username, email, password) {
|
|
89
|
+
this.state.loading = true;
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const response = await fetch(`${this.state.registryUrl}/register`, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: {
|
|
95
|
+
'Content-Type': 'application/json'
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
username: username,
|
|
99
|
+
email: email,
|
|
100
|
+
password: password
|
|
101
|
+
})
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (response.ok) {
|
|
105
|
+
this.showToast('Registration successful! You can now log in.', 'success');
|
|
106
|
+
return true;
|
|
107
|
+
} else {
|
|
108
|
+
const errorData = await response.json();
|
|
109
|
+
this.showToast(errorData.detail || 'Registration failed', 'error');
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
this.showToast('Network error: ' + error.message, 'error');
|
|
114
|
+
return false;
|
|
115
|
+
} finally {
|
|
116
|
+
this.state.loading = false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
logout() {
|
|
121
|
+
this.state.authToken = null;
|
|
122
|
+
this.state.isLoggedIn = false;
|
|
123
|
+
this.state.currentUser = null;
|
|
124
|
+
localStorage.removeItem('registry_token');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
showToast(message, type = 'info', duration = 5000) {
|
|
128
|
+
this.main.showToast(message, type, duration);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// === Data Loading Services ===
|
|
132
|
+
|
|
133
|
+
async loadStats() {
|
|
134
|
+
try {
|
|
135
|
+
const response = await fetch(`${this.state.registryUrl}/stats`);
|
|
136
|
+
if (response.ok) {
|
|
137
|
+
this.main.stats = await response.json();
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error('Error loading stats:', error);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async loadLocalContent() {
|
|
145
|
+
try {
|
|
146
|
+
const pluginsResponse = await fetch('/admin/plugin-manager/get-all-plugins');
|
|
147
|
+
if (pluginsResponse.ok) {
|
|
148
|
+
const pluginsData = await pluginsResponse.json();
|
|
149
|
+
this.state.localPlugins = pluginsData.data || [];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const agentsResponse = await fetch('/agents/local');
|
|
153
|
+
if (agentsResponse.ok) {
|
|
154
|
+
const agentsData = await agentsResponse.json();
|
|
155
|
+
this.state.localAgents = agentsData;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const mcpResponse = await fetch('/admin/mcp/list');
|
|
159
|
+
if (mcpResponse.ok) {
|
|
160
|
+
const mcpData = await mcpResponse.json();
|
|
161
|
+
this.main.localMcpServers = mcpData.data || [];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const ownershipResponse = await fetch('/agents/ownership-info');
|
|
165
|
+
if (ownershipResponse.ok) {
|
|
166
|
+
this.main.agentOwnership = await ownershipResponse.json();
|
|
167
|
+
}
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error('Error loading local content:', error);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (this.main.searchResults.length === 0) {
|
|
173
|
+
await this.loadTopContent();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async loadTopContent() {
|
|
178
|
+
this.state.loading = true;
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
const response = await fetch(`${this.state.registryUrl}/search?limit=20&sort=downloads`);
|
|
182
|
+
|
|
183
|
+
if (response.ok) {
|
|
184
|
+
const data = await response.json();
|
|
185
|
+
this.main.searchResults = data.results || [];
|
|
186
|
+
} else {
|
|
187
|
+
console.warn('Failed to load top content, trying basic search');
|
|
188
|
+
await this.search('', 'all');
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.warn('Error loading top content:', error);
|
|
192
|
+
} finally {
|
|
193
|
+
this.state.loading = false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// === Search Services ===
|
|
198
|
+
|
|
199
|
+
async search(query = this.main.searchQuery, category = this.main.selectedCategory) {
|
|
200
|
+
this.state.loading = true;
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
const params = new URLSearchParams({
|
|
204
|
+
query: query || '',
|
|
205
|
+
limit: '20',
|
|
206
|
+
semantic: 'true'
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
if (category && category !== 'all') {
|
|
210
|
+
params.append('category', category);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const response = await fetch(`${this.state.registryUrl}/search?${params}`);
|
|
214
|
+
|
|
215
|
+
if (response.ok) {
|
|
216
|
+
const data = await response.json();
|
|
217
|
+
this.main.searchResults = data.results || [];
|
|
218
|
+
|
|
219
|
+
if (data.semantic_results && data.semantic_results.length > 0) {
|
|
220
|
+
this.mergeSemanticResults(data.semantic_results);
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
this.showToast('Search failed', 'error');
|
|
224
|
+
}
|
|
225
|
+
} catch (error) {
|
|
226
|
+
this.showToast('Network error: ' + error.message, 'error');
|
|
227
|
+
} finally {
|
|
228
|
+
this.state.loading = false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
mergeSemanticResults(semanticResults) {
|
|
233
|
+
semanticResults.sort((a, b) => (a.distance || 1) - (b.distance || 1));
|
|
234
|
+
|
|
235
|
+
const existingIds = new Set(this.main.searchResults.map(item => item.id.toString()));
|
|
236
|
+
const semanticItems = [];
|
|
237
|
+
|
|
238
|
+
for (const semanticResult of semanticResults) {
|
|
239
|
+
if (!existingIds.has(semanticResult.id)) {
|
|
240
|
+
/*
|
|
241
|
+
const semanticItem = {
|
|
242
|
+
id: parseInt(semanticResult.id),
|
|
243
|
+
title: semanticResult.metadata.title || 'Unknown',
|
|
244
|
+
description: semanticResult.document || '',
|
|
245
|
+
category: semanticResult.metadata.category || 'unknown',
|
|
246
|
+
distance_score: semanticResult.distance,
|
|
247
|
+
is_semantic: true
|
|
248
|
+
}; */
|
|
249
|
+
semanticItems.push(semanticResult);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
semanticItems.sort((a, b) => (a.distance_score || 1) - (b.distance_score || 1));
|
|
254
|
+
this.main.searchResults = [...this.main.searchResults, ...semanticItems];
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// === Installation Services ===
|
|
258
|
+
|
|
259
|
+
async installFromRegistry(item) {
|
|
260
|
+
this.state.loading = true;
|
|
261
|
+
this.main.error = '';
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
if (this.state.authToken) {
|
|
265
|
+
await fetch(`${this.state.registryUrl}/install/${item.id}`, {
|
|
266
|
+
method: 'POST',
|
|
267
|
+
headers: {
|
|
268
|
+
'Authorization': `Bearer ${this.state.authToken}`
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (item.category === 'plugin') {
|
|
274
|
+
await this.installPlugin(item);
|
|
275
|
+
} else if (item.category === 'agent') {
|
|
276
|
+
await this.installAgent(item);
|
|
277
|
+
await this.checkAndInstallRecommendedPlugins(item);
|
|
278
|
+
} else if (item.category === 'mcp_server') {
|
|
279
|
+
await this.installMcpServer(item);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
await this.loadLocalContent();
|
|
283
|
+
this.state.loading = false;
|
|
284
|
+
this.showToast('Item installed successfully!', 'success');
|
|
285
|
+
|
|
286
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
287
|
+
|
|
288
|
+
} catch (error) {
|
|
289
|
+
this.state.loading = false;
|
|
290
|
+
this.showToast('Installation failed: ' + error.message, 'error');
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async installMcpServer(item) {
|
|
295
|
+
// Retrieve secrets from the main component's state
|
|
296
|
+
const secrets = this.main.mcpInstallSecrets[item.id] || null;
|
|
297
|
+
console.log(`[SharedServices] Installing MCP Server '${item.title}' (ID: ${item.id}) with secrets:`, secrets ? Object.keys(secrets) : 'None');
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
if (item.data && item.data.auth_type === 'oauth2') {
|
|
301
|
+
await this.installOAuthMcpServer(item);
|
|
302
|
+
} else {
|
|
303
|
+
// Use the new install endpoint that accepts secrets
|
|
304
|
+
const response = await fetch('/admin/mcp/install', {
|
|
305
|
+
method: 'POST',
|
|
306
|
+
headers: { 'Content-Type': 'application/json' },
|
|
307
|
+
body: JSON.stringify({
|
|
308
|
+
registry_id: item.id,
|
|
309
|
+
registry_url: this.state.registryUrl,
|
|
310
|
+
secrets: secrets
|
|
311
|
+
})
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
if (response.ok) {
|
|
315
|
+
this.showToast(`MCP Server '${item.title}' installed successfully.`, 'success');
|
|
316
|
+
} else {
|
|
317
|
+
const err = await response.json();
|
|
318
|
+
throw new Error(err.detail || 'Failed to install MCP server');
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
} catch (error) {
|
|
322
|
+
throw new Error(`MCP Server installation failed: ${error.message}`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Connect an already installed OAuth MCP server from a registry item.
|
|
328
|
+
* This triggers the /admin/mcp/test-remote -> OAuth popup -> /complete-oauth flow,
|
|
329
|
+
* then refreshes local MCP list and attempts a final /admin/mcp/connect to ensure
|
|
330
|
+
* dynamic MCP commands are registered.
|
|
331
|
+
*/
|
|
332
|
+
async connectInstalledOAuthMcp(item) {
|
|
333
|
+
try {
|
|
334
|
+
// Derive name and URL from registry data and local list
|
|
335
|
+
const serverName = item.data?.name || item.title;
|
|
336
|
+
const remoteUrl = item.data?.transport_url || item.data?.url || item.data?.provider_url || item.data?.authorization_server_url || '';
|
|
337
|
+
|
|
338
|
+
if (!serverName) {
|
|
339
|
+
throw new Error('Missing server name');
|
|
340
|
+
}
|
|
341
|
+
if (!remoteUrl) {
|
|
342
|
+
// Fallback: try to infer from local list
|
|
343
|
+
const localServer = (this.main.localMcpServers || []).find(s => s.name === item.title || s.name === serverName);
|
|
344
|
+
if (localServer?.transport_url) {
|
|
345
|
+
// ok
|
|
346
|
+
} else {
|
|
347
|
+
this.showToast('Cannot determine remote URL for OAuth connection', 'error');
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Kick off test-remote to either connect or return requires_oauth
|
|
353
|
+
const testResp = await fetch('/admin/mcp/test-remote', {
|
|
354
|
+
method: 'POST',
|
|
355
|
+
headers: { 'Content-Type': 'application/json' },
|
|
356
|
+
body: JSON.stringify({ url: remoteUrl, name: serverName })
|
|
357
|
+
});
|
|
358
|
+
const testData = await testResp.json();
|
|
359
|
+
|
|
360
|
+
if (testData.success) {
|
|
361
|
+
this.showToast(testData.message || `Connected to '${serverName}'.`, 'success');
|
|
362
|
+
await this.loadLocalContent();
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (testData.requires_oauth && testData.auth_url && testData.server_name) {
|
|
367
|
+
await this.startOAuthWindowFlow({
|
|
368
|
+
server_name: testData.server_name,
|
|
369
|
+
auth_url: testData.auth_url,
|
|
370
|
+
remote_url: remoteUrl
|
|
371
|
+
});
|
|
372
|
+
// startOAuthWindowFlow does post-completion recheck and refresh
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
throw new Error(testData.detail || 'Connection failed');
|
|
377
|
+
} catch (e) {
|
|
378
|
+
this.showToast(`Connect failed: ${e.message}`, 'error');
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async installOAuthMcpServer(item) {
|
|
384
|
+
try {
|
|
385
|
+
// 1) Ensure the server exists in backend manager
|
|
386
|
+
const addResp = await fetch('/admin/mcp/add', {
|
|
387
|
+
method: 'POST',
|
|
388
|
+
headers: { 'Content-Type': 'application/json' },
|
|
389
|
+
body: JSON.stringify(item.data)
|
|
390
|
+
});
|
|
391
|
+
if (!addResp.ok) {
|
|
392
|
+
const err = await addResp.json();
|
|
393
|
+
throw new Error(err.detail || 'Failed to install MCP server');
|
|
394
|
+
}
|
|
395
|
+
this.showToast(`MCP Server '${item.title}' installed locally. Connecting...`, 'info');
|
|
396
|
+
|
|
397
|
+
// 2) Kick off connection test which will return requires_oauth if needed
|
|
398
|
+
const testBody = {
|
|
399
|
+
url: item.data.transport_url || item.data.url || item.data.provider_url || item.data.authorization_server_url || '',
|
|
400
|
+
name: item.data.name || item.title
|
|
401
|
+
};
|
|
402
|
+
const testResp = await fetch('/admin/mcp/test-remote', {
|
|
403
|
+
method: 'POST',
|
|
404
|
+
headers: { 'Content-Type': 'application/json' },
|
|
405
|
+
body: JSON.stringify(testBody)
|
|
406
|
+
});
|
|
407
|
+
const testData = await testResp.json();
|
|
408
|
+
|
|
409
|
+
if (testData.success) {
|
|
410
|
+
this.showToast(testData.message || `Connected to '${item.title}'.`, 'success');
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (testData.requires_oauth && testData.auth_url && testData.server_name) {
|
|
415
|
+
// 3) Open OAuth window and wire completion to /admin/mcp/complete-oauth
|
|
416
|
+
await this.startOAuthWindowFlow({
|
|
417
|
+
server_name: testData.server_name,
|
|
418
|
+
auth_url: testData.auth_url,
|
|
419
|
+
remote_url: testBody.url
|
|
420
|
+
});
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
throw new Error(testData.detail || 'Connection failed');
|
|
425
|
+
} catch (error) {
|
|
426
|
+
throw new Error(`OAuth MCP Server installation failed: ${error.message}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async startOAuthWindowFlow({ server_name, auth_url, remote_url }) {
|
|
431
|
+
this.showToast(`Opening OAuth authorization for '${server_name}'...`, 'info');
|
|
432
|
+
|
|
433
|
+
const width = 600;
|
|
434
|
+
const height = 700;
|
|
435
|
+
const left = (screen.width - width) / 2;
|
|
436
|
+
const top = (screen.height - height) / 2;
|
|
437
|
+
const popup = window.open(
|
|
438
|
+
auth_url,
|
|
439
|
+
'oauth_window',
|
|
440
|
+
`width=${width},height=${height},left=${left},top=${top},scrollbars=yes,resizable=yes`
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
if (!popup) {
|
|
444
|
+
this.showToast('Failed to open OAuth window. Please allow popups and try again.', 'error');
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const onMessage = async (event) => {
|
|
449
|
+
if (!event.data || event.data.type !== 'oauth_callback') return;
|
|
450
|
+
window.removeEventListener('message', onMessage);
|
|
451
|
+
try {
|
|
452
|
+
// Close popup ASAP
|
|
453
|
+
if (popup && !popup.closed) popup.close();
|
|
454
|
+
|
|
455
|
+
// Complete the OAuth flow with backend
|
|
456
|
+
const resp = await fetch('/admin/mcp/complete-oauth', {
|
|
457
|
+
method: 'POST',
|
|
458
|
+
headers: { 'Content-Type': 'application/json' },
|
|
459
|
+
body: JSON.stringify({ server_name, code: event.data.code, state: event.data.state })
|
|
460
|
+
});
|
|
461
|
+
const data = await resp.json();
|
|
462
|
+
|
|
463
|
+
if (!data.success) {
|
|
464
|
+
this.showToast(data.detail || 'OAuth completion failed', 'error');
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Optionally re-test to fetch tools, like publisher
|
|
469
|
+
try {
|
|
470
|
+
const recheck = await fetch('/admin/mcp/test-remote', {
|
|
471
|
+
method: 'POST',
|
|
472
|
+
headers: { 'Content-Type': 'application/json' },
|
|
473
|
+
body: JSON.stringify({ url: remote_url, name: server_name })
|
|
474
|
+
});
|
|
475
|
+
const reData = await recheck.json();
|
|
476
|
+
if (reData.success) {
|
|
477
|
+
this.showToast(reData.message || `Connected to '${server_name}'.`, 'success');
|
|
478
|
+
}
|
|
479
|
+
} catch (e) {
|
|
480
|
+
// ignore
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Ensure dynamic commands are ready by refreshing local content
|
|
484
|
+
await this.loadLocalContent();
|
|
485
|
+
|
|
486
|
+
// As a final safety, if not connected, try /admin/mcp/connect
|
|
487
|
+
try {
|
|
488
|
+
await fetch('/admin/mcp/connect', {
|
|
489
|
+
method: 'POST',
|
|
490
|
+
headers: { 'Content-Type': 'application/json' },
|
|
491
|
+
body: JSON.stringify({ server_name })
|
|
492
|
+
});
|
|
493
|
+
await this.loadLocalContent();
|
|
494
|
+
} catch (_) {}
|
|
495
|
+
|
|
496
|
+
return true;
|
|
497
|
+
} catch (err) {
|
|
498
|
+
this.showToast(`OAuth completion failed: ${err.message}`, 'error');
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
window.addEventListener('message', onMessage);
|
|
504
|
+
|
|
505
|
+
// Cleanup if user closes popup before finishing
|
|
506
|
+
const checkClosed = setInterval(() => {
|
|
507
|
+
if (!popup || popup.closed) {
|
|
508
|
+
clearInterval(checkClosed);
|
|
509
|
+
window.removeEventListener('message', onMessage);
|
|
510
|
+
}
|
|
511
|
+
}, 1000);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
async installPlugin(item) {
|
|
515
|
+
return new Promise((resolve, reject) => {
|
|
516
|
+
try {
|
|
517
|
+
let installDialog = document.querySelector('plugin-install-dialog');
|
|
518
|
+
if (!installDialog) {
|
|
519
|
+
installDialog = document.createElement('plugin-install-dialog');
|
|
520
|
+
document.body.appendChild(installDialog);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const source = item.github_url ? 'github' : 'pypi';
|
|
524
|
+
const sourcePath = item.github_url || item.pypi_module;
|
|
525
|
+
|
|
526
|
+
installDialog.open(item.title, source.charAt(0).toUpperCase() + source.slice(1));
|
|
527
|
+
|
|
528
|
+
const params = new URLSearchParams();
|
|
529
|
+
params.append('plugin', item.title);
|
|
530
|
+
params.append('source', source);
|
|
531
|
+
params.append('source_path', sourcePath);
|
|
532
|
+
|
|
533
|
+
const eventSource = new EventSource(`/plugin-manager/stream-install-plugin?${params.toString()}`);
|
|
534
|
+
let hasError = false;
|
|
535
|
+
|
|
536
|
+
eventSource.addEventListener('message', (event) => {
|
|
537
|
+
installDialog.addOutput(event.data, 'info');
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
eventSource.addEventListener('error', (event) => {
|
|
541
|
+
hasError = true;
|
|
542
|
+
installDialog.addOutput(event.data, 'error');
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
eventSource.addEventListener('warning', (event) => {
|
|
546
|
+
installDialog.addOutput(event.data, 'warning');
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
eventSource.addEventListener('complete', (event) => {
|
|
550
|
+
installDialog.addOutput(event.data, 'success');
|
|
551
|
+
installDialog.setComplete(hasError);
|
|
552
|
+
eventSource.close();
|
|
553
|
+
this.loadLocalContent(); // Refresh plugin list
|
|
554
|
+
resolve();
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
eventSource.onerror = (err) => {
|
|
558
|
+
if (eventSource.readyState === EventSource.CLOSED) {
|
|
559
|
+
if (!hasError) {
|
|
560
|
+
installDialog.setComplete(false); // Closed cleanly
|
|
561
|
+
} else {
|
|
562
|
+
installDialog.addOutput('Connection closed with an error.', 'error');
|
|
563
|
+
installDialog.setComplete(true);
|
|
564
|
+
}
|
|
565
|
+
} else {
|
|
566
|
+
installDialog.addOutput('An unknown error occurred with the connection.', 'error');
|
|
567
|
+
installDialog.setComplete(true);
|
|
568
|
+
}
|
|
569
|
+
eventSource.close();
|
|
570
|
+
reject(new Error('Plugin installation stream failed.'));
|
|
571
|
+
};
|
|
572
|
+
} catch (error) {
|
|
573
|
+
this.showToast(`Failed to start plugin installation: ${error.message}`, 'error');
|
|
574
|
+
reject(error);
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
async installAgent(item) {
|
|
580
|
+
console.log('Installing agent from registry:', item);
|
|
581
|
+
|
|
582
|
+
if (item.data.persona_data && item.data.persona_ref) {
|
|
583
|
+
const personaAssets = await this.extractPersonaAssets(item.data.persona_ref, item.data.persona_data);
|
|
584
|
+
await this.installPersonaFromRegistry(item.data.persona_ref, item.data.persona_data, personaAssets);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const agentData = this.buildAgentData(item);
|
|
588
|
+
|
|
589
|
+
const response = await fetch('/agents/local', {
|
|
590
|
+
method: 'POST',
|
|
591
|
+
headers: {
|
|
592
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
593
|
+
},
|
|
594
|
+
body: `agent=${encodeURIComponent(JSON.stringify(agentData))}`
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
if (!response.ok) {
|
|
598
|
+
const errorData = await response.text();
|
|
599
|
+
if (errorData.includes('already exists')) {
|
|
600
|
+
const confirmOverwrite = confirm(`Agent "${agentData.name}" already exists. Do you want to overwrite it?`);
|
|
601
|
+
if (confirmOverwrite) {
|
|
602
|
+
agentData.overwrite = true;
|
|
603
|
+
const retryResponse = await fetch('/agents/local', {
|
|
604
|
+
method: 'POST',
|
|
605
|
+
headers: {
|
|
606
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
607
|
+
},
|
|
608
|
+
body: `agent=${encodeURIComponent(JSON.stringify(agentData))}`
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
if (!retryResponse.ok) {
|
|
612
|
+
throw new Error(`Agent installation failed: ${retryResponse.status}`);
|
|
613
|
+
}
|
|
614
|
+
} else {
|
|
615
|
+
throw new Error('Installation cancelled by user');
|
|
616
|
+
}
|
|
617
|
+
} else {
|
|
618
|
+
throw new Error(`Agent installation failed: ${response.status} - ${errorData}`);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
buildAgentData(item) {
|
|
624
|
+
const agentData = {
|
|
625
|
+
...item.data.agent_config || {},
|
|
626
|
+
name: item.title,
|
|
627
|
+
description: item.description,
|
|
628
|
+
instructions: item.data.instructions || item.data.agent_config?.instructions || '',
|
|
629
|
+
model: item.data.model || item.data.agent_config?.model || 'default',
|
|
630
|
+
recommended_plugins: item.data.recommended_plugins || item.data.required_plugins || item.data.agent_config?.recommended_plugins || item.data.agent_config?.required_plugins || [],
|
|
631
|
+
registry_owner: item.owner || item.creator,
|
|
632
|
+
registry_id: item.id,
|
|
633
|
+
registry_version: item.version,
|
|
634
|
+
installed_from_registry: true,
|
|
635
|
+
installed_at: new Date().toISOString()
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
if (item.data.persona_ref && item.data.persona_data) {
|
|
639
|
+
const owner = item.data.persona_ref.owner;
|
|
640
|
+
const personaName = item.data.persona_data.name;
|
|
641
|
+
agentData.persona = `registry/${owner}/${personaName}`;
|
|
642
|
+
} else if (!agentData.persona) {
|
|
643
|
+
agentData.persona = agentData.name;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
return agentData;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
async extractPersonaAssets(personaRef, personaData) {
|
|
650
|
+
try {
|
|
651
|
+
const assets = {};
|
|
652
|
+
const personaAssets = personaData.persona_assets || {};
|
|
653
|
+
|
|
654
|
+
if (personaAssets.avatar) {
|
|
655
|
+
const base64Data = personaAssets.avatar.data;
|
|
656
|
+
const binaryString = atob(base64Data);
|
|
657
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
658
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
659
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
660
|
+
}
|
|
661
|
+
const blob = new Blob([bytes], { type: personaAssets.avatar.type || 'image/png' });
|
|
662
|
+
assets.avatar = blob;
|
|
663
|
+
} else if (personaData.asset_hashes?.avatar) {
|
|
664
|
+
try {
|
|
665
|
+
const response = await fetch(`${this.state.registryUrl}/assets/${personaData.asset_hashes.avatar}`);
|
|
666
|
+
if (response.ok) {
|
|
667
|
+
assets.avatar = await response.blob();
|
|
668
|
+
}
|
|
669
|
+
} catch (e) {
|
|
670
|
+
console.log('Could not download avatar from asset store:', e);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (personaAssets.faceref) {
|
|
675
|
+
const base64Data = personaAssets.faceref.data;
|
|
676
|
+
const binaryString = atob(base64Data);
|
|
677
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
678
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
679
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
680
|
+
}
|
|
681
|
+
const blob = new Blob([bytes], { type: personaAssets.faceref.type || 'image/png' });
|
|
682
|
+
assets.faceref = blob;
|
|
683
|
+
} else if (personaData.asset_hashes?.faceref) {
|
|
684
|
+
try {
|
|
685
|
+
const response = await fetch(`${this.state.registryUrl}/assets/${personaData.asset_hashes.faceref}`);
|
|
686
|
+
if (response.ok) {
|
|
687
|
+
assets.faceref = await response.blob();
|
|
688
|
+
}
|
|
689
|
+
} catch (e) {
|
|
690
|
+
console.log('Could not download faceref from asset store:', e);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
return assets;
|
|
695
|
+
} catch (error) {
|
|
696
|
+
console.error('Error extracting persona assets:', error);
|
|
697
|
+
return {};
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
async installPersonaFromRegistry(personaRef, personaData, personaAssets) {
|
|
702
|
+
try {
|
|
703
|
+
const owner = personaRef.owner;
|
|
704
|
+
const personaName = personaData.name;
|
|
705
|
+
|
|
706
|
+
const cleanPersonaData = {
|
|
707
|
+
name: personaData.name,
|
|
708
|
+
description: personaData.description,
|
|
709
|
+
instructions: personaData.instructions,
|
|
710
|
+
model: personaData.model,
|
|
711
|
+
version: personaData.version,
|
|
712
|
+
moderated: personaData.moderated
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
Object.keys(cleanPersonaData).forEach(key => {
|
|
716
|
+
if (cleanPersonaData[key] === undefined) {
|
|
717
|
+
delete cleanPersonaData[key];
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
const formData = new FormData();
|
|
722
|
+
formData.append('persona', JSON.stringify(cleanPersonaData));
|
|
723
|
+
formData.append('owner', owner);
|
|
724
|
+
|
|
725
|
+
if (personaAssets && personaAssets.avatar) {
|
|
726
|
+
formData.append('avatar', personaAssets.avatar, 'avatar.png');
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (personaAssets && personaAssets.faceref) {
|
|
730
|
+
formData.append('faceref', personaAssets.faceref, 'faceref.png');
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const response = await fetch('/personas/registry/with-assets', {
|
|
734
|
+
method: 'POST',
|
|
735
|
+
body: formData
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
if (!response.ok) {
|
|
739
|
+
const errorText = await response.text();
|
|
740
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
console.log(`Installed registry persona: registry/${owner}/${personaName}`);
|
|
744
|
+
} catch (error) {
|
|
745
|
+
console.error('Error installing persona from registry:', error);
|
|
746
|
+
throw error;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
async checkAndInstallRecommendedPlugins(item) {
|
|
751
|
+
try {
|
|
752
|
+
const agentName = item.title;
|
|
753
|
+
const checkResponse = await fetch(`/admin/check-recommended-plugins/${encodeURIComponent(agentName)}`);
|
|
754
|
+
|
|
755
|
+
if (checkResponse.ok) {
|
|
756
|
+
const checkData = await checkResponse.json();
|
|
757
|
+
|
|
758
|
+
if (checkData.pending_plugins && checkData.pending_plugins.length > 0) {
|
|
759
|
+
console.log(`Found ${checkData.pending_plugins.length} recommended plugins to install:`, checkData.pending_plugins);
|
|
760
|
+
|
|
761
|
+
this.showToast(`Installing ${checkData.pending_plugins.length} recommended plugins...`, 'info');
|
|
762
|
+
|
|
763
|
+
let installDialog = document.querySelector('plugin-install-dialog');
|
|
764
|
+
if (!installDialog) {
|
|
765
|
+
installDialog = document.createElement('plugin-install-dialog');
|
|
766
|
+
document.body.appendChild(installDialog);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
installDialog.open(`Recommended plugins for ${agentName}`, 'Agent Setup');
|
|
770
|
+
|
|
771
|
+
const eventSource = new EventSource(`/admin/stream-install-recommended-plugins/${encodeURIComponent(agentName)}`);
|
|
772
|
+
|
|
773
|
+
let hasError = false;
|
|
774
|
+
let receivedEnd = false;
|
|
775
|
+
|
|
776
|
+
eventSource.onmessage = (event) => {
|
|
777
|
+
if (event.data === 'END') {
|
|
778
|
+
receivedEnd = true;
|
|
779
|
+
eventSource.close();
|
|
780
|
+
installDialog.setComplete(hasError);
|
|
781
|
+
this.loadLocalContent();
|
|
782
|
+
} else {
|
|
783
|
+
if (event.data.startsWith('ERROR:')) {
|
|
784
|
+
hasError = true;
|
|
785
|
+
installDialog.addOutput(event.data, 'error');
|
|
786
|
+
} else if (event.data.startsWith('WARNING:')) {
|
|
787
|
+
installDialog.addOutput(event.data, 'warning');
|
|
788
|
+
} else {
|
|
789
|
+
installDialog.addOutput(event.data, 'info');
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
eventSource.onerror = () => {
|
|
795
|
+
if (!receivedEnd) {
|
|
796
|
+
if (eventSource.readyState === EventSource.CLOSED) {
|
|
797
|
+
hasError = true;
|
|
798
|
+
installDialog.addOutput('Connection closed unexpectedly', 'error');
|
|
799
|
+
installDialog.setComplete(true);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
eventSource.close();
|
|
803
|
+
};
|
|
804
|
+
} else {
|
|
805
|
+
const agentRecommended = item.data.recommended_plugins || item.data.required_plugins || [];
|
|
806
|
+
if (agentRecommended.length > 0) {
|
|
807
|
+
this.showToast(`All ${agentRecommended.length} recommended plugins are already installed`, 'success');
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
} catch (error) {
|
|
812
|
+
console.error('Error checking recommended plugins:', error);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
// === Publishing Services ===
|
|
816
|
+
|
|
817
|
+
async refreshOwnershipCache() {
|
|
818
|
+
this.state.loading = true;
|
|
819
|
+
try {
|
|
820
|
+
const response = await fetch('/agents/refresh-ownership-cache', {
|
|
821
|
+
method: 'POST'
|
|
822
|
+
});
|
|
823
|
+
if (response.ok) {
|
|
824
|
+
const result = await response.json();
|
|
825
|
+
this.main.agentOwnership = result.data;
|
|
826
|
+
}
|
|
827
|
+
} catch (error) {
|
|
828
|
+
console.error('Error refreshing ownership cache:', error);
|
|
829
|
+
} finally {
|
|
830
|
+
this.state.loading = false;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
async publishPluginFromGithub(repo) {
|
|
835
|
+
if (!repo || !repo.includes('/')) {
|
|
836
|
+
this.showToast('Please provide a valid GitHub repository (e.g., user/repo)', 'error');
|
|
837
|
+
return false;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
this.state.loading = true;
|
|
841
|
+
this.main.error = '';
|
|
842
|
+
|
|
843
|
+
try {
|
|
844
|
+
const response = await fetch('/admin/plugins/publish_from_github', {
|
|
845
|
+
method: 'POST',
|
|
846
|
+
headers: {
|
|
847
|
+
'Content-Type': 'application/json',
|
|
848
|
+
'Authorization': `Bearer ${this.state.authToken}`
|
|
849
|
+
},
|
|
850
|
+
body: JSON.stringify({ repo: repo, registry_url: this.state.registryUrl })
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
if (response.ok) {
|
|
854
|
+
const result = await response.json();
|
|
855
|
+
this.showToast(result.message || 'Published successfully!', 'success');
|
|
856
|
+
return true;
|
|
857
|
+
} else {
|
|
858
|
+
const errorData = await response.json();
|
|
859
|
+
this.showToast(errorData.detail || 'Publishing failed', 'error');
|
|
860
|
+
return false;
|
|
861
|
+
}
|
|
862
|
+
} catch (error) {
|
|
863
|
+
this.showToast('Network error: ' + error.message, 'error');
|
|
864
|
+
return false;
|
|
865
|
+
} finally {
|
|
866
|
+
this.state.loading = false;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// === Settings Services ===
|
|
871
|
+
|
|
872
|
+
async updateRegistryUrl(newUrl) {
|
|
873
|
+
this.state.registryUrl = newUrl;
|
|
874
|
+
localStorage.setItem('registry_url', newUrl);
|
|
875
|
+
this.logout();
|
|
876
|
+
await this.loadStats();
|
|
877
|
+
this.main.requestUpdate();
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
async testConnection() {
|
|
881
|
+
try {
|
|
882
|
+
const response = await fetch(`${this.state.registryUrl}/stats`);
|
|
883
|
+
if (response.ok) {
|
|
884
|
+
this.main.error = '';
|
|
885
|
+
await this.loadStats();
|
|
886
|
+
const originalError = this.main.error;
|
|
887
|
+
this.main.error = 'Connection successful!';
|
|
888
|
+
this.main.requestUpdate();
|
|
889
|
+
setTimeout(() => {
|
|
890
|
+
this.main.error = originalError;
|
|
891
|
+
this.main.requestUpdate();
|
|
892
|
+
}, 2000);
|
|
893
|
+
} else {
|
|
894
|
+
this.main.error = `Connection failed: ${response.status}`;
|
|
895
|
+
}
|
|
896
|
+
} catch (error) {
|
|
897
|
+
this.main.error = `Connection failed: ${error.message}`;
|
|
898
|
+
}
|
|
899
|
+
this.main.requestUpdate();
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
export { RegistrySharedServices };
|