imcp 0.1.1 → 0.1.2
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/cli/index.js +1 -45
- package/dist/core/installers/clients/BaseClientInstaller.d.ts +1 -5
- package/dist/core/installers/clients/BaseClientInstaller.js +40 -38
- package/dist/core/installers/clients/ClientInstaller.d.ts +9 -9
- package/dist/core/installers/clients/ClientInstaller.js +105 -99
- package/dist/core/installers/requirements/BaseInstaller.d.ts +9 -1
- package/dist/core/installers/requirements/CommandInstaller.d.ts +9 -1
- package/dist/core/installers/requirements/CommandInstaller.js +46 -12
- package/dist/core/installers/requirements/GeneralInstaller.d.ts +11 -1
- package/dist/core/installers/requirements/GeneralInstaller.js +46 -10
- package/dist/core/installers/requirements/InstallerFactory.d.ts +3 -1
- package/dist/core/installers/requirements/InstallerFactory.js +3 -2
- package/dist/core/installers/requirements/NpmInstaller.d.ts +4 -2
- package/dist/core/installers/requirements/NpmInstaller.js +38 -22
- package/dist/core/installers/requirements/PipInstaller.d.ts +3 -1
- package/dist/core/installers/requirements/PipInstaller.js +58 -36
- package/dist/core/installers/requirements/RequirementInstaller.d.ts +4 -1
- package/dist/core/loaders/InstallOperationManager.d.ts +115 -0
- package/dist/core/loaders/InstallOperationManager.js +311 -0
- package/dist/core/loaders/SystemSettingsManager.d.ts +54 -0
- package/dist/core/loaders/SystemSettingsManager.js +257 -0
- package/dist/core/metadatas/recordingConstants.d.ts +44 -0
- package/dist/core/metadatas/recordingConstants.js +45 -0
- package/dist/core/metadatas/types.d.ts +21 -0
- package/dist/core/onboard/InstallOperationManager.d.ts +23 -0
- package/dist/core/onboard/InstallOperationManager.js +144 -0
- package/dist/core/onboard/OnboardStatusManager.js +2 -1
- package/dist/core/validators/StdioServerValidator.js +4 -3
- package/dist/services/InstallationService.d.ts +2 -37
- package/dist/services/InstallationService.js +45 -313
- package/dist/services/MCPManager.d.ts +1 -1
- package/dist/services/MCPManager.js +4 -58
- package/dist/services/RequirementService.d.ts +85 -12
- package/dist/services/RequirementService.js +488 -49
- package/dist/services/ServerService.d.ts +0 -6
- package/dist/services/ServerService.js +0 -74
- package/dist/utils/adoUtils.js +6 -3
- package/dist/utils/logger.js +1 -1
- package/dist/utils/macroExpressionUtils.js +3 -25
- package/dist/utils/osUtils.d.ts +22 -1
- package/dist/utils/osUtils.js +92 -1
- package/dist/utils/versionUtils.d.ts +20 -1
- package/dist/utils/versionUtils.js +51 -4
- package/dist/web/public/css/modal.css +292 -1
- package/dist/web/public/css/serverDetails.css +14 -1
- package/dist/web/public/index.html +122 -20
- package/dist/web/public/js/flights/flights.js +1 -0
- package/dist/web/public/js/modal/index.js +8 -14
- package/dist/web/public/js/modal/installModal.js +3 -4
- package/dist/web/public/js/modal/installation.js +122 -137
- package/dist/web/public/js/modal/loadingModal.js +155 -25
- package/dist/web/public/js/modal/messageQueue.js +45 -101
- package/dist/web/public/js/modal/modalSetup.js +125 -43
- package/dist/web/public/js/modal/modalUtils.js +0 -12
- package/dist/web/public/js/modal.js +23 -10
- package/dist/web/public/js/onboard/publishHandler.js +22 -20
- package/dist/web/public/js/serverCategoryDetails.js +60 -11
- package/dist/web/public/js/serverCategoryList.js +2 -2
- package/dist/web/public/js/settings.js +314 -0
- package/dist/web/public/settings.html +135 -0
- package/dist/web/public/styles.css +32 -0
- package/dist/web/server.js +82 -0
- package/memory-bank/activeContext.md +13 -1
- package/memory-bank/decisionLog.md +63 -0
- package/memory-bank/progress.md +30 -0
- package/memory-bank/systemPatterns.md +7 -0
- package/package.json +1 -1
- package/src/cli/index.ts +1 -48
- package/src/core/installers/clients/BaseClientInstaller.ts +64 -50
- package/src/core/installers/clients/ClientInstaller.ts +130 -130
- package/src/core/installers/requirements/BaseInstaller.ts +9 -1
- package/src/core/installers/requirements/CommandInstaller.ts +47 -13
- package/src/core/installers/requirements/GeneralInstaller.ts +48 -10
- package/src/core/installers/requirements/InstallerFactory.ts +4 -3
- package/src/core/installers/requirements/NpmInstaller.ts +90 -68
- package/src/core/installers/requirements/PipInstaller.ts +81 -55
- package/src/core/installers/requirements/RequirementInstaller.ts +4 -3
- package/src/core/loaders/InstallOperationManager.ts +367 -0
- package/src/core/loaders/SystemSettingsManager.ts +278 -0
- package/src/core/metadatas/recordingConstants.ts +62 -0
- package/src/core/metadatas/types.ts +23 -0
- package/src/core/onboard/OnboardStatusManager.ts +2 -1
- package/src/core/validators/StdioServerValidator.ts +4 -3
- package/src/services/InstallationService.ts +54 -399
- package/src/services/MCPManager.ts +4 -77
- package/src/services/RequirementService.ts +564 -67
- package/src/services/ServerService.ts +0 -90
- package/src/utils/adoUtils.ts +6 -4
- package/src/utils/logger.ts +1 -1
- package/src/utils/macroExpressionUtils.ts +4 -21
- package/src/utils/osUtils.ts +92 -1
- package/src/utils/versionUtils.ts +71 -19
- package/src/web/public/css/modal.css +292 -1
- package/src/web/public/css/serverDetails.css +14 -1
- package/src/web/public/index.html +122 -20
- package/src/web/public/js/flights/flights.js +1 -1
- package/src/web/public/js/modal/index.js +8 -14
- package/src/web/public/js/modal/installModal.js +3 -4
- package/src/web/public/js/modal/installation.js +122 -137
- package/src/web/public/js/modal/loadingModal.js +155 -25
- package/src/web/public/js/modal/modalSetup.js +125 -43
- package/src/web/public/js/modal/modalUtils.js +0 -12
- package/src/web/public/js/modal.js +23 -10
- package/src/web/public/js/onboard/publishHandler.js +22 -20
- package/src/web/public/js/serverCategoryDetails.js +60 -11
- package/src/web/public/js/serverCategoryList.js +2 -2
- package/src/web/public/js/settings.js +314 -0
- package/src/web/public/settings.html +135 -0
- package/src/web/public/styles.css +32 -0
- package/src/web/server.ts +85 -0
- package/src/web/public/js/modal/messageQueue.js +0 -112
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { delayedAppendInstallLoadingMessage } from './messageQueue.js';
|
|
2
|
-
import { showInstallLoadingModal } from './loadingModal.js';
|
|
1
|
+
// import { delayedAppendInstallLoadingMessage } from './messageQueue.js'; // No longer used directly for steps
|
|
2
|
+
import { showInstallLoadingModal, updateOverallInstallStatus, addInstallationStep } from './loadingModal.js';
|
|
3
3
|
import { showToast } from '../notifications.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -19,6 +19,14 @@ export async function handleBulkClientInstall(categoryName, serverName, targets,
|
|
|
19
19
|
console.log('[LoadingModal] installModal:', installModal);
|
|
20
20
|
if (installModal) installModal.style.display = "none";
|
|
21
21
|
|
|
22
|
+
// Store category and server name for potential refresh
|
|
23
|
+
if (categoryName) {
|
|
24
|
+
localStorage.setItem('lastSelectedCategory', categoryName);
|
|
25
|
+
}
|
|
26
|
+
if (serverName) {
|
|
27
|
+
localStorage.setItem('lastSelectedServerName', serverName);
|
|
28
|
+
}
|
|
29
|
+
|
|
22
30
|
// If serverData is provided, extract the installing message from it (latest status)
|
|
23
31
|
if (serverData?.data?.installationStatus?.serversStatus) {
|
|
24
32
|
const serverStatuses = serverData.data.installationStatus.serversStatus;
|
|
@@ -34,11 +42,11 @@ export async function handleBulkClientInstall(categoryName, serverName, targets,
|
|
|
34
42
|
}
|
|
35
43
|
}
|
|
36
44
|
|
|
37
|
-
showInstallLoadingModal();
|
|
38
|
-
delayedAppendInstallLoadingMessage(installingMessage);
|
|
45
|
+
showInstallLoadingModal(`Installing ${serverName}`); // Pass initial server name
|
|
46
|
+
// delayedAppendInstallLoadingMessage(installingMessage); // Old way
|
|
39
47
|
|
|
40
48
|
try {
|
|
41
|
-
delayedAppendInstallLoadingMessage("Installing, please wait...");
|
|
49
|
+
// delayedAppendInstallLoadingMessage("Installing, please wait..."); // Old way
|
|
42
50
|
|
|
43
51
|
// Use serverInstallOptions if provided, otherwise build the traditional options
|
|
44
52
|
const requestBody = {
|
|
@@ -63,7 +71,8 @@ export async function handleBulkClientInstall(categoryName, serverName, targets,
|
|
|
63
71
|
|
|
64
72
|
if (!response.ok) {
|
|
65
73
|
const errorData = await response.text();
|
|
66
|
-
delayedAppendInstallLoadingMessage(`<span style="color:#f59e0b;">Failed: ${errorData || response.statusText}</span>`);
|
|
74
|
+
// delayedAppendInstallLoadingMessage(`<span style="color:#f59e0b;">Failed: ${errorData || response.statusText}</span>`); // Old way
|
|
75
|
+
updateOverallInstallStatus('failed', `Installation request failed: ${errorData || response.statusText}`);
|
|
67
76
|
console.error('[LoadingModal] Failed:', errorData || response.statusText);
|
|
68
77
|
throw new Error(`Installation failed: ${errorData || response.statusText}`);
|
|
69
78
|
}
|
|
@@ -71,150 +80,86 @@ export async function handleBulkClientInstall(categoryName, serverName, targets,
|
|
|
71
80
|
const result = await response.json();
|
|
72
81
|
console.log('[LoadingModal] install result:', result);
|
|
73
82
|
if (!result.success) {
|
|
74
|
-
delayedAppendInstallLoadingMessage(`<span style="color:#f59e0b;">${result.error || 'Operation failed'}</span>`);
|
|
83
|
+
// delayedAppendInstallLoadingMessage(`<span style="color:#f59e0b;">${result.error || 'Operation failed'}</span>`); // Old way
|
|
84
|
+
updateOverallInstallStatus('failed', result.error || 'Installation operation failed to start.');
|
|
75
85
|
console.error('[LoadingModal] Error:', result.error || 'Operation failed');
|
|
76
86
|
throw new Error(result.error || 'Installation failed');
|
|
77
87
|
}
|
|
78
88
|
|
|
79
|
-
// Start polling for install status
|
|
80
|
-
|
|
81
|
-
pollInstallStatus(categoryName, serverName, targets, 2000, requirements);
|
|
89
|
+
// Start polling for install status
|
|
90
|
+
pollNewInstallStatus(categoryName, serverName, 2000); // New polling
|
|
82
91
|
} catch (error) {
|
|
83
92
|
console.error('[LoadingModal] Error applying configuration:', error);
|
|
84
|
-
delayedAppendInstallLoadingMessage(`<span style="color:red;">Error: ${error.message}</span>`);
|
|
93
|
+
// delayedAppendInstallLoadingMessage(`<span style="color:red;">Error: ${error.message}</span>`); // Old way
|
|
94
|
+
updateOverallInstallStatus('failed', `Error: ${error.message}`);
|
|
85
95
|
}
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
/**
|
|
89
|
-
* Poll install status
|
|
99
|
+
* Poll install status using the new API endpoint for detailed steps.
|
|
90
100
|
* @param {string} categoryName - The category name
|
|
91
101
|
* @param {string} serverName - The server name
|
|
92
|
-
* @param {string[]} targets - Target clients
|
|
93
102
|
* @param {number} interval - Polling interval in milliseconds
|
|
94
|
-
* @param {Array} requirements - Requirements to check
|
|
95
103
|
*/
|
|
96
|
-
async function
|
|
97
|
-
let lastMessages = {};
|
|
98
|
-
let lastRequirementMessages = {};
|
|
99
|
-
let lastReqirementErrorMessages = {};
|
|
104
|
+
async function pollNewInstallStatus(categoryName, serverName, interval = 2000) {
|
|
100
105
|
let completionMessageSent = false;
|
|
101
|
-
|
|
102
106
|
const startTime = Date.now();
|
|
103
|
-
const maxTimeout = 10 * 60 * 1000; // 10 minutes
|
|
107
|
+
const maxTimeout = 10 * 60 * 1000; // 10 minutes
|
|
104
108
|
|
|
105
|
-
while (Date.now() - startTime < maxTimeout) {
|
|
109
|
+
while (Date.now() - startTime < maxTimeout && !completionMessageSent) {
|
|
106
110
|
try {
|
|
107
|
-
const resp = await fetch(`/api/categories/${categoryName}`);
|
|
111
|
+
const resp = await fetch(`/api/categories/${categoryName}/servers/${serverName}/installation/status`);
|
|
108
112
|
if (resp.ok) {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (reqStatus.installed === true && !reqStatus.operationStatus) {
|
|
132
|
-
// const msg = `Requirement [${req.name}] already installed.`;
|
|
133
|
-
// if (msg && lastRequirementMessages[req.name] !== msg) {
|
|
134
|
-
// delayedAppendInstallLoadingMessage(msg);
|
|
135
|
-
// lastRequirementMessages[req.name] = msg;
|
|
136
|
-
// }
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
const opStatus = reqStatus.operationStatus || {};
|
|
140
|
-
const msg = opStatus.message;
|
|
141
|
-
const status = opStatus.status;
|
|
142
|
-
|
|
143
|
-
// Only append new messages for requirements
|
|
144
|
-
if (msg && lastRequirementMessages[req.name] !== msg) {
|
|
145
|
-
delayedAppendInstallLoadingMessage(`${req.name}: ${msg}`);
|
|
146
|
-
lastRequirementMessages[req.name] = msg;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (status !== "completed" && status !== "failed") {
|
|
150
|
-
allRequirementsCompleted = false;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// If a requirement failed, show an error
|
|
154
|
-
if (status === "failed") {
|
|
155
|
-
if (lastReqirementErrorMessages[req.name] !== reqStatus.error) {
|
|
156
|
-
delayedAppendInstallLoadingMessage(`${req.name} failed: ${reqStatus.error}`);
|
|
157
|
-
lastReqirementErrorMessages[req.name] = reqStatus.error;
|
|
158
|
-
}
|
|
159
|
-
if (lastRequirementMessages[req.name] !== msg) {
|
|
160
|
-
delayedAppendInstallLoadingMessage(`<span style="color:#f59e0b;">${req.name}: Update failed - ${msg || 'Unknown error'}</span>`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Now check target statuses
|
|
166
|
-
let allTargetsCompleted = true;
|
|
167
|
-
let hasTargets = targets && targets.length > 0;
|
|
168
|
-
|
|
169
|
-
if (hasTargets) {
|
|
170
|
-
for (const target of targets) {
|
|
171
|
-
const status = serverStatus.installedStatus?.[target]?.status;
|
|
172
|
-
const msg = serverStatus.installedStatus?.[target]?.message;
|
|
173
|
-
// Only append new messages for targets, making output more compact
|
|
174
|
-
if (msg && lastMessages[target] !== msg) {
|
|
175
|
-
delayedAppendInstallLoadingMessage(`${target}: ${msg}`);
|
|
176
|
-
lastMessages[target] = msg;
|
|
177
|
-
}
|
|
178
|
-
if (status !== "completed" && status !== "failed") {
|
|
179
|
-
allTargetsCompleted = false;
|
|
180
|
-
}
|
|
113
|
+
const result = await resp.json();
|
|
114
|
+
if (result.success && result.data) {
|
|
115
|
+
const { steps, overallStatus, error } = result.data;
|
|
116
|
+
|
|
117
|
+
updateOverallInstallStatus(overallStatus, error || `Installation ${overallStatus}`);
|
|
118
|
+
|
|
119
|
+
// Always update or insert each step, so UI stays in sync with backend
|
|
120
|
+
steps.forEach(step => {
|
|
121
|
+
addInstallationStep(step.name, step.message, step.status, step.timestamp);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (overallStatus === 'completed' || overallStatus === 'failed') {
|
|
125
|
+
completionMessageSent = true;
|
|
126
|
+
// The hideInstallLoadingModal will be called by the user or after a timeout.
|
|
127
|
+
// We can add a "Close" button or auto-close logic here if needed.
|
|
128
|
+
// For now, just stop polling.
|
|
129
|
+
// Ensure final status is displayed
|
|
130
|
+
const finalMessage = overallStatus === 'completed' ?
|
|
131
|
+
`Installation completed successfully for ${serverName}.` :
|
|
132
|
+
`Installation failed for ${serverName}`;
|
|
133
|
+
updateOverallInstallStatus(overallStatus, finalMessage);
|
|
134
|
+
return;
|
|
181
135
|
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
(
|
|
187
|
-
|
|
188
|
-
if (allCompleted && !completionMessageSent) {
|
|
189
|
-
completionMessageSent = true;
|
|
190
|
-
|
|
191
|
-
// Compose completion message
|
|
192
|
-
const completionMessage = hasRequirements && hasTargets ?
|
|
193
|
-
`Updated requirements and configured ${targets.length} client(s)` :
|
|
194
|
-
hasRequirements ? 'Requirements updated' :
|
|
195
|
-
hasTargets ? `Configured ${targets.length} client(s)` :
|
|
196
|
-
'Operation completed';
|
|
197
|
-
|
|
198
|
-
console.log('[LoadingModal] Completed:', completionMessage);
|
|
199
|
-
delayedAppendInstallLoadingMessage(`<span style="color:green;">${completionMessage}</span>`, true);
|
|
200
|
-
|
|
201
|
-
// Give time for message queue to process before returning
|
|
202
|
-
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
136
|
+
} else if (!result.success && resp.status === 404) {
|
|
137
|
+
// Still waiting for the status file to be created.
|
|
138
|
+
updateOverallInstallStatus('in-progress', `Waiting for installation to start for ${serverName}...`);
|
|
139
|
+
} else if (!result.success) {
|
|
140
|
+
updateOverallInstallStatus('failed', `Error fetching status: ${result.error || 'Unknown error'}`);
|
|
141
|
+
completionMessageSent = true; // Stop polling on error
|
|
203
142
|
return;
|
|
204
143
|
}
|
|
144
|
+
} else if (resp.status === 404) {
|
|
145
|
+
updateOverallInstallStatus('in-progress', `Installation status not yet available for ${serverName}. Retrying...`);
|
|
146
|
+
} else {
|
|
147
|
+
const errorText = await resp.text();
|
|
148
|
+
updateOverallInstallStatus('failed', `Error fetching status: ${errorText || resp.statusText}`);
|
|
149
|
+
completionMessageSent = true; // Stop polling on error
|
|
150
|
+
return;
|
|
205
151
|
}
|
|
206
152
|
} catch (error) {
|
|
207
|
-
console.error('[LoadingModal] Error polling status:', error);
|
|
153
|
+
console.error('[LoadingModal] Error polling new install status:', error);
|
|
154
|
+
updateOverallInstallStatus('failed', `Error polling status: ${error.message}`);
|
|
155
|
+
completionMessageSent = true; // Stop polling on error
|
|
156
|
+
return;
|
|
208
157
|
}
|
|
209
|
-
await new Promise(resolve => setTimeout(resolve, interval));
|
|
158
|
+
await new Promise(resolve => setTimeout(resolve, interval));
|
|
210
159
|
}
|
|
211
160
|
|
|
212
|
-
// If we reach here, we've timed out
|
|
213
|
-
console.error('[LoadingModal] Operation timed out after 10 minutes');
|
|
214
|
-
|
|
215
|
-
// On timeout, show timeout message but don't auto-close
|
|
216
161
|
if (!completionMessageSent) {
|
|
217
|
-
|
|
162
|
+
updateOverallInstallStatus('failed', `Operation timed out for ${serverName} - Please check logs or refresh.`);
|
|
218
163
|
}
|
|
219
164
|
}
|
|
220
165
|
|
|
@@ -225,6 +170,14 @@ async function pollInstallStatus(categoryName, serverName, targets, interval = 2
|
|
|
225
170
|
* @param {string[]} targets - Target clients
|
|
226
171
|
*/
|
|
227
172
|
export async function uninstallTools(categoryName, serverList, targets) {
|
|
173
|
+
|
|
174
|
+
console.log('[LoadingModal] uninstallTools called', { categoryName, serverList, targets });
|
|
175
|
+
// Store category and server name for potential refresh
|
|
176
|
+
if (categoryName) {
|
|
177
|
+
console.log('[LoadingModal] Setting lastSelectedCategory:', categoryName);
|
|
178
|
+
localStorage.setItem('lastSelectedCategory', categoryName);
|
|
179
|
+
}
|
|
180
|
+
|
|
228
181
|
// Get selected targets from window.selectedClients or the provided targets
|
|
229
182
|
const selectedTargets = window.selectedClients || (Array.isArray(targets) ? targets : [targets]);
|
|
230
183
|
|
|
@@ -234,17 +187,26 @@ export async function uninstallTools(categoryName, serverList, targets) {
|
|
|
234
187
|
return;
|
|
235
188
|
}
|
|
236
189
|
|
|
237
|
-
|
|
238
|
-
|
|
190
|
+
// Ensure showInstallLoadingModal is called if not already visible,
|
|
191
|
+
// or ensure overall status is updated if it is.
|
|
192
|
+
// For simplicity, we assume the modal is shown by the caller (e.g., modalSetup.js)
|
|
193
|
+
// Add uninstall started step
|
|
194
|
+
if (typeof addInstallationStep === 'function') {
|
|
195
|
+
addInstallationStep(
|
|
196
|
+
'Uninstalling',
|
|
197
|
+
`Uninstallation process started for ${Object.keys(serverList).join(', ')}`,
|
|
198
|
+
'in-progress',
|
|
199
|
+
new Date().toISOString()
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
updateOverallInstallStatus('in-progress', `Starting uninstallation for ${Object.keys(serverList).join(', ')}...`);
|
|
239
203
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
serverList = formattedServerList;
|
|
247
|
-
}
|
|
204
|
+
try {
|
|
205
|
+
const serverListKeys = Object.keys(serverList);
|
|
206
|
+
serverListKeys.forEach(server => {
|
|
207
|
+
console.log('[LoadingModal] Setting lastSelectedServerName:', server);
|
|
208
|
+
localStorage.setItem('lastSelectedServerName', server);
|
|
209
|
+
});
|
|
248
210
|
|
|
249
211
|
const response = await fetch(`/api/categories/${categoryName}/uninstall`, {
|
|
250
212
|
method: 'POST',
|
|
@@ -260,22 +222,45 @@ export async function uninstallTools(categoryName, serverList, targets) {
|
|
|
260
222
|
|
|
261
223
|
if (!response.ok) {
|
|
262
224
|
const errorData = await response.text();
|
|
225
|
+
updateOverallInstallStatus('failed', `Uninstallation request failed: ${errorData || response.statusText}`);
|
|
263
226
|
throw new Error(`Uninstallation failed: ${errorData || response.statusText}`);
|
|
264
227
|
}
|
|
265
228
|
|
|
266
229
|
const result = await response.json();
|
|
267
230
|
if (!result.success) {
|
|
231
|
+
updateOverallInstallStatus('failed', `Uninstallation failed: ${result.error || 'Operation failed'}`);
|
|
268
232
|
throw new Error(result.error || 'Uninstallation failed');
|
|
269
233
|
}
|
|
270
234
|
|
|
271
|
-
// Add
|
|
272
|
-
|
|
235
|
+
// Add uninstall finished step with 1s delay before updating overall status
|
|
236
|
+
if (typeof addInstallationStep === 'function') {
|
|
237
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
238
|
+
addInstallationStep(
|
|
239
|
+
'Uninstalling',
|
|
240
|
+
`Uninstallation process finished for ${Object.keys(serverList).join(', ')}`,
|
|
241
|
+
'completed',
|
|
242
|
+
new Date().toISOString()
|
|
243
|
+
);
|
|
244
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
245
|
+
addInstallationStep(
|
|
246
|
+
'Uninstall Finished',
|
|
247
|
+
`Uninstallation finished for ${Object.keys(serverList).join(', ')}`,
|
|
248
|
+
'completed',
|
|
249
|
+
new Date().toISOString()
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
253
|
+
updateOverallInstallStatus('completed', `Successfully uninstalled from ${selectedTargets.join(', ')}.`);
|
|
254
|
+
window.selectedClients = []; // Clear selected clients
|
|
273
255
|
|
|
274
|
-
// Clear selected clients after successful uninstall
|
|
275
|
-
window.selectedClients = [];
|
|
276
256
|
} catch (error) {
|
|
277
257
|
console.error('Error uninstalling tools:', error);
|
|
278
|
-
|
|
258
|
+
// The status should have been updated by the specific error handlers above.
|
|
259
|
+
// If an error occurs before those, update status here.
|
|
260
|
+
const overallStatusTextEl = document.getElementById('overallStatusText'); // Check if element exists
|
|
261
|
+
if (overallStatusTextEl && overallStatusTextEl.textContent.startsWith('Starting uninstallation')) {
|
|
262
|
+
updateOverallInstallStatus('failed', `Error uninstalling tools: ${error.message}`);
|
|
263
|
+
}
|
|
279
264
|
showToast(`Error uninstalling tools: ${error.message}`, 'error');
|
|
280
265
|
}
|
|
281
266
|
}
|
|
@@ -1,31 +1,164 @@
|
|
|
1
|
-
import { delayedAppendInstallLoadingMessage } from './messageQueue.js';
|
|
1
|
+
// import { delayedAppendInstallLoadingMessage } from './messageQueue.js'; // Not directly used here anymore for appending
|
|
2
|
+
|
|
3
|
+
const ICONS = {
|
|
4
|
+
IN_PROGRESS: '<div class="status-spinner"></div>',
|
|
5
|
+
COMPLETED: '<div class="status-icon-circled icon-check-container"><span class="status-icon icon-check">✓</span></div>',
|
|
6
|
+
FAILED: '<span class="status-icon icon-cross">✗</span>', // Assuming cross doesn't need a circle for now
|
|
7
|
+
CANCELED: '<span class="status-icon icon-minus">–</span>', // Simple minus for canceled
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
let overallStatusIconEl, overallStatusTextEl, installStepDetailsListEl;
|
|
2
11
|
|
|
3
12
|
/**
|
|
4
13
|
* Display the installation loading modal and prepare it for messages.
|
|
5
|
-
* @param {string} [
|
|
14
|
+
* @param {string} [initialOperationText='Installing'] - The initial overall operation text.
|
|
6
15
|
*/
|
|
7
|
-
export function showInstallLoadingModal(
|
|
16
|
+
export function showInstallLoadingModal(initialOperationText = 'Installing') {
|
|
8
17
|
const loadingModal = document.getElementById('installLoadingModal');
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
18
|
+
// Assuming modal-content is the direct child where content should be placed.
|
|
19
|
+
const modalContent = loadingModal ? loadingModal.querySelector('.modal-content') : null;
|
|
20
|
+
|
|
21
|
+
if (loadingModal && modalContent) {
|
|
22
|
+
modalContent.innerHTML = `
|
|
23
|
+
<button id="installLoadingModalCloseBtn" class="modal-close-btn">×</button>
|
|
24
|
+
<div class="installation-status-header">
|
|
25
|
+
<div id="overallStatusIconContainer" class="overall-status-icon">
|
|
26
|
+
${ICONS.IN_PROGRESS}
|
|
27
|
+
</div>
|
|
28
|
+
<h3 id="overallStatusText" class="overall-status-text">${initialOperationText}...</h3>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="installation-steps-container">
|
|
31
|
+
<ul id="installStepDetailsList" class="install-step-details-list"></ul>
|
|
32
|
+
</div>
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
overallStatusIconEl = modalContent.querySelector('#overallStatusIconContainer');
|
|
36
|
+
overallStatusTextEl = modalContent.querySelector('#overallStatusText');
|
|
37
|
+
installStepDetailsListEl = modalContent.querySelector('#installStepDetailsList');
|
|
38
|
+
|
|
39
|
+
const closeButton = modalContent.querySelector('#installLoadingModalCloseBtn');
|
|
40
|
+
if (closeButton) {
|
|
41
|
+
closeButton.addEventListener('click', hideInstallLoadingModal);
|
|
42
|
+
}
|
|
43
|
+
|
|
12
44
|
loadingModal.style.display = 'block';
|
|
13
|
-
loadingMsg.innerHTML = '';
|
|
14
|
-
loadingTitle.textContent = `${operation}...`;
|
|
15
45
|
} else {
|
|
16
|
-
console.error('[LoadingModal] Required elements not found: installLoadingModal
|
|
46
|
+
console.error('[LoadingModal] Required elements not found: #installLoadingModal or .modal-content');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Updates the overall status display in the loading modal.
|
|
52
|
+
* @param {'in-progress' | 'completed' | 'failed'} status - The overall status.
|
|
53
|
+
* @param {string} message - The message to display for the overall status.
|
|
54
|
+
*/
|
|
55
|
+
export function updateOverallInstallStatus(status, message) {
|
|
56
|
+
if (!overallStatusIconEl || !overallStatusTextEl) {
|
|
57
|
+
// console.warn('[LoadingModal] Overall status elements not initialized or found.');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let iconHtml = ICONS.IN_PROGRESS;
|
|
62
|
+
let statusClass = 'in-progress';
|
|
63
|
+
|
|
64
|
+
switch (status) {
|
|
65
|
+
case 'completed':
|
|
66
|
+
iconHtml = ICONS.COMPLETED;
|
|
67
|
+
statusClass = 'completed';
|
|
68
|
+
break;
|
|
69
|
+
case 'failed':
|
|
70
|
+
iconHtml = ICONS.FAILED;
|
|
71
|
+
statusClass = 'failed';
|
|
72
|
+
break;
|
|
73
|
+
case 'in-progress':
|
|
74
|
+
default:
|
|
75
|
+
// iconHtml and statusClass already set
|
|
76
|
+
break;
|
|
17
77
|
}
|
|
78
|
+
|
|
79
|
+
overallStatusIconEl.innerHTML = iconHtml;
|
|
80
|
+
overallStatusIconEl.className = `overall-status-icon ${statusClass}`; // Update class for styling
|
|
81
|
+
overallStatusTextEl.textContent = message || (status.charAt(0).toUpperCase() + status.slice(1));
|
|
18
82
|
}
|
|
19
83
|
|
|
20
84
|
/**
|
|
21
|
-
*
|
|
22
|
-
* @param {string}
|
|
85
|
+
* Adds a step to the installation details list.
|
|
86
|
+
* @param {string} stepName - The name/title of the step.
|
|
87
|
+
* @param {string} message - The message associated with the step.
|
|
88
|
+
* @param {'pending'|'in-progress'|'completed'|'failed'|'canceled'} status - The status of the step.
|
|
89
|
+
* @param {string} timestamp - ISO timestamp string for the step.
|
|
23
90
|
*/
|
|
24
|
-
export function
|
|
25
|
-
|
|
26
|
-
|
|
91
|
+
export function addInstallationStep(stepName, message, status, timestamp) {
|
|
92
|
+
if (!installStepDetailsListEl) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Use stepName as a unique identifier for the step
|
|
97
|
+
const stepId = `install-step-${stepName.replace(/[^a-zA-Z0-9_-]/g, '_')}`;
|
|
98
|
+
let listItem = document.getElementById(stepId);
|
|
99
|
+
|
|
100
|
+
let statusClass = '';
|
|
101
|
+
let stepIcon = '';
|
|
102
|
+
|
|
103
|
+
switch (status) {
|
|
104
|
+
case 'completed':
|
|
105
|
+
statusClass = 'success';
|
|
106
|
+
stepIcon = '<span class="step-icon icon-check-small">✓</span>';
|
|
107
|
+
break;
|
|
108
|
+
case 'failed':
|
|
109
|
+
statusClass = 'error';
|
|
110
|
+
stepIcon = '<span class="step-icon icon-cross-small">✗</span>';
|
|
111
|
+
break;
|
|
112
|
+
case 'canceled':
|
|
113
|
+
statusClass = 'canceled';
|
|
114
|
+
stepIcon = '<span class="step-icon icon-minus-small">–</span>';
|
|
115
|
+
break;
|
|
116
|
+
case 'in-progress':
|
|
117
|
+
case 'pending':
|
|
118
|
+
default:
|
|
119
|
+
statusClass = 'in-progress';
|
|
120
|
+
stepIcon = '<div class="status-spinner step-spinner"></div>';
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let timeString = '';
|
|
125
|
+
if (timestamp) {
|
|
126
|
+
try {
|
|
127
|
+
timeString = new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });
|
|
128
|
+
} catch (e) { /* ignore */ }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const innerHTML = `
|
|
132
|
+
${stepIcon}
|
|
133
|
+
<div class="step-info">
|
|
134
|
+
<span class="step-name">${stepName}</span>
|
|
135
|
+
<span class="step-message">${message || (
|
|
136
|
+
status === 'completed' ? 'Completed' :
|
|
137
|
+
status === 'failed' ? 'Failed' :
|
|
138
|
+
status === 'canceled' ? 'Canceled' :
|
|
139
|
+
'In Progress'
|
|
140
|
+
)}</span>
|
|
141
|
+
</div>
|
|
142
|
+
<span class="step-timestamp">${timeString}</span>
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
if (listItem) {
|
|
146
|
+
// Update existing step
|
|
147
|
+
listItem.className = `step-detail-item ${statusClass}`;
|
|
148
|
+
listItem.innerHTML = innerHTML;
|
|
149
|
+
} else {
|
|
150
|
+
// Create new step
|
|
151
|
+
listItem = document.createElement('li');
|
|
152
|
+
listItem.id = stepId;
|
|
153
|
+
listItem.className = `step-detail-item ${statusClass}`;
|
|
154
|
+
listItem.innerHTML = innerHTML;
|
|
155
|
+
installStepDetailsListEl.appendChild(listItem);
|
|
156
|
+
// Scroll to the bottom of the list
|
|
157
|
+
installStepDetailsListEl.scrollTop = installStepDetailsListEl.scrollHeight;
|
|
158
|
+
}
|
|
27
159
|
}
|
|
28
160
|
|
|
161
|
+
|
|
29
162
|
/**
|
|
30
163
|
* Hide the install loading modal.
|
|
31
164
|
*/
|
|
@@ -35,17 +168,14 @@ export function hideInstallLoadingModal() {
|
|
|
35
168
|
if (loadingModal) {
|
|
36
169
|
loadingModal.style.display = 'none';
|
|
37
170
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
location.reload();
|
|
47
|
-
}
|
|
48
|
-
}, 100);
|
|
171
|
+
// Clear references to avoid stale elements if modal is reshown
|
|
172
|
+
overallStatusIconEl = null;
|
|
173
|
+
overallStatusTextEl = null;
|
|
174
|
+
installStepDetailsListEl = null;
|
|
175
|
+
|
|
176
|
+
// Dispatch an event to signal that the main modal content should be refreshed
|
|
177
|
+
document.dispatchEvent(new CustomEvent('refreshMainModalContent'));
|
|
178
|
+
console.log('[LoadingModal] Dispatched refreshMainModalContent event.');
|
|
49
179
|
} else {
|
|
50
180
|
console.error('[LoadingModal] loading modal DOM not found');
|
|
51
181
|
}
|