imcp 0.0.13 → 0.0.15
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.
- package/dist/core/ConfigurationProvider.d.ts +1 -0
- package/dist/core/ConfigurationProvider.js +15 -0
- package/dist/core/InstallationService.js +2 -7
- package/dist/core/MCPManager.d.ts +11 -2
- package/dist/core/MCPManager.js +24 -1
- package/dist/core/RequirementService.js +2 -8
- package/dist/core/installers/clients/BaseClientInstaller.d.ts +51 -0
- package/dist/core/installers/clients/BaseClientInstaller.js +160 -0
- package/dist/core/installers/clients/ClientInstaller.d.ts +16 -8
- package/dist/core/installers/clients/ClientInstaller.js +77 -504
- package/dist/core/installers/clients/ClientInstallerFactory.d.ts +19 -0
- package/dist/core/installers/clients/ClientInstallerFactory.js +41 -0
- package/dist/core/installers/clients/ClineInstaller.d.ts +18 -0
- package/dist/core/installers/clients/ClineInstaller.js +124 -0
- package/dist/core/installers/clients/GithubCopilotInstaller.d.ts +34 -0
- package/dist/core/installers/clients/GithubCopilotInstaller.js +162 -0
- package/dist/core/installers/clients/MSRooCodeInstaller.d.ts +15 -0
- package/dist/core/installers/clients/MSRooCodeInstaller.js +122 -0
- package/dist/core/installers/requirements/BaseInstaller.d.ts +11 -34
- package/dist/core/installers/requirements/BaseInstaller.js +5 -116
- package/dist/core/installers/requirements/CommandInstaller.d.ts +6 -1
- package/dist/core/installers/requirements/CommandInstaller.js +7 -0
- package/dist/core/installers/requirements/GeneralInstaller.d.ts +6 -1
- package/dist/core/installers/requirements/GeneralInstaller.js +9 -4
- package/dist/core/installers/requirements/NpmInstaller.d.ts +46 -7
- package/dist/core/installers/requirements/NpmInstaller.js +150 -58
- package/dist/core/installers/requirements/PipInstaller.d.ts +9 -0
- package/dist/core/installers/requirements/PipInstaller.js +66 -28
- package/dist/core/onboard/FeedOnboardService.d.ts +72 -0
- package/dist/core/onboard/FeedOnboardService.js +312 -0
- package/dist/core/onboard/OnboardProcessor.d.ts +79 -0
- package/dist/core/onboard/OnboardProcessor.js +290 -0
- package/dist/core/onboard/OnboardStatus.d.ts +49 -0
- package/dist/core/onboard/OnboardStatus.js +10 -0
- package/dist/core/onboard/OnboardStatusManager.d.ts +57 -0
- package/dist/core/onboard/OnboardStatusManager.js +176 -0
- package/dist/core/types.d.ts +6 -6
- package/dist/core/validators/FeedValidator.d.ts +20 -0
- package/dist/core/validators/FeedValidator.js +80 -0
- package/dist/core/validators/IServerValidator.d.ts +19 -0
- package/dist/core/validators/IServerValidator.js +2 -0
- package/dist/core/validators/SSEServerValidator.d.ts +15 -0
- package/dist/core/validators/SSEServerValidator.js +39 -0
- package/dist/core/validators/ServerValidatorFactory.d.ts +24 -0
- package/dist/core/validators/ServerValidatorFactory.js +45 -0
- package/dist/core/validators/StdioServerValidator.d.ts +46 -0
- package/dist/core/validators/StdioServerValidator.js +229 -0
- package/dist/services/InstallRequestValidator.d.ts +1 -1
- package/dist/services/ServerService.d.ts +9 -6
- package/dist/services/ServerService.js +18 -7
- package/dist/utils/adoUtils.d.ts +29 -0
- package/dist/utils/adoUtils.js +252 -0
- package/dist/utils/clientUtils.d.ts +0 -7
- package/dist/utils/clientUtils.js +0 -42
- package/dist/utils/githubUtils.d.ts +10 -0
- package/dist/utils/githubUtils.js +22 -0
- package/dist/utils/macroExpressionUtils.d.ts +38 -0
- package/dist/utils/macroExpressionUtils.js +116 -0
- package/dist/utils/osUtils.d.ts +4 -20
- package/dist/utils/osUtils.js +78 -23
- package/dist/web/contract/serverContract.d.ts +66 -0
- package/dist/web/contract/serverContract.js +2 -0
- package/dist/web/public/css/notifications.css +48 -17
- package/dist/web/public/css/onboard.css +107 -0
- package/dist/web/public/index.html +90 -18
- package/dist/web/public/js/api.js +3 -6
- package/dist/web/public/js/flights/flights.js +127 -0
- package/dist/web/public/js/modal/index.js +58 -0
- package/dist/web/public/js/modal/installHandler.js +227 -0
- package/dist/web/public/js/modal/installModal.js +163 -0
- package/dist/web/public/js/modal/installation.js +281 -0
- package/dist/web/public/js/modal/loadingModal.js +52 -0
- package/dist/web/public/js/modal/loadingUI.js +74 -0
- package/dist/web/public/js/modal/messageQueue.js +112 -0
- package/dist/web/public/js/modal/modalSetup.js +513 -0
- package/dist/web/public/js/modal/modalUI.js +214 -0
- package/dist/web/public/js/modal/modalUtils.js +49 -0
- package/dist/web/public/js/modal/version.js +20 -0
- package/dist/web/public/js/modal/versionUtils.js +20 -0
- package/dist/web/public/js/modal.js +25 -1041
- package/dist/web/public/js/notifications.js +66 -27
- package/dist/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
- package/dist/web/public/js/onboard/formProcessor.js +864 -0
- package/dist/web/public/js/onboard/index.js +374 -0
- package/dist/web/public/js/onboard/publishHandler.js +132 -0
- package/dist/web/public/js/onboard/state.js +76 -0
- package/dist/web/public/js/onboard/templates.js +343 -0
- package/dist/web/public/js/onboard/uiHandlers.js +758 -0
- package/dist/web/public/js/onboard/validationHandlers.js +378 -0
- package/dist/web/public/js/serverCategoryDetails.js +43 -17
- package/dist/web/public/js/serverCategoryList.js +15 -2
- package/dist/web/public/onboard.html +296 -0
- package/dist/web/public/styles.css +91 -1
- package/dist/web/server.d.ts +0 -10
- package/dist/web/server.js +131 -22
- package/package.json +2 -2
- package/src/core/ConfigurationProvider.ts +15 -0
- package/src/core/InstallationService.ts +2 -7
- package/src/core/MCPManager.ts +26 -1
- package/src/core/RequirementService.ts +2 -9
- package/src/core/installers/clients/BaseClientInstaller.ts +196 -0
- package/src/core/installers/clients/ClientInstaller.ts +97 -589
- package/src/core/installers/clients/ClientInstallerFactory.ts +46 -0
- package/src/core/installers/clients/ClineInstaller.ts +135 -0
- package/src/core/installers/clients/GithubCopilotInstaller.ts +179 -0
- package/src/core/installers/clients/MSRooCodeInstaller.ts +133 -0
- package/src/core/installers/requirements/BaseInstaller.ts +13 -136
- package/src/core/installers/requirements/CommandInstaller.ts +9 -1
- package/src/core/installers/requirements/GeneralInstaller.ts +11 -4
- package/src/core/installers/requirements/NpmInstaller.ts +178 -61
- package/src/core/installers/requirements/PipInstaller.ts +68 -29
- package/src/core/onboard/FeedOnboardService.ts +346 -0
- package/src/core/onboard/OnboardProcessor.ts +305 -0
- package/src/core/onboard/OnboardStatus.ts +55 -0
- package/src/core/onboard/OnboardStatusManager.ts +188 -0
- package/src/core/types.ts +6 -6
- package/src/core/validators/FeedValidator.ts +79 -0
- package/src/core/validators/IServerValidator.ts +21 -0
- package/src/core/validators/SSEServerValidator.ts +43 -0
- package/src/core/validators/ServerValidatorFactory.ts +51 -0
- package/src/core/validators/StdioServerValidator.ts +259 -0
- package/src/services/InstallRequestValidator.ts +1 -1
- package/src/services/ServerService.ts +22 -7
- package/src/utils/adoUtils.ts +291 -0
- package/src/utils/clientUtils.ts +0 -44
- package/src/utils/githubUtils.ts +24 -0
- package/src/utils/macroExpressionUtils.ts +121 -0
- package/src/utils/osUtils.ts +89 -24
- package/src/web/contract/serverContract.ts +74 -0
- package/src/web/public/css/notifications.css +48 -17
- package/src/web/public/css/onboard.css +107 -0
- package/src/web/public/index.html +90 -18
- package/src/web/public/js/api.js +3 -6
- package/src/web/public/js/flights/flights.js +127 -0
- package/src/web/public/js/modal/index.js +58 -0
- package/src/web/public/js/modal/installModal.js +163 -0
- package/src/web/public/js/modal/installation.js +281 -0
- package/src/web/public/js/modal/loadingModal.js +52 -0
- package/src/web/public/js/modal/messageQueue.js +112 -0
- package/src/web/public/js/modal/modalSetup.js +513 -0
- package/src/web/public/js/modal/modalUtils.js +49 -0
- package/src/web/public/js/modal/versionUtils.js +20 -0
- package/src/web/public/js/modal.js +25 -1041
- package/src/web/public/js/notifications.js +66 -27
- package/src/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
- package/src/web/public/js/onboard/formProcessor.js +864 -0
- package/src/web/public/js/onboard/index.js +374 -0
- package/src/web/public/js/onboard/publishHandler.js +132 -0
- package/src/web/public/js/onboard/state.js +76 -0
- package/src/web/public/js/onboard/templates.js +343 -0
- package/src/web/public/js/onboard/uiHandlers.js +758 -0
- package/src/web/public/js/onboard/validationHandlers.js +378 -0
- package/src/web/public/js/serverCategoryDetails.js +43 -17
- package/src/web/public/js/serverCategoryList.js +15 -2
- package/src/web/public/onboard.html +296 -0
- package/src/web/public/styles.css +91 -1
- package/src/web/server.ts +167 -58
|
@@ -1,1045 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
setupModalOutsideClick
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (v1Part < v2Part) return -1;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return 0;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Message queue for delayed loading modal updates.
|
|
30
|
-
* @type {Array<{msg: string, isCompletion: boolean}>}
|
|
31
|
-
*/
|
|
32
|
-
let messageQueue = [];
|
|
33
|
-
|
|
34
|
-
/** @type {boolean} Flag indicating if a message is currently being appended */
|
|
35
|
-
let isAppending = false;
|
|
36
|
-
|
|
37
|
-
/** @type {boolean} Flag indicating if completion UI update is pending */
|
|
38
|
-
let completionPending = false;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Process messages in the queue with a delay between each message.
|
|
42
|
-
* When the queue is empty and completion is pending, triggers the completion UI.
|
|
43
|
-
* @private
|
|
44
|
-
*/
|
|
45
|
-
function processMessageQueue() {
|
|
46
|
-
if (isAppending || messageQueue.length === 0) {
|
|
47
|
-
// Only update completion UI if all messages have been displayed
|
|
48
|
-
if (messageQueue.length === 0 && completionPending) {
|
|
49
|
-
// Add small delay before showing completion to ensure last message is visible
|
|
50
|
-
setTimeout(() => {
|
|
51
|
-
updateCompletionUI();
|
|
52
|
-
completionPending = false;
|
|
53
|
-
}, 500);
|
|
54
|
-
}
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
isAppending = true;
|
|
59
|
-
const { msg, isCompletion } = messageQueue.shift();
|
|
60
|
-
|
|
61
|
-
if (isCompletion) {
|
|
62
|
-
completionPending = true;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
_appendInstallLoadingMessage(msg);
|
|
66
|
-
|
|
67
|
-
setTimeout(() => {
|
|
68
|
-
isAppending = false;
|
|
69
|
-
processMessageQueue();
|
|
70
|
-
}, 1000);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Queue a message to be displayed with a delay.
|
|
75
|
-
* Messages are displayed sequentially with a 1-second delay between each.
|
|
76
|
-
* @param {string} msg The message to display
|
|
77
|
-
* @param {boolean} [isCompletion=false] If true, triggers completion UI after message display
|
|
78
|
-
*/
|
|
79
|
-
function delayedAppendInstallLoadingMessage(msg, isCompletion = false) {
|
|
80
|
-
messageQueue.push({ msg, isCompletion });
|
|
81
|
-
processMessageQueue();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function updateCompletionUI() {
|
|
85
|
-
const loadingTitle = document.querySelector('.loading-title');
|
|
86
|
-
const loadingIcon = document.querySelector('.loading-icon');
|
|
87
|
-
|
|
88
|
-
if (loadingTitle) {
|
|
89
|
-
loadingTitle.textContent = 'Completed';
|
|
90
|
-
loadingTitle.classList.add('completed');
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (loadingIcon) {
|
|
94
|
-
loadingIcon.innerHTML = `
|
|
95
|
-
<svg width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
96
|
-
<circle cx="24" cy="24" r="20" stroke="#059669" stroke-width="4"/>
|
|
97
|
-
<path d="M16 24l6 6 12-12" stroke="#059669" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
|
98
|
-
</svg>
|
|
99
|
-
`;
|
|
100
|
-
loadingIcon.classList.add('completed');
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Internal function to append a message to the loading modal with formatting.
|
|
105
|
-
* @private
|
|
106
|
-
* @param {string} message - The message to append
|
|
107
|
-
*/
|
|
108
|
-
function _appendInstallLoadingMessage(message) {
|
|
109
|
-
const loadingMsg = document.getElementById('installLoadingMessage');
|
|
110
|
-
if (loadingMsg) {
|
|
111
|
-
// Split message on newlines
|
|
112
|
-
const lines = message.split('\n');
|
|
113
|
-
|
|
114
|
-
lines.forEach(line => {
|
|
115
|
-
// Check if line contains an error
|
|
116
|
-
const isError = line.toLowerCase().includes('error') ||
|
|
117
|
-
line.toLowerCase().includes('failed') ||
|
|
118
|
-
line.toLowerCase().includes('timeout');
|
|
119
|
-
|
|
120
|
-
const formattedMessage = line;
|
|
121
|
-
|
|
122
|
-
// Create message container
|
|
123
|
-
const messageDiv = document.createElement('div');
|
|
124
|
-
messageDiv.className = 'message-line';
|
|
125
|
-
|
|
126
|
-
// Apply yellow color for error messages
|
|
127
|
-
if (isError) {
|
|
128
|
-
messageDiv.style.color = '#f59e0b';
|
|
129
|
-
}
|
|
130
|
-
messageDiv.innerHTML = formattedMessage;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
loadingMsg.appendChild(messageDiv);
|
|
134
|
-
loadingMsg.scrollTop = loadingMsg.scrollHeight;
|
|
135
|
-
});
|
|
136
|
-
} else {
|
|
137
|
-
console.error('[LoadingModal] Element not found: installLoadingMessage');
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Display the installation loading modal and prepare it for messages.
|
|
143
|
-
*/
|
|
144
|
-
function showInstallLoadingModal(operation = 'Installing') {
|
|
145
|
-
const loadingModal = document.getElementById('installLoadingModal');
|
|
146
|
-
const loadingMsg = document.getElementById('installLoadingMessage');
|
|
147
|
-
const loadingTitle = document.querySelector('.loading-title');
|
|
148
|
-
if (loadingModal && loadingMsg && loadingTitle) {
|
|
149
|
-
loadingModal.style.display = 'block';
|
|
150
|
-
loadingMsg.innerHTML = '';
|
|
151
|
-
loadingTitle.textContent = `${operation}...`;
|
|
152
|
-
} else {
|
|
153
|
-
console.error('[LoadingModal] Required elements not found: installLoadingModal, installLoadingMessage, or loading-title');
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Append a message to the loading modal display.
|
|
159
|
-
* @param {string} message - The message to display
|
|
160
|
-
*/
|
|
161
|
-
function appendInstallLoadingMessage(message) {
|
|
162
|
-
console.log(`[LoadingModal] Message: ${message}`);
|
|
163
|
-
_appendInstallLoadingMessage(message);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Hide the install loading modal.
|
|
168
|
-
*/
|
|
169
|
-
function hideInstallLoadingModal() {
|
|
170
|
-
console.log('[LoadingModal] Hiding installation modal');
|
|
171
|
-
const loadingModal = document.getElementById('installLoadingModal');
|
|
172
|
-
if (loadingModal) {
|
|
173
|
-
loadingModal.style.display = 'none';
|
|
174
|
-
|
|
175
|
-
// Get the last selected category
|
|
176
|
-
const lastSelected = localStorage.getItem('lastSelectedCategory');
|
|
177
|
-
|
|
178
|
-
// Refresh page while maintaining category selection
|
|
179
|
-
setTimeout(() => {
|
|
180
|
-
if (lastSelected) {
|
|
181
|
-
window.location.href = window.location.pathname + '?category=' + encodeURIComponent(lastSelected);
|
|
182
|
-
} else {
|
|
183
|
-
location.reload();
|
|
184
|
-
}
|
|
185
|
-
}, 100);
|
|
186
|
-
} else {
|
|
187
|
-
console.error('[LoadingModal] loading modal DOM not found');
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Show install modal for MCP tools
|
|
192
|
-
async function showInstallModal(categoryName, serverName, callback) {
|
|
193
|
-
console.log("Showing install modal for:", serverName);
|
|
194
|
-
|
|
195
|
-
// Wait for a short delay to ensure modal is loaded
|
|
196
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
197
|
-
|
|
198
|
-
const modal = document.getElementById('installModal');
|
|
199
|
-
if (!modal) {
|
|
200
|
-
console.error('Modal container not found');
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const title = modal.querySelector('#modalTitle');
|
|
205
|
-
const envInputsDiv = modal.querySelector('#modalEnvInputs');
|
|
206
|
-
const targetDiv = modal.querySelector('#modalTargets');
|
|
207
|
-
const modalRequirements = modal.querySelector('#modalRequirements');
|
|
208
|
-
const modalArguments = modal.querySelector('#modalArguments');
|
|
209
|
-
|
|
210
|
-
// Global array to track selected clients
|
|
211
|
-
window.selectedClients = [];
|
|
212
|
-
|
|
213
|
-
// Verify all required modal elements exist
|
|
214
|
-
if (!title || !envInputsDiv || !targetDiv || !modalRequirements || !modalArguments) {
|
|
215
|
-
console.error('Required modal elements not found');
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
title.textContent = `Install ${serverName}`;
|
|
220
|
-
envInputsDiv.innerHTML = ''; // Clear previous inputs
|
|
221
|
-
targetDiv.innerHTML = ''; // Clear previous targets
|
|
222
|
-
modalRequirements.innerHTML = ''; // Clear previous requirements
|
|
223
|
-
modalArguments.innerHTML = ''; // Clear previous arguments
|
|
224
|
-
|
|
225
|
-
try {
|
|
226
|
-
// Fetch both targets and server data simultaneously
|
|
227
|
-
const [targetResponse, serverResponse] = await Promise.all([
|
|
228
|
-
fetch('/api/targets'),
|
|
229
|
-
fetch(`/api/categories/${categoryName}`)
|
|
230
|
-
]);
|
|
231
|
-
|
|
232
|
-
if (!targetResponse.ok || !serverResponse.ok) {
|
|
233
|
-
throw new Error('Failed to fetch required data');
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const [targetData, serverData] = await Promise.all([
|
|
237
|
-
targetResponse.json(),
|
|
238
|
-
serverResponse.json()
|
|
239
|
-
]);
|
|
240
|
-
|
|
241
|
-
if (!targetData.success || !serverData.success) {
|
|
242
|
-
throw new Error('Invalid data received');
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const mcpServer = serverData.data.feedConfiguration.mcpServers.find(server => server.name === serverName);
|
|
246
|
-
if (!mcpServer) {
|
|
247
|
-
throw new Error('Server configuration not found');
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const installationStatus = serverData.data.installationStatus || {};
|
|
251
|
-
const serverStatuses = installationStatus.serversStatus || {};
|
|
252
|
-
const serverStatus = serverStatuses[serverName] || { installedStatus: {} };
|
|
253
|
-
|
|
254
|
-
// Create client items with switch toggles
|
|
255
|
-
targetData.data.forEach(target => {
|
|
256
|
-
const operationStatus = serverStatus.installedStatus[target] || { status: 'not-installed', type: 'check', target: 'server' };
|
|
257
|
-
|
|
258
|
-
// Determine client status
|
|
259
|
-
let statusText = '';
|
|
260
|
-
let statusClass = '';
|
|
261
|
-
|
|
262
|
-
if (operationStatus.status === 'completed' && operationStatus.type === 'install') {
|
|
263
|
-
statusText = 'Installed';
|
|
264
|
-
statusClass = 'installed';
|
|
265
|
-
} else if (operationStatus.status === 'pending') {
|
|
266
|
-
statusText = 'Pending Requirements';
|
|
267
|
-
statusClass = 'pending';
|
|
268
|
-
} else if (operationStatus.status === 'in-progress') {
|
|
269
|
-
statusText = 'In Progress';
|
|
270
|
-
statusClass = 'pending';
|
|
271
|
-
} else if (operationStatus.status === 'failed') {
|
|
272
|
-
statusText = 'Failed';
|
|
273
|
-
statusClass = 'not-installed';
|
|
274
|
-
}
|
|
275
|
-
// Do not show not-installed status for targets that aren't installed
|
|
276
|
-
|
|
277
|
-
const isConfigured = operationStatus.status === 'completed' && operationStatus.type === 'install';
|
|
278
|
-
|
|
279
|
-
// Create client item
|
|
280
|
-
const clientItem = document.createElement('div');
|
|
281
|
-
clientItem.className = 'client-item';
|
|
282
|
-
clientItem.dataset.target = target;
|
|
283
|
-
clientItem.dataset.selected = 'false';
|
|
284
|
-
|
|
285
|
-
// Determine if item should be selectable
|
|
286
|
-
const isInProgress = operationStatus.status === 'in-progress';
|
|
287
|
-
const isSelectable = !isConfigured && !isInProgress;
|
|
288
|
-
|
|
289
|
-
// Add appropriate non-selectable classes
|
|
290
|
-
if (!isSelectable) {
|
|
291
|
-
clientItem.classList.add('non-selectable');
|
|
292
|
-
|
|
293
|
-
if (isConfigured) {
|
|
294
|
-
clientItem.classList.add('installed-item');
|
|
295
|
-
clientItem.title = 'Already installed';
|
|
296
|
-
} else if (isInProgress) {
|
|
297
|
-
clientItem.classList.add('in-progress-item');
|
|
298
|
-
clientItem.title = 'Installation in progress';
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Make selectable clients clickable
|
|
303
|
-
if (isSelectable) {
|
|
304
|
-
clientItem.addEventListener('click', (e) => {
|
|
305
|
-
const isSelected = clientItem.dataset.selected === 'true';
|
|
306
|
-
clientItem.dataset.selected = isSelected ? 'false' : 'true';
|
|
307
|
-
|
|
308
|
-
// Update class
|
|
309
|
-
if (isSelected) {
|
|
310
|
-
clientItem.classList.remove('selected');
|
|
311
|
-
window.selectedClients = window.selectedClients.filter(c => c !== target);
|
|
312
|
-
} else {
|
|
313
|
-
clientItem.classList.add('selected');
|
|
314
|
-
if (!window.selectedClients.includes(target)) {
|
|
315
|
-
window.selectedClients.push(target);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
console.log('Selected clients:', window.selectedClients);
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Create client info (name)
|
|
324
|
-
const clientInfo = document.createElement('div');
|
|
325
|
-
clientInfo.className = 'client-info';
|
|
326
|
-
|
|
327
|
-
// Client name label
|
|
328
|
-
const clientName = document.createElement('span');
|
|
329
|
-
clientName.className = 'text-sm font-medium text-gray-900';
|
|
330
|
-
clientName.textContent = target;
|
|
331
|
-
|
|
332
|
-
// Add elements to client info
|
|
333
|
-
clientInfo.appendChild(clientName);
|
|
334
|
-
|
|
335
|
-
// Add elements to client item
|
|
336
|
-
clientItem.appendChild(clientInfo);
|
|
337
|
-
|
|
338
|
-
// Status container for badge and uninstall button
|
|
339
|
-
const statusContainer = document.createElement('div');
|
|
340
|
-
statusContainer.className = 'status-container';
|
|
341
|
-
|
|
342
|
-
// Status badge - only show if we have status text
|
|
343
|
-
if (statusText) {
|
|
344
|
-
const statusBadge = document.createElement('span');
|
|
345
|
-
statusBadge.className = `status-badge ${statusClass}`;
|
|
346
|
-
statusBadge.textContent = statusText;
|
|
347
|
-
statusContainer.appendChild(statusBadge);
|
|
348
|
-
|
|
349
|
-
// Add uninstall button right after status badge if installed
|
|
350
|
-
if (operationStatus.status === 'completed' && operationStatus.type === 'install') {
|
|
351
|
-
const uninstallBtn = document.createElement('button');
|
|
352
|
-
uninstallBtn.className = 'uninstall-btn text-red-600 hover:text-red-800 ml-2';
|
|
353
|
-
uninstallBtn.innerHTML = '<i class="bx bx-trash"></i>';
|
|
354
|
-
uninstallBtn.title = 'Uninstall from this client';
|
|
355
|
-
uninstallBtn.onclick = async (e) => {
|
|
356
|
-
e.stopPropagation(); // Prevent item selection
|
|
357
|
-
e.preventDefault(); // Prevent form submission
|
|
358
|
-
const confirmed = await showConfirm('Uninstall Confirmation', `Are you sure you want to uninstall ${serverName} from ${target}?`);
|
|
359
|
-
if (confirmed) {
|
|
360
|
-
// Add target to selectedClients for uninstallation
|
|
361
|
-
window.selectedClients = [target];
|
|
362
|
-
showInstallLoadingModal('Uninstalling');
|
|
363
|
-
const serverList = {
|
|
364
|
-
[serverName]: {
|
|
365
|
-
removeData: true // Include removal of associated data
|
|
366
|
-
}
|
|
367
|
-
};
|
|
368
|
-
try {
|
|
369
|
-
delayedAppendInstallLoadingMessage(`Uninstalling ${serverName} from ${target}...`);
|
|
370
|
-
await uninstallTools(categoryName, serverList, [target]);
|
|
371
|
-
} catch (error) {
|
|
372
|
-
delayedAppendInstallLoadingMessage(`Error: ${error.message}`);
|
|
373
|
-
throw error; // Re-throw to trigger error handling in uninstallTools
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
return false; // Prevent form submission
|
|
377
|
-
};
|
|
378
|
-
statusContainer.appendChild(uninstallBtn);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
clientItem.appendChild(statusContainer);
|
|
383
|
-
|
|
384
|
-
// Add client item to target div
|
|
385
|
-
targetDiv.appendChild(clientItem);
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
// Add section title at the top of the modal target div
|
|
389
|
-
if (!targetDiv.querySelector('.section-title')) {
|
|
390
|
-
const titleElement = document.createElement('h3');
|
|
391
|
-
titleElement.className = 'section-title text-lg font-semibold text-gray-700 mb-3';
|
|
392
|
-
titleElement.textContent = 'Client Status';
|
|
393
|
-
targetDiv.insertBefore(titleElement, targetDiv.firstChild);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// Handle environment variables section
|
|
397
|
-
const envRequirements = mcpServer.installation?.['env'] || mcpServer.installation?.env || {};
|
|
398
|
-
|
|
399
|
-
// Make sure environment variables section has a title
|
|
400
|
-
if (!envInputsDiv.querySelector('.section-title')) {
|
|
401
|
-
const envTitle = document.createElement('h3');
|
|
402
|
-
envTitle.className = 'section-title text-lg font-semibold text-gray-700 mb-3';
|
|
403
|
-
envTitle.textContent = 'Environment Variables';
|
|
404
|
-
envInputsDiv.insertBefore(envTitle, envInputsDiv.firstChild);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Handle installation arguments section
|
|
408
|
-
const installation = mcpServer.installation;
|
|
409
|
-
if (installation) {
|
|
410
|
-
// Add section title
|
|
411
|
-
const argsTitle = document.createElement('h3');
|
|
412
|
-
argsTitle.className = 'section-title text-lg font-semibold text-gray-700 mb-3';
|
|
413
|
-
argsTitle.textContent = 'Arguments';
|
|
414
|
-
modalArguments.appendChild(argsTitle);
|
|
415
|
-
|
|
416
|
-
const argsContainer = document.createElement('div');
|
|
417
|
-
argsContainer.className = 'args-container mb-3';
|
|
418
|
-
|
|
419
|
-
// Add button to add new argument
|
|
420
|
-
const addButton = document.createElement('button');
|
|
421
|
-
addButton.type = 'button';
|
|
422
|
-
addButton.className = 'add-arg-button px-3 py-1 text-sm text-blue-600 hover:text-blue-800 border border-blue-600 hover:border-blue-800 rounded-md mb-2';
|
|
423
|
-
addButton.innerHTML = '<i class="bx bx-plus"></i> Add Argument';
|
|
424
|
-
argsContainer.appendChild(addButton);
|
|
425
|
-
|
|
426
|
-
// Function to create new argument input
|
|
427
|
-
const createArgInput = (value = '') => {
|
|
428
|
-
const argWrapper = document.createElement('div');
|
|
429
|
-
argWrapper.className = 'arg-wrapper flex items-center gap-2 mb-2';
|
|
430
|
-
|
|
431
|
-
const input = document.createElement('input');
|
|
432
|
-
input.type = 'text';
|
|
433
|
-
input.className = 'arg-input flex-grow';
|
|
434
|
-
input.value = value;
|
|
435
|
-
input.placeholder = 'Enter argument value';
|
|
436
|
-
|
|
437
|
-
const removeButton = document.createElement('button');
|
|
438
|
-
removeButton.type = 'button';
|
|
439
|
-
removeButton.className = 'remove-arg-button text-red-600 hover:text-red-800';
|
|
440
|
-
removeButton.innerHTML = '<i class="bx bx-trash"></i>';
|
|
441
|
-
removeButton.onclick = () => argWrapper.remove();
|
|
442
|
-
|
|
443
|
-
argWrapper.appendChild(input);
|
|
444
|
-
argWrapper.appendChild(removeButton);
|
|
445
|
-
return argWrapper;
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
// Add click handler for add button
|
|
449
|
-
addButton.onclick = () => {
|
|
450
|
-
argsContainer.appendChild(createArgInput());
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
// Create wrapper for args section
|
|
454
|
-
const argsWrapper = document.createElement('div');
|
|
455
|
-
argsWrapper.className = 'mb-3';
|
|
456
|
-
|
|
457
|
-
// Set default values from installation args if available
|
|
458
|
-
if (installation.args && Array.isArray(installation.args)) {
|
|
459
|
-
installation.args.forEach(arg => {
|
|
460
|
-
argsContainer.appendChild(createArgInput(arg));
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// Add initial empty input if no args
|
|
465
|
-
if (!installation.args || installation.args.length === 0) {
|
|
466
|
-
argsContainer.appendChild(createArgInput());
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
argsWrapper.appendChild(argsContainer);
|
|
470
|
-
|
|
471
|
-
// Add description
|
|
472
|
-
const description = document.createElement('p');
|
|
473
|
-
description.className = 'text-xs text-gray-500 mt-1';
|
|
474
|
-
description.textContent = 'Specify installation arguments, one per line. These will be used when installing the server.';
|
|
475
|
-
argsWrapper.appendChild(description);
|
|
476
|
-
|
|
477
|
-
modalArguments.appendChild(argsWrapper);
|
|
478
|
-
|
|
479
|
-
// If command type is python, add python environment input
|
|
480
|
-
if (installation.command === 'python' || installation.command.includes('python')) {
|
|
481
|
-
const pythonEnvWrapper = document.createElement('div');
|
|
482
|
-
pythonEnvWrapper.className = 'mt-4';
|
|
483
|
-
|
|
484
|
-
const pythonEnvLabel = document.createElement('label');
|
|
485
|
-
pythonEnvLabel.htmlFor = 'python_env';
|
|
486
|
-
pythonEnvLabel.className = 'block text-sm font-medium text-gray-700 mb-1';
|
|
487
|
-
pythonEnvLabel.textContent = 'Python Environment';
|
|
488
|
-
|
|
489
|
-
const pythonEnvInput = document.createElement('input');
|
|
490
|
-
pythonEnvInput.type = 'text';
|
|
491
|
-
pythonEnvInput.id = 'python_env';
|
|
492
|
-
pythonEnvInput.className = 'input-field';
|
|
493
|
-
pythonEnvInput.placeholder = 'Enter Python environment path (optional)';
|
|
494
|
-
|
|
495
|
-
pythonEnvWrapper.appendChild(pythonEnvLabel);
|
|
496
|
-
pythonEnvWrapper.appendChild(pythonEnvInput);
|
|
497
|
-
|
|
498
|
-
const envDescription = document.createElement('p');
|
|
499
|
-
envDescription.className = 'text-xs text-gray-500 mt-1';
|
|
500
|
-
envDescription.textContent = 'Specify the Python executable file(e.g. C:/python312/python) to use for installation. Leave empty to use system default.';
|
|
501
|
-
pythonEnvWrapper.appendChild(envDescription);
|
|
502
|
-
|
|
503
|
-
modalArguments.appendChild(pythonEnvWrapper);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
if (Object.keys(envRequirements).length === 0) {
|
|
508
|
-
const noEnvMessage = document.createElement('p');
|
|
509
|
-
noEnvMessage.className = 'text-gray-600';
|
|
510
|
-
noEnvMessage.textContent = 'No environment variables required for this MCP server.';
|
|
511
|
-
envInputsDiv.appendChild(noEnvMessage);
|
|
512
|
-
} else {
|
|
513
|
-
// Get clientMcpSettings from the targets endpoint
|
|
514
|
-
let clientSettings = targetData.clientMcpSettings;
|
|
515
|
-
|
|
516
|
-
// Create inputs for each environment variable
|
|
517
|
-
Object.keys(envRequirements).forEach(key => {
|
|
518
|
-
const req = envRequirements[key];
|
|
519
|
-
const inputId = `env_${key}`;
|
|
520
|
-
const inputWrapper = document.createElement('div');
|
|
521
|
-
inputWrapper.className = 'mb-3';
|
|
522
|
-
|
|
523
|
-
const label = document.createElement('label');
|
|
524
|
-
label.htmlFor = inputId;
|
|
525
|
-
label.className = 'block text-sm font-medium text-gray-700 mb-1';
|
|
526
|
-
label.innerHTML = `${key} ${req.Required ? '<span class="text-red-500">*</span>' : ''}`;
|
|
527
|
-
|
|
528
|
-
const input = document.createElement('input');
|
|
529
|
-
input.type = req.isSecret ? 'password' : 'text';
|
|
530
|
-
input.id = inputId;
|
|
531
|
-
input.name = key;
|
|
532
|
-
input.placeholder = req.Description || key;
|
|
533
|
-
|
|
534
|
-
// For default value, first check MSRooCode for the target server, then use the provided default
|
|
535
|
-
let defaultValue = req.Default || '';
|
|
536
|
-
|
|
537
|
-
// Check if we have settings from MSRooCode
|
|
538
|
-
if (clientSettings && clientSettings.MSRooCode &&
|
|
539
|
-
clientSettings.MSRooCode.mcpServers &&
|
|
540
|
-
clientSettings.MSRooCode.mcpServers[serverName] &&
|
|
541
|
-
clientSettings.MSRooCode.mcpServers[serverName].env &&
|
|
542
|
-
clientSettings.MSRooCode.mcpServers[serverName].env[key]) {
|
|
543
|
-
defaultValue = clientSettings.MSRooCode.mcpServers[serverName].env[key];
|
|
544
|
-
console.log(`Using MSRooCode value for ${key}: ${defaultValue}`);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
input.value = defaultValue;
|
|
548
|
-
input.required = req.Required;
|
|
549
|
-
input.className = 'input-field';
|
|
550
|
-
|
|
551
|
-
inputWrapper.appendChild(label);
|
|
552
|
-
inputWrapper.appendChild(input);
|
|
553
|
-
|
|
554
|
-
if (req.Description) {
|
|
555
|
-
const description = document.createElement('p');
|
|
556
|
-
description.className = 'text-xs text-gray-500 mt-1';
|
|
557
|
-
description.textContent = req.Description;
|
|
558
|
-
inputWrapper.appendChild(description);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
envInputsDiv.appendChild(inputWrapper);
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
// Handle server requirements section last
|
|
566
|
-
const serverRequirements = mcpServer.dependencies?.requirements || [];
|
|
567
|
-
const requirements = serverData.data.installationStatus?.requirementsStatus || {};
|
|
568
|
-
|
|
569
|
-
if (serverRequirements.length > 0) {
|
|
570
|
-
const reqHtml = serverRequirements.map(req => {
|
|
571
|
-
const status = requirements[req.name] || {};
|
|
572
|
-
let statusClass = status.installed
|
|
573
|
-
? 'text-green-600 bg-green-50'
|
|
574
|
-
: 'text-yellow-600 bg-yellow-50';
|
|
575
|
-
let statusText = status.installed ? 'Installed' : 'Required';
|
|
576
|
-
let versionDisplay = status.version ? ` • <span class="font-medium">${status.version}</span>` : '';
|
|
577
|
-
let updateToggle = '';
|
|
578
|
-
|
|
579
|
-
// Check if there's an available update
|
|
580
|
-
if (status.installed && status.availableUpdate && status.availableUpdate.version) {
|
|
581
|
-
if (status.version && compareVersions(status.availableUpdate.version, status.version) > 0) {
|
|
582
|
-
// Show version update information with yellow color and icon
|
|
583
|
-
statusClass = 'text-yellow-600 bg-yellow-50';
|
|
584
|
-
statusText = `<span style="color: #f59e0b; font-weight: bold; margin-right: 5px;">↑</span>${status.availableUpdate.version}`;
|
|
585
|
-
|
|
586
|
-
// Create a toggle switch for update
|
|
587
|
-
updateToggle = `
|
|
588
|
-
<label class="inline-flex items-center cursor-pointer ml-2">
|
|
589
|
-
<input type="checkbox" class="toggle-update sr-only"
|
|
590
|
-
data-name="${req.name}"
|
|
591
|
-
data-version="${status.availableUpdate.version}"
|
|
592
|
-
data-category="${categoryName}"
|
|
593
|
-
data-server="${serverName}">
|
|
594
|
-
<div class="relative w-10 h-5 bg-gray-200 rounded-full toggle-bg">
|
|
595
|
-
<div class="absolute inset-y-0 left-0 w-5 h-5 bg-white rounded-full transition-transform duration-300 transform"></div>
|
|
596
|
-
</div>
|
|
597
|
-
<span class="ml-2 text-sm text-gray-700">Update</span>
|
|
598
|
-
</label>
|
|
599
|
-
`;
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
return `
|
|
604
|
-
<div class="border border-gray-200 p-3 rounded-lg mb-2 hover:bg-gray-50">
|
|
605
|
-
<div class="flex justify-between items-center">
|
|
606
|
-
<div>
|
|
607
|
-
<div class="font-semibold text-gray-800">${req.name}</div>
|
|
608
|
-
<div class="text-sm text-gray-600 shadow-sm p-1 rounded bg-gray-50">
|
|
609
|
-
<span class="font-medium">${status.type || 'package'}</span>${versionDisplay}
|
|
610
|
-
</div>
|
|
611
|
-
</div>
|
|
612
|
-
<div class="flex items-center">
|
|
613
|
-
<span class="${statusClass} inline-flex items-center px-3 py-1 rounded-full text-sm">
|
|
614
|
-
${statusText}
|
|
615
|
-
</span>
|
|
616
|
-
${updateToggle}
|
|
617
|
-
</div>
|
|
618
|
-
</div>
|
|
619
|
-
</div>
|
|
620
|
-
`;
|
|
621
|
-
}).join('');
|
|
622
|
-
|
|
623
|
-
modalRequirements.innerHTML = `
|
|
624
|
-
<h3 class="text-lg font-semibold text-gray-700 mb-3">Dependencies</h3>
|
|
625
|
-
<p class="text-sm text-gray-600 mb-4">These dependencies will be automatically installed when installing the server</p>
|
|
626
|
-
${reqHtml}
|
|
627
|
-
`;
|
|
628
|
-
} else {
|
|
629
|
-
modalRequirements.innerHTML = '<p class="text-gray-600">No additional dependencies required.</p>';
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// Add event listeners for update toggles
|
|
633
|
-
setTimeout(() => {
|
|
634
|
-
const updateToggles = modalRequirements.querySelectorAll('.toggle-update');
|
|
635
|
-
updateToggles.forEach(toggle => {
|
|
636
|
-
toggle.addEventListener('change', function () {
|
|
637
|
-
// When toggled, update the visual appearance
|
|
638
|
-
const toggleBg = this.parentElement.querySelector('.toggle-bg');
|
|
639
|
-
if (this.checked) {
|
|
640
|
-
toggleBg.classList.add('bg-blue-500');
|
|
641
|
-
toggleBg.querySelector('div').classList.add('translate-x-5');
|
|
642
|
-
} else {
|
|
643
|
-
toggleBg.classList.remove('bg-blue-500');
|
|
644
|
-
toggleBg.querySelector('div').classList.remove('translate-x-5');
|
|
645
|
-
}
|
|
646
|
-
});
|
|
647
|
-
});
|
|
648
|
-
}, 100);
|
|
649
|
-
|
|
650
|
-
// Set up the install form submit handler
|
|
651
|
-
const installForm = document.getElementById('installForm');
|
|
652
|
-
installForm.onsubmit = (e) => {
|
|
653
|
-
e.preventDefault();
|
|
654
|
-
|
|
655
|
-
// Get all environment variables and arguments
|
|
656
|
-
const envVars = {};
|
|
657
|
-
const inputs = envInputsDiv.querySelectorAll('input');
|
|
658
|
-
inputs.forEach(input => {
|
|
659
|
-
if (input.name && input.value) {
|
|
660
|
-
envVars[input.name] = input.value;
|
|
661
|
-
}
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
// Get installation arguments from individual input fields
|
|
665
|
-
const argInputs = modalArguments.querySelectorAll('.arg-input');
|
|
666
|
-
const args = Array.from(argInputs)
|
|
667
|
-
.map(input => input.value.trim())
|
|
668
|
-
.filter(val => val !== '');
|
|
669
|
-
|
|
670
|
-
// Get Python environment if available
|
|
671
|
-
const pythonEnvInput = document.getElementById('python_env');
|
|
672
|
-
const pythonEnv = pythonEnvInput?.value?.trim();
|
|
673
|
-
|
|
674
|
-
// Check for enabled update toggles and collect requirements to update
|
|
675
|
-
const requirementsToUpdate = [];
|
|
676
|
-
const updateToggles = modalRequirements.querySelectorAll('.toggle-update:checked');
|
|
677
|
-
updateToggles.forEach(toggle => {
|
|
678
|
-
requirementsToUpdate.push({
|
|
679
|
-
name: toggle.dataset.name,
|
|
680
|
-
version: toggle.dataset.version
|
|
681
|
-
});
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
// Check if we have any requirements selected for update
|
|
685
|
-
const hasRequirementsToUpdate = requirementsToUpdate.length > 0;
|
|
686
|
-
|
|
687
|
-
// Only proceed if this isn't an uninstall operation
|
|
688
|
-
const uninstallBtn = document.querySelector('.uninstall-btn');
|
|
689
|
-
if (!uninstallBtn || !uninstallBtn.matches(':active')) {
|
|
690
|
-
// Get selected clients
|
|
691
|
-
const selectedTargets = window.selectedClients.length > 0 ?
|
|
692
|
-
window.selectedClients :
|
|
693
|
-
Array.from(document.querySelectorAll('.client-item.selected'))
|
|
694
|
-
.map(item => item.dataset.target);
|
|
695
|
-
|
|
696
|
-
console.log('Selected targets:', selectedTargets);
|
|
697
|
-
console.log('Requirements to update:', requirementsToUpdate);
|
|
698
|
-
if (selectedTargets.length === 0 && !hasRequirementsToUpdate) {
|
|
699
|
-
showToast('Please select at least one client to configure.', 'error');
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
window.selectedClients = selectedTargets;
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
// Call install function with selected targets
|
|
706
|
-
// Find installing message for the first selected target
|
|
707
|
-
let installingMessage = "Starting installation...";
|
|
708
|
-
const serverStatus = serverStatuses[serverName] || { installedStatus: {} };
|
|
709
|
-
if (window.selectedClients.length > 0) {
|
|
710
|
-
const target = window.selectedClients[0];
|
|
711
|
-
const msg = serverStatus.installedStatus?.[target]?.message;
|
|
712
|
-
if (msg) installingMessage = msg;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
// Add requirements to update to serverInstallOptions if any
|
|
716
|
-
const serverInstallOptions = {
|
|
717
|
-
targetClients: window.selectedClients,
|
|
718
|
-
env: envVars,
|
|
719
|
-
args: args,
|
|
720
|
-
settings: pythonEnv ? { pythonEnv } : undefined
|
|
721
|
-
};
|
|
722
|
-
|
|
723
|
-
// Only add requirements if we have any to update
|
|
724
|
-
if (requirementsToUpdate.length > 0) {
|
|
725
|
-
serverInstallOptions.requirements = requirementsToUpdate;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
// For installation, use the selectedTargets from the validation above
|
|
729
|
-
const targetsToUse = document.querySelector('.uninstall-btn:hover') ? [] : window.selectedClients;
|
|
730
|
-
handleBulkClientInstall(categoryName, serverName, targetsToUse, envVars, installingMessage, serverData, serverInstallOptions);
|
|
731
|
-
};
|
|
732
|
-
|
|
733
|
-
} catch (error) {
|
|
734
|
-
console.error("Error loading data:", error);
|
|
735
|
-
targetDiv.innerHTML = `<p class="text-red-500">Error: ${error.message}</p>`;
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
modal.style.display = "block";
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
// Function to handle bulk client installations
|
|
742
|
-
async function handleBulkClientInstall(categoryName, serverName, targets, envVars = {}, installingMessage = "Starting installation...", serverData = null, serverInstallOptions = null) {
|
|
743
|
-
console.log('[LoadingModal] handleBulkClientInstall called', { categoryName, serverName, targets, envVars, serverInstallOptions });
|
|
744
|
-
// Hide install modal, show loading modal
|
|
745
|
-
const installModal = document.getElementById('installModal');
|
|
746
|
-
console.log('[LoadingModal] installModal:', installModal);
|
|
747
|
-
if (installModal) installModal.style.display = "none";
|
|
748
|
-
|
|
749
|
-
// If serverData is provided, extract the installing message from it (latest status)
|
|
750
|
-
if (serverData && serverData.data && serverData.data.installationStatus && serverData.data.installationStatus.serversStatus) {
|
|
751
|
-
const serverStatuses = serverData.data.installationStatus.serversStatus;
|
|
752
|
-
const serverStatus = serverStatuses[serverName] || { installedStatus: {} };
|
|
753
|
-
if (targets && targets.length > 0) {
|
|
754
|
-
const target = targets[0];
|
|
755
|
-
const msg = serverStatus.installedStatus?.[target]?.message;
|
|
756
|
-
if (msg) installingMessage = msg;
|
|
757
|
-
}
|
|
758
|
-
// Append the installing message for user visibility
|
|
759
|
-
if (installingMessage && installingMessage !== "Starting installation...") {
|
|
760
|
-
delayedAppendInstallLoadingMessage(installingMessage);
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
showInstallLoadingModal();
|
|
765
|
-
delayedAppendInstallLoadingMessage(installingMessage);
|
|
766
|
-
|
|
767
|
-
try {
|
|
768
|
-
delayedAppendInstallLoadingMessage("Installing, please wait...");
|
|
769
|
-
|
|
770
|
-
// Use serverInstallOptions if provided, otherwise build the traditional options
|
|
771
|
-
const requestBody = {
|
|
772
|
-
serverList: {
|
|
773
|
-
[serverName]: serverInstallOptions || {
|
|
774
|
-
targetClients: targets,
|
|
775
|
-
env: envVars
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
};
|
|
779
|
-
|
|
780
|
-
const response = await fetch(`/api/categories/${categoryName}/install`, {
|
|
781
|
-
method: 'POST',
|
|
782
|
-
headers: {
|
|
783
|
-
'Content-Type': 'application/json',
|
|
784
|
-
'Accept': 'application/json'
|
|
785
|
-
},
|
|
786
|
-
body: JSON.stringify(requestBody)
|
|
787
|
-
});
|
|
788
|
-
|
|
789
|
-
console.log('[LoadingModal] fetch install response:', response);
|
|
790
|
-
|
|
791
|
-
if (!response.ok) {
|
|
792
|
-
const errorData = await response.text();
|
|
793
|
-
delayedAppendInstallLoadingMessage(`<span style="color:#f59e0b;">Failed: ${errorData || response.statusText}</span>`);
|
|
794
|
-
console.error('[LoadingModal] Failed:', errorData || response.statusText);
|
|
795
|
-
throw new Error(`Installation failed: ${errorData || response.statusText}`);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
const result = await response.json();
|
|
799
|
-
console.log('[LoadingModal] install result:', result);
|
|
800
|
-
if (!result.success) {
|
|
801
|
-
delayedAppendInstallLoadingMessage(`<span style="color:#f59e0b;">${result.error || 'Operation failed'}</span>`);
|
|
802
|
-
console.error('[LoadingModal] Error:', result.error || 'Operation failed');
|
|
803
|
-
throw new Error(result.error || 'Installation failed');
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
// Optionally, you can refresh modal data here or trigger a callback
|
|
807
|
-
// Start polling for install status, pass requirements if available
|
|
808
|
-
const requirements = serverInstallOptions?.requirements || [];
|
|
809
|
-
pollInstallStatus(categoryName, serverName, targets, 2000, requirements);
|
|
810
|
-
} catch (error) {
|
|
811
|
-
console.error('[LoadingModal] Error applying configuration:', error);
|
|
812
|
-
delayedAppendInstallLoadingMessage(`<span style="color:red;">Error: ${error.message}</span>`);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
// Poll install status for the given server/targets and optional requirements
|
|
817
|
-
async function pollInstallStatus(categoryName, serverName, targets, interval = 2000, requirements = []) {
|
|
818
|
-
let lastMessages = {};
|
|
819
|
-
let lastRequirementMessages = {};
|
|
820
|
-
let lastReqirementErrorMessages = {};
|
|
821
|
-
let completionMessageSent = false;
|
|
822
|
-
|
|
823
|
-
const startTime = Date.now();
|
|
824
|
-
const maxTimeout = 10 * 60 * 1000; // 10 minutes in milliseconds
|
|
825
|
-
|
|
826
|
-
while (Date.now() - startTime < maxTimeout) {
|
|
827
|
-
try {
|
|
828
|
-
const resp = await fetch(`/api/categories/${categoryName}`);
|
|
829
|
-
if (resp.ok) {
|
|
830
|
-
const data = await resp.json();
|
|
831
|
-
// If requirements is empty and we have targets, get requirements from server's dependencies
|
|
832
|
-
if (requirements.length === 0 && targets?.length > 0) {
|
|
833
|
-
const feedConfig = data?.data?.feedConfiguration;
|
|
834
|
-
if (feedConfig?.mcpServers) {
|
|
835
|
-
const server = feedConfig.mcpServers.find(s => s.name === serverName);
|
|
836
|
-
if (server?.dependencies?.requirements) {
|
|
837
|
-
requirements = server.dependencies.requirements;
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
const installationStatus = data?.data?.installationStatus || {};
|
|
842
|
-
const serverStatuses = installationStatus.serversStatus || {};
|
|
843
|
-
const requirementsStatus = installationStatus.requirementsStatus || {};
|
|
844
|
-
const serverStatus = serverStatuses[serverName] || { installedStatus: {} };
|
|
845
|
-
|
|
846
|
-
// First check requirements status if we have any
|
|
847
|
-
let allRequirementsCompleted = true;
|
|
848
|
-
let hasRequirements = requirements.length > 0;
|
|
849
|
-
|
|
850
|
-
for (const req of requirements) {
|
|
851
|
-
const reqStatus = requirementsStatus[req.name] || {};
|
|
852
|
-
if (reqStatus.installed === true && !reqStatus.operationStatus) {
|
|
853
|
-
const msg = `Requirement [${req.name}] already installed.`;
|
|
854
|
-
if (msg && lastRequirementMessages[req.name] !== msg) {
|
|
855
|
-
delayedAppendInstallLoadingMessage(msg);
|
|
856
|
-
lastRequirementMessages[req.name] = msg;
|
|
857
|
-
}
|
|
858
|
-
continue;
|
|
859
|
-
}
|
|
860
|
-
const opStatus = reqStatus.operationStatus || {};
|
|
861
|
-
const msg = opStatus.message;
|
|
862
|
-
const status = opStatus.status;
|
|
863
|
-
|
|
864
|
-
// Only append new messages for requirements
|
|
865
|
-
if (msg && lastRequirementMessages[req.name] !== msg) {
|
|
866
|
-
delayedAppendInstallLoadingMessage(`${req.name}: ${msg}`);
|
|
867
|
-
lastRequirementMessages[req.name] = msg;
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
if (status !== "completed" && status !== "failed") {
|
|
871
|
-
allRequirementsCompleted = false;
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
// If a requirement failed, show an error
|
|
875
|
-
if (status === "failed") {
|
|
876
|
-
if (lastReqirementErrorMessages[req.name] !== reqStatus.error) {
|
|
877
|
-
delayedAppendInstallLoadingMessage(`${req.name} failed: ${reqStatus.error}`);
|
|
878
|
-
lastReqirementErrorMessages[req.name] = reqStatus.error;
|
|
879
|
-
}
|
|
880
|
-
if (lastRequirementMessages[req.name] !== msg) {
|
|
881
|
-
delayedAppendInstallLoadingMessage(`<span style="color:#f59e0b;">${req.name}: Update failed - ${msg || 'Unknown error'}</span>`);
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
// Now check target statuses
|
|
887
|
-
let allTargetsCompleted = true;
|
|
888
|
-
let hasTargets = targets && targets.length > 0;
|
|
889
|
-
|
|
890
|
-
if (hasTargets) {
|
|
891
|
-
for (const target of targets) {
|
|
892
|
-
const status = serverStatus.installedStatus?.[target]?.status;
|
|
893
|
-
const msg = serverStatus.installedStatus?.[target]?.message;
|
|
894
|
-
// Only append new messages for targets, making output more compact
|
|
895
|
-
if (msg && lastMessages[target] !== msg) {
|
|
896
|
-
delayedAppendInstallLoadingMessage(`${target}: ${msg}`);
|
|
897
|
-
lastMessages[target] = msg;
|
|
898
|
-
}
|
|
899
|
-
if (status !== "completed") {
|
|
900
|
-
allTargetsCompleted = false;
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
// Complete if all operations are done
|
|
906
|
-
const allCompleted = (!hasRequirements || allRequirementsCompleted) &&
|
|
907
|
-
(!hasTargets || allTargetsCompleted);
|
|
908
|
-
|
|
909
|
-
if (allCompleted && !completionMessageSent) {
|
|
910
|
-
completionMessageSent = true;
|
|
911
|
-
|
|
912
|
-
// Compose completion message
|
|
913
|
-
const completionMessage = hasRequirements && hasTargets ?
|
|
914
|
-
`Updated requirements and configured ${targets.length} client(s)` :
|
|
915
|
-
hasRequirements ? 'Requirements updated' :
|
|
916
|
-
hasTargets ? `Configured ${targets.length} client(s)` :
|
|
917
|
-
'Operation completed';
|
|
918
|
-
|
|
919
|
-
console.log('[LoadingModal] Completed:', completionMessage);
|
|
920
|
-
delayedAppendInstallLoadingMessage(`<span style="color:green;">${completionMessage}</span>`, true);
|
|
921
|
-
|
|
922
|
-
// Give time for message queue to process before returning
|
|
923
|
-
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
924
|
-
return;
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
} catch (error) {
|
|
928
|
-
console.error('[LoadingModal] Error polling status:', error);
|
|
929
|
-
}
|
|
930
|
-
await new Promise(resolve => setTimeout(resolve, interval)); // 2 second interval
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
// If we reach here, we've timed out
|
|
934
|
-
console.error('[LoadingModal] Operation timed out after 10 minutes');
|
|
935
|
-
|
|
936
|
-
// On timeout, show timeout message but don't auto-close
|
|
937
|
-
if (!completionMessageSent) {
|
|
938
|
-
delayedAppendInstallLoadingMessage(`<span style="color:#f59e0b;">Operation timed out - Please refresh the page</span>`, true);
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
// Function to handle client uninstallation for multiple targets
|
|
943
|
-
async function uninstallTools(categoryName, serverList, targets) {
|
|
944
|
-
// Get selected targets from window.selectedClients or the provided targets
|
|
945
|
-
const selectedTargets = window.selectedClients || (Array.isArray(targets) ? targets : [targets]);
|
|
946
|
-
|
|
947
|
-
// Validate selected targets
|
|
948
|
-
if (!selectedTargets || selectedTargets.length === 0) {
|
|
949
|
-
showToast('Please select at least one client to uninstall.', 'error');
|
|
950
|
-
return;
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
try {
|
|
954
|
-
delayedAppendInstallLoadingMessage('Starting uninstallation...');
|
|
955
|
-
|
|
956
|
-
// Ensure serverList is an object where each key is a server name
|
|
957
|
-
if (Array.isArray(serverList)) {
|
|
958
|
-
const formattedServerList = {};
|
|
959
|
-
serverList.forEach(server => {
|
|
960
|
-
formattedServerList[server] = { removeData: true };
|
|
961
|
-
});
|
|
962
|
-
serverList = formattedServerList;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
const response = await fetch(`/api/categories/${categoryName}/uninstall`, {
|
|
966
|
-
method: 'POST',
|
|
967
|
-
headers: { 'Content-Type': 'application/json' },
|
|
968
|
-
body: JSON.stringify({
|
|
969
|
-
serverList: serverList,
|
|
970
|
-
options: {
|
|
971
|
-
targets: selectedTargets,
|
|
972
|
-
removeData: true
|
|
973
|
-
}
|
|
974
|
-
})
|
|
975
|
-
});
|
|
976
|
-
|
|
977
|
-
if (!response.ok) {
|
|
978
|
-
const errorData = await response.text();
|
|
979
|
-
throw new Error(`Uninstallation failed: ${errorData || response.statusText}`);
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
const result = await response.json();
|
|
983
|
-
if (!result.success) {
|
|
984
|
-
throw new Error(result.error || 'Uninstallation failed');
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
// Add completion message and trigger completion UI
|
|
988
|
-
delayedAppendInstallLoadingMessage(`Successfully uninstalled from ${selectedTargets.join(', ')}`, true);
|
|
989
|
-
|
|
990
|
-
// Clear selected clients after successful uninstall
|
|
991
|
-
window.selectedClients = [];
|
|
992
|
-
} catch (error) {
|
|
993
|
-
console.error('Error uninstalling tools:', error);
|
|
994
|
-
delayedAppendInstallLoadingMessage(`Error: ${error.message}`, true);
|
|
995
|
-
showToast(`Error uninstalling tools: ${error.message}`, 'error');
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
// Close modal and ensure selected category persists
|
|
1000
|
-
function closeModal() {
|
|
1001
|
-
document.getElementById('installModal').style.display = "none";
|
|
1002
|
-
|
|
1003
|
-
// Get the last selected category from localStorage
|
|
1004
|
-
const lastSelected = localStorage.getItem('lastSelectedCategory');
|
|
1005
|
-
|
|
1006
|
-
// Refresh page and restore selection
|
|
1007
|
-
if (lastSelected) {
|
|
1008
|
-
setTimeout(() => {
|
|
1009
|
-
window.location.href = window.location.pathname + '?category=' + encodeURIComponent(lastSelected);
|
|
1010
|
-
}, 100);
|
|
1011
|
-
} else {
|
|
1012
|
-
location.reload();
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
// Close modal if clicked outside content
|
|
1017
|
-
function setupModalOutsideClick() {
|
|
1018
|
-
window.onclick = function (event) {
|
|
1019
|
-
const installModal = document.getElementById('installModal');
|
|
1020
|
-
if (event.target == installModal) {
|
|
1021
|
-
closeModal();
|
|
1022
|
-
}
|
|
1023
|
-
};
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1
|
+
// Import all functionality from the modal folder
|
|
2
|
+
import {
|
|
3
|
+
showInstallModal,
|
|
4
|
+
closeModal,
|
|
5
|
+
setupModalOutsideClick,
|
|
6
|
+
uninstallTools,
|
|
7
|
+
showInstallLoadingModal,
|
|
8
|
+
appendInstallLoadingMessage,
|
|
9
|
+
hideInstallLoadingModal
|
|
10
|
+
} from './modal/index.js';
|
|
11
|
+
|
|
12
|
+
// Re-export all modal functionality
|
|
13
|
+
export {
|
|
14
|
+
showInstallModal,
|
|
15
|
+
closeModal,
|
|
16
|
+
setupModalOutsideClick,
|
|
17
|
+
uninstallTools
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Make certain functions available globally
|
|
21
|
+
window.showInstallModal = showInstallModal;
|
|
1026
22
|
window.showInstallLoadingModal = showInstallLoadingModal;
|
|
1027
23
|
window.appendInstallLoadingMessage = appendInstallLoadingMessage;
|
|
1028
24
|
window.hideInstallLoadingModal = hideInstallLoadingModal;
|
|
1029
25
|
|
|
1030
|
-
//
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
background-color: #3b82f6;
|
|
1035
|
-
}
|
|
1036
|
-
.toggle-bg {
|
|
1037
|
-
transition: background-color 0.3s;
|
|
1038
|
-
}
|
|
1039
|
-
.toggle-bg div {
|
|
1040
|
-
transition: transform 0.3s;
|
|
1041
|
-
}
|
|
1042
|
-
`;
|
|
1043
|
-
document.head.appendChild(styleElement);
|
|
1044
|
-
|
|
1045
|
-
export { showInstallModal, closeModal, setupModalOutsideClick, uninstallTools };
|
|
26
|
+
// Initialize modal functionality when DOM is loaded
|
|
27
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
28
|
+
setupModalOutsideClick();
|
|
29
|
+
});
|