imcp 0.0.19 → 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/.roo/rules-code/rules.md +88 -0
- 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/constants.d.ts +7 -0
- package/dist/core/metadatas/constants.js +7 -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/FeedOnboardService.d.ts +7 -3
- package/dist/core/onboard/FeedOnboardService.js +52 -5
- 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 +53 -47
- 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/services/TelemetryService.d.ts +15 -0
- package/dist/services/TelemetryService.js +54 -0
- package/dist/utils/adoUtils.js +6 -3
- package/dist/utils/githubAuth.js +65 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.js +78 -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 -0
- package/dist/utils/versionUtils.js +76 -0
- package/dist/web/public/css/modal.css +292 -1
- package/dist/web/public/css/serverCategoryList.css +120 -0
- package/dist/web/public/css/serverDetails.css +14 -1
- package/dist/web/public/index.html +126 -21
- package/dist/web/public/js/flights/flights.js +1 -1
- 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/formProcessor.js +18 -11
- package/dist/web/public/js/onboard/publishHandler.js +35 -3
- package/dist/web/public/js/onboard/templates.js +5 -1
- package/dist/web/public/js/onboard/uiHandlers.js +266 -39
- package/dist/web/public/js/onboard/validationHandlers.js +71 -39
- package/dist/web/public/js/serverCategoryDetails.js +60 -11
- package/dist/web/public/js/serverCategoryList.js +93 -9
- package/dist/web/public/js/settings.js +314 -0
- package/dist/web/public/onboard.html +2 -2
- package/dist/web/public/settings.html +135 -0
- package/dist/web/public/styles.css +32 -0
- package/dist/web/server.js +93 -1
- package/{src/web/public/js/onboard → docs}/ONBOARDING_PAGE_DESIGN.md +15 -125
- package/docs/Telemetry.md +136 -0
- package/memory-bank/activeContext.md +26 -0
- package/memory-bank/decisionLog.md +91 -0
- package/memory-bank/productContext.md +41 -0
- package/memory-bank/progress.md +35 -0
- package/memory-bank/systemPatterns.md +10 -0
- package/package.json +2 -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/constants.ts +9 -0
- package/src/core/metadatas/recordingConstants.ts +62 -0
- package/src/core/metadatas/types.ts +23 -0
- package/src/core/onboard/FeedOnboardService.ts +59 -5
- 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 +61 -64
- package/src/services/RequirementService.ts +564 -67
- package/src/services/ServerService.ts +0 -90
- package/src/services/TelemetryService.ts +59 -0
- package/src/utils/adoUtils.ts +6 -4
- package/src/utils/githubAuth.ts +84 -1
- package/src/utils/logger.ts +83 -1
- package/src/utils/macroExpressionUtils.ts +4 -21
- package/src/utils/osUtils.ts +92 -1
- package/src/utils/versionUtils.ts +98 -13
- package/src/web/public/css/modal.css +292 -1
- package/src/web/public/css/serverCategoryList.css +120 -0
- package/src/web/public/css/serverDetails.css +14 -1
- package/src/web/public/index.html +126 -21
- 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/formProcessor.js +18 -11
- package/src/web/public/js/onboard/publishHandler.js +35 -3
- package/src/web/public/js/onboard/templates.js +5 -1
- package/src/web/public/js/onboard/uiHandlers.js +266 -39
- package/src/web/public/js/onboard/validationHandlers.js +71 -39
- package/src/web/public/js/serverCategoryDetails.js +60 -11
- package/src/web/public/js/serverCategoryList.js +93 -9
- package/src/web/public/js/settings.js +314 -0
- package/src/web/public/onboard.html +2 -2
- package/src/web/public/settings.html +135 -0
- package/src/web/public/styles.css +32 -0
- package/src/web/server.ts +96 -1
- package/dist/cli/commands/start.d.ts +0 -2
- package/dist/cli/commands/start.js +0 -32
- package/dist/cli/commands/sync.d.ts +0 -2
- package/dist/cli/commands/sync.js +0 -17
- package/dist/core/ConfigurationLoader.d.ts +0 -32
- package/dist/core/ConfigurationLoader.js +0 -236
- package/dist/core/ConfigurationProvider.d.ts +0 -35
- package/dist/core/ConfigurationProvider.js +0 -375
- package/dist/core/InstallationService.d.ts +0 -50
- package/dist/core/InstallationService.js +0 -350
- package/dist/core/MCPManager.d.ts +0 -28
- package/dist/core/MCPManager.js +0 -188
- package/dist/core/RequirementService.d.ts +0 -40
- package/dist/core/RequirementService.js +0 -110
- package/dist/core/ServerSchemaLoader.d.ts +0 -11
- package/dist/core/ServerSchemaLoader.js +0 -43
- package/dist/core/ServerSchemaProvider.d.ts +0 -17
- package/dist/core/ServerSchemaProvider.js +0 -120
- package/dist/core/constants.d.ts +0 -47
- package/dist/core/constants.js +0 -94
- package/dist/core/installers/BaseInstaller.d.ts +0 -74
- package/dist/core/installers/BaseInstaller.js +0 -253
- package/dist/core/installers/ClientInstaller.d.ts +0 -23
- package/dist/core/installers/ClientInstaller.js +0 -564
- package/dist/core/installers/CommandInstaller.d.ts +0 -37
- package/dist/core/installers/CommandInstaller.js +0 -173
- package/dist/core/installers/GeneralInstaller.d.ts +0 -33
- package/dist/core/installers/GeneralInstaller.js +0 -85
- package/dist/core/installers/InstallerFactory.d.ts +0 -54
- package/dist/core/installers/InstallerFactory.js +0 -97
- package/dist/core/installers/NpmInstaller.d.ts +0 -26
- package/dist/core/installers/NpmInstaller.js +0 -127
- package/dist/core/installers/PipInstaller.d.ts +0 -28
- package/dist/core/installers/PipInstaller.js +0 -127
- package/dist/core/installers/RequirementInstaller.d.ts +0 -33
- package/dist/core/installers/RequirementInstaller.js +0 -3
- package/dist/core/types.d.ts +0 -166
- package/dist/core/types.js +0 -16
- package/dist/services/InstallRequestValidator.d.ts +0 -21
- package/dist/services/InstallRequestValidator.js +0 -99
- package/dist/web/public/js/modal/installHandler.js +0 -227
- package/dist/web/public/js/modal/loadingUI.js +0 -74
- package/dist/web/public/js/modal/modalUI.js +0 -214
- package/dist/web/public/js/modal/version.js +0 -20
- package/src/web/public/js/modal/messageQueue.js +0 -112
|
@@ -1,112 +1,56 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// This file previously handled a message queue for the loading modal.
|
|
2
|
+
// With the new design in loadingModal.js, which directly manipulates its content
|
|
3
|
+
// for overall status and step details, this queueing mechanism for individual
|
|
4
|
+
// step messages is no longer the primary way messages are displayed.
|
|
5
|
+
// The loadingModal.js now has `updateOverallInstallStatus` and `addInstallationStep`.
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Message queue for delayed loading modal updates.
|
|
9
|
-
* @type {Array<{msg: string, isCompletion: boolean}>}
|
|
10
|
-
*/
|
|
11
|
-
let messageQueue = [];
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Process messages in the queue with a delay between each message.
|
|
15
|
-
* When the queue is empty and completion is pending, triggers the completion UI.
|
|
16
|
-
* @private
|
|
17
|
-
*/
|
|
18
|
-
function processMessageQueue() {
|
|
19
|
-
if (isAppending || messageQueue.length === 0) {
|
|
20
|
-
// Only update completion UI if all messages have been displayed
|
|
21
|
-
if (messageQueue.length === 0 && completionPending) {
|
|
22
|
-
// Add small delay before showing completion to ensure last message is visible
|
|
23
|
-
setTimeout(() => {
|
|
24
|
-
updateCompletionUI();
|
|
25
|
-
completionPending = false;
|
|
26
|
-
}, 500);
|
|
27
|
-
}
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
isAppending = true;
|
|
32
|
-
const { msg, isCompletion } = messageQueue.shift();
|
|
33
|
-
|
|
34
|
-
if (isCompletion) {
|
|
35
|
-
completionPending = true;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
appendInstallLoadingMessage(msg);
|
|
7
|
+
// The `updateCompletionUI` function might still be relevant if we want to
|
|
8
|
+
// change the overall modal's appearance (e.g., title, main icon) upon final completion,
|
|
9
|
+
// separate from the step-by-step updates.
|
|
39
10
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
processMessageQueue();
|
|
43
|
-
}, 1000);
|
|
44
|
-
}
|
|
11
|
+
/** @type {boolean} Flag indicating if completion UI update is pending */
|
|
12
|
+
let completionPending = false; // This might still be useful for a final "Completed" state change of the modal itself.
|
|
45
13
|
|
|
46
14
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* @param {
|
|
15
|
+
* This function could be called by `installation.js` once polling determines
|
|
16
|
+
* the absolute final state (e.g., after all messages are processed by `loadingModal.js`
|
|
17
|
+
* and a small delay).
|
|
18
|
+
* @param {'completed' | 'failed'} finalState
|
|
51
19
|
*/
|
|
52
|
-
export function
|
|
53
|
-
|
|
54
|
-
|
|
20
|
+
export function triggerFinalModalStateUpdate(finalState) {
|
|
21
|
+
// This function is a placeholder for now.
|
|
22
|
+
// The actual update of the overall status icon and text is now handled by
|
|
23
|
+
// `updateOverallInstallStatus` in `loadingModal.js`.
|
|
24
|
+
// If there's a need for a *separate* final transformation of the modal
|
|
25
|
+
// (e.g. changing the main title, showing a global success/fail icon not tied to overallStatusTextEl),
|
|
26
|
+
// that logic would go here or be triggered from here.
|
|
27
|
+
|
|
28
|
+
// For now, let's assume `updateOverallInstallStatus` covers the visual needs.
|
|
29
|
+
// If `updateCompletionUI` was doing something unique beyond what `updateOverallInstallStatus` does,
|
|
30
|
+
// we could call it here.
|
|
31
|
+
console.log(`[MessageQueue] Triggering final modal state: ${finalState}`);
|
|
32
|
+
// if (finalState === 'completed') {
|
|
33
|
+
// updateCompletionUI(); // If this has distinct visual changes for the modal wrapper itself.
|
|
34
|
+
// }
|
|
55
35
|
}
|
|
56
36
|
|
|
57
|
-
function updateCompletionUI() {
|
|
58
|
-
const loadingTitle = document.querySelector('.loading-title');
|
|
59
|
-
const loadingIcon = document.querySelector('.loading-icon');
|
|
60
|
-
|
|
61
|
-
if (loadingTitle) {
|
|
62
|
-
loadingTitle.textContent = 'Completed';
|
|
63
|
-
loadingTitle.classList.add('completed');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (loadingIcon) {
|
|
67
|
-
loadingIcon.innerHTML = `
|
|
68
|
-
<svg width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
69
|
-
<circle cx="24" cy="24" r="20" stroke="#059669" stroke-width="4"/>
|
|
70
|
-
<path d="M16 24l6 6 12-12" stroke="#059669" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
|
71
|
-
</svg>
|
|
72
|
-
`;
|
|
73
|
-
loadingIcon.classList.add('completed');
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Internal function to append a message to the loading modal with formatting.
|
|
79
|
-
* @private
|
|
80
|
-
* @param {string} message - The message to append
|
|
81
|
-
*/
|
|
82
|
-
function appendInstallLoadingMessage(message) {
|
|
83
|
-
const loadingMsg = document.getElementById('installLoadingMessage');
|
|
84
|
-
if (loadingMsg) {
|
|
85
|
-
// Split message on newlines
|
|
86
|
-
const lines = message.split('\n');
|
|
87
|
-
|
|
88
|
-
lines.forEach(line => {
|
|
89
|
-
// Check if line contains an error
|
|
90
|
-
const isError = line.toLowerCase().includes('error') ||
|
|
91
|
-
line.toLowerCase().includes('failed') ||
|
|
92
|
-
line.toLowerCase().includes('timeout');
|
|
93
37
|
|
|
94
|
-
|
|
38
|
+
// function updateCompletionUI() {
|
|
39
|
+
// // This function's original content might be merged into updateOverallInstallStatus
|
|
40
|
+
// // or called if a distinct "modal completed" visual state is needed beyond the text/icon.
|
|
41
|
+
// const loadingTitle = document.querySelector('.loading-title'); // This element might not exist with new structure
|
|
42
|
+
// const loadingIcon = document.querySelector('.loading-icon'); // This element might not exist
|
|
95
43
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
44
|
+
// if (loadingTitle) {
|
|
45
|
+
// loadingTitle.textContent = 'Completed';
|
|
46
|
+
// loadingTitle.classList.add('completed');
|
|
47
|
+
// }
|
|
99
48
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
messageDiv.innerHTML = formattedMessage;
|
|
49
|
+
// if (loadingIcon) {
|
|
50
|
+
// // The main overall status icon is now handled by updateOverallInstallStatus
|
|
51
|
+
// }
|
|
52
|
+
// }
|
|
105
53
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
} else {
|
|
110
|
-
console.error('[LoadingModal] Element not found: installLoadingMessage');
|
|
111
|
-
}
|
|
112
|
-
}
|
|
54
|
+
// The old delayedAppendInstallLoadingMessage and appendInstallLoadingMessage are removed
|
|
55
|
+
// as step messages are now directly added by loadingModal.js->addInstallationStep
|
|
56
|
+
// and overall status by loadingModal.js->updateOverallInstallStatus.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { showToast, showConfirm } from '../notifications.js';
|
|
2
|
-
import { showInstallLoadingModal } from './loadingModal.js';
|
|
3
|
-
import { delayedAppendInstallLoadingMessage } from './messageQueue.js';
|
|
2
|
+
import { showInstallLoadingModal, updateOverallInstallStatus } from './loadingModal.js';
|
|
3
|
+
// import { delayedAppendInstallLoadingMessage } from './messageQueue.js'; // No longer used
|
|
4
4
|
import { uninstallTools } from './installation.js';
|
|
5
5
|
import { handleBulkClientInstall } from './installation.js';
|
|
6
6
|
import { compareVersions } from './versionUtils.js';
|
|
@@ -56,17 +56,31 @@ export function setupEnvironmentVariables(mcpServer, envInputsDiv, targetData) {
|
|
|
56
56
|
const envRequirements = mcpServer.installation?.['env'] || mcpServer.installation?.env || {};
|
|
57
57
|
addEnvironmentTitle(envInputsDiv);
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
// Fetch system settings to get userConfigurations for env defaults
|
|
60
|
+
fetch('/api/settings')
|
|
61
|
+
.then(response => response.json())
|
|
62
|
+
.then(data => {
|
|
63
|
+
const userConfigurations = (data && data.data && data.data.userConfigurations) ? data.data.userConfigurations : {};
|
|
64
|
+
if (Object.keys(envRequirements).length === 0) {
|
|
65
|
+
addNoEnvMessage(envInputsDiv);
|
|
66
|
+
} else {
|
|
67
|
+
createEnvironmentInputs(envRequirements, envInputsDiv, targetData.clientMcpSettings, mcpServer.name, userConfigurations);
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
.catch(() => {
|
|
71
|
+
// fallback to old logic if fetch fails
|
|
72
|
+
if (Object.keys(envRequirements).length === 0) {
|
|
73
|
+
addNoEnvMessage(envInputsDiv);
|
|
74
|
+
} else {
|
|
75
|
+
createEnvironmentInputs(envRequirements, envInputsDiv, targetData.clientMcpSettings, mcpServer.name, {});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
64
78
|
}
|
|
65
79
|
|
|
66
80
|
/**
|
|
67
81
|
* Set up installation arguments section in the modal
|
|
68
82
|
*/
|
|
69
|
-
export function setupInstallationArguments(installation, modalArguments, mcpServer) {
|
|
83
|
+
export function setupInstallationArguments(installation, modalArguments, categoryName, mcpServer) {
|
|
70
84
|
// For SSE mode, don't show arguments section at all
|
|
71
85
|
if (mcpServer?.mode === 'sse') {
|
|
72
86
|
modalArguments.style.display = 'none';
|
|
@@ -88,7 +102,7 @@ export function setupInstallationArguments(installation, modalArguments, mcpServ
|
|
|
88
102
|
}
|
|
89
103
|
|
|
90
104
|
if (installation.command === 'python' || installation.command.includes('python')) {
|
|
91
|
-
addPythonEnvironmentInput(modalArguments);
|
|
105
|
+
addPythonEnvironmentInput(categoryName, mcpServer.name, modalArguments);
|
|
92
106
|
}
|
|
93
107
|
}
|
|
94
108
|
|
|
@@ -157,36 +171,90 @@ export function setupFormSubmitHandler(form, envInputsDiv, modalArguments, modal
|
|
|
157
171
|
});
|
|
158
172
|
}
|
|
159
173
|
|
|
160
|
-
|
|
174
|
+
// Only POST userConfigurations and/or pythonEnvs if there is a difference, then proceed with install
|
|
175
|
+
fetch('/api/settings')
|
|
176
|
+
.then(response => response.json())
|
|
177
|
+
.then(data => {
|
|
178
|
+
const userConfigurations = (data && data.data && data.data.userConfigurations) ? data.data.userConfigurations : {};
|
|
179
|
+
const pythonEnvs = (data && data.data && data.data.pythonEnvs) ? data.data.pythonEnvs : {};
|
|
180
|
+
const updatedUserConfigurations = { ...userConfigurations };
|
|
181
|
+
const updatedPythonEnvs = { ...pythonEnvs };
|
|
182
|
+
|
|
183
|
+
Object.keys(envVars).forEach(key => {
|
|
184
|
+
updatedUserConfigurations[key] = envVars[key];
|
|
185
|
+
});
|
|
161
186
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
187
|
+
// Check for userConfigurations difference
|
|
188
|
+
let needsUserConfigUpdate = false;
|
|
189
|
+
const allUserConfigKeys = new Set([...Object.keys(userConfigurations), ...Object.keys(envVars)]);
|
|
190
|
+
for (const key of allUserConfigKeys) {
|
|
191
|
+
if (userConfigurations[key] !== updatedUserConfigurations[key]) {
|
|
192
|
+
needsUserConfigUpdate = true;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
169
196
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
197
|
+
// Check for pythonEnvs difference
|
|
198
|
+
let needsPythonEnvUpdate = false;
|
|
199
|
+
if (pythonEnv !== undefined && pythonEnv !== pythonEnvs[`${categoryName}:${serverName}`]) {
|
|
200
|
+
updatedPythonEnvs[`${categoryName}:${serverName}`] = pythonEnv;
|
|
201
|
+
needsPythonEnvUpdate = true;
|
|
202
|
+
}
|
|
177
203
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
204
|
+
// If either needs update, POST the merged object
|
|
205
|
+
if (needsUserConfigUpdate || needsPythonEnvUpdate) {
|
|
206
|
+
const postBody = {};
|
|
207
|
+
if (needsUserConfigUpdate) postBody.userConfigurations = updatedUserConfigurations;
|
|
208
|
+
if (needsPythonEnvUpdate) postBody.pythonEnvs = updatedPythonEnvs;
|
|
209
|
+
fetch('/api/settings', {
|
|
210
|
+
method: 'POST',
|
|
211
|
+
headers: { 'Content-Type': 'application/json' },
|
|
212
|
+
body: JSON.stringify(postBody)
|
|
213
|
+
}).then(() => {
|
|
214
|
+
proceedInstall();
|
|
215
|
+
}).catch(() => {
|
|
216
|
+
proceedInstall();
|
|
217
|
+
});
|
|
218
|
+
} else {
|
|
219
|
+
proceedInstall();
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
.catch(() => {
|
|
223
|
+
proceedInstall();
|
|
224
|
+
});
|
|
184
225
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
226
|
+
function proceedInstall() {
|
|
227
|
+
const hasRequirementsToUpdate = requirementsToUpdate.length > 0;
|
|
228
|
+
|
|
229
|
+
const uninstallBtn = document.querySelector('.uninstall-btn');
|
|
230
|
+
if (!uninstallBtn || !uninstallBtn.matches(':active')) {
|
|
231
|
+
const selectedTargets = window.selectedClients;
|
|
232
|
+
if (selectedTargets.length === 0 && !hasRequirementsToUpdate) {
|
|
233
|
+
showToast('Please select at least one client to configure.', 'error');
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
let installingMessage = "Starting installation...";
|
|
238
|
+
const serverStatus = serverStatuses[serverName] || { installedStatus: {} };
|
|
239
|
+
if (selectedTargets.length > 0) {
|
|
240
|
+
const target = selectedTargets[0];
|
|
241
|
+
const msg = serverStatus.installedStatus?.[target]?.message;
|
|
242
|
+
if (msg) installingMessage = msg;
|
|
243
|
+
}
|
|
188
244
|
|
|
189
|
-
|
|
245
|
+
const serverInstallOptions = {
|
|
246
|
+
targetClients: selectedTargets,
|
|
247
|
+
env: envVars,
|
|
248
|
+
args: args,
|
|
249
|
+
settings: pythonEnv ? { pythonEnv: pythonEnv } : {}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
if (requirementsToUpdate.length > 0) {
|
|
253
|
+
serverInstallOptions.requirements = requirementsToUpdate;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
handleBulkClientInstall(categoryName, serverName, selectedTargets, envVars, installingMessage, serverData, serverInstallOptions);
|
|
257
|
+
}
|
|
190
258
|
}
|
|
191
259
|
};
|
|
192
260
|
}
|
|
@@ -288,11 +356,12 @@ function createUninstallButton(target, categoryName, serverName) {
|
|
|
288
356
|
};
|
|
289
357
|
|
|
290
358
|
try {
|
|
291
|
-
|
|
359
|
+
updateOverallInstallStatus('in-progress', `Uninstalling ${serverName} from ${target}...`);
|
|
292
360
|
await uninstallTools(categoryName, serverList, [target]);
|
|
361
|
+
// uninstallTools will now handle its own final status update via updateOverallInstallStatus
|
|
293
362
|
} catch (error) {
|
|
294
|
-
|
|
295
|
-
throw
|
|
363
|
+
updateOverallInstallStatus('failed', `Error during uninstall: ${error.message}`);
|
|
364
|
+
// No need to throw here if uninstallTools handles its errors by updating status
|
|
296
365
|
}
|
|
297
366
|
}
|
|
298
367
|
return false;
|
|
@@ -326,7 +395,7 @@ function addNoEnvMessage(envInputsDiv) {
|
|
|
326
395
|
envInputsDiv.appendChild(noEnvMessage);
|
|
327
396
|
}
|
|
328
397
|
|
|
329
|
-
function createEnvironmentInputs(envRequirements, envInputsDiv, clientSettings, serverName) {
|
|
398
|
+
function createEnvironmentInputs(envRequirements, envInputsDiv, clientSettings, serverName, userConfigurations = {}) {
|
|
330
399
|
Object.keys(envRequirements).forEach(key => {
|
|
331
400
|
const req = envRequirements[key];
|
|
332
401
|
const inputId = `env_${key}`;
|
|
@@ -346,11 +415,13 @@ function createEnvironmentInputs(envRequirements, envInputsDiv, clientSettings,
|
|
|
346
415
|
input.required = req.Required;
|
|
347
416
|
input.className = 'input-field';
|
|
348
417
|
|
|
349
|
-
// For default value, first check
|
|
418
|
+
// For default value, first check userConfigurations, then client settings, then provided default
|
|
350
419
|
let defaultValue = req.Default || '';
|
|
351
420
|
|
|
352
|
-
//
|
|
353
|
-
if (
|
|
421
|
+
// Use userConfigurations if present
|
|
422
|
+
if (userConfigurations && userConfigurations[key]) {
|
|
423
|
+
defaultValue = userConfigurations[key];
|
|
424
|
+
} else if (clientSettings && clientSettings.MSRooCode &&
|
|
354
425
|
clientSettings.MSRooCode.mcpServers &&
|
|
355
426
|
clientSettings.MSRooCode.mcpServers[serverName] &&
|
|
356
427
|
clientSettings.MSRooCode.mcpServers[serverName].env &&
|
|
@@ -416,7 +487,7 @@ function createArgumentInput(value = '') {
|
|
|
416
487
|
return argWrapper;
|
|
417
488
|
}
|
|
418
489
|
|
|
419
|
-
function addPythonEnvironmentInput(modalArguments) {
|
|
490
|
+
function addPythonEnvironmentInput(categoryName, serverName, modalArguments) {
|
|
420
491
|
const pythonEnvWrapper = document.createElement('div');
|
|
421
492
|
pythonEnvWrapper.className = 'mt-4';
|
|
422
493
|
|
|
@@ -433,12 +504,23 @@ function addPythonEnvironmentInput(modalArguments) {
|
|
|
433
504
|
|
|
434
505
|
const envDescription = document.createElement('p');
|
|
435
506
|
envDescription.className = 'text-xs text-gray-500 mt-1';
|
|
436
|
-
envDescription.textContent = 'Specify the Python
|
|
507
|
+
envDescription.textContent = 'Specify the Python exectable file(e.g. C:/python312/python) to use for installation. Leave empty to use system default. You can specify value in Settings page';
|
|
437
508
|
|
|
438
509
|
pythonEnvWrapper.appendChild(pythonEnvLabel);
|
|
439
510
|
pythonEnvWrapper.appendChild(pythonEnvInput);
|
|
440
511
|
pythonEnvWrapper.appendChild(envDescription);
|
|
441
512
|
modalArguments.appendChild(pythonEnvWrapper);
|
|
513
|
+
// Fetch system settings and set default value for python_env
|
|
514
|
+
fetch('/api/settings')
|
|
515
|
+
.then(response => response.json())
|
|
516
|
+
.then(data => {
|
|
517
|
+
if (data && data.data && data.data.pythonEnvs) {
|
|
518
|
+
pythonEnvInput.value = data.data.pythonEnvs[`${categoryName}:${serverName}`] || data.data.pythonEnvs['system'] || '';
|
|
519
|
+
}
|
|
520
|
+
})
|
|
521
|
+
.catch(() => {
|
|
522
|
+
// Ignore errors, leave input empty
|
|
523
|
+
});
|
|
442
524
|
}
|
|
443
525
|
|
|
444
526
|
function renderRequirements(serverRequirements, requirements, categoryName, serverName) {
|
|
@@ -17,18 +17,6 @@ export function closeModal() {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
* Close modal if clicked outside content
|
|
22
|
-
*/
|
|
23
|
-
export function setupModalOutsideClick() {
|
|
24
|
-
window.onclick = function (event) {
|
|
25
|
-
const installModal = document.getElementById('installModal');
|
|
26
|
-
if (event.target == installModal) {
|
|
27
|
-
closeModal();
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
20
|
/**
|
|
33
21
|
* Setup toggle switch styles
|
|
34
22
|
*/
|
|
@@ -2,28 +2,41 @@
|
|
|
2
2
|
import {
|
|
3
3
|
showInstallModal,
|
|
4
4
|
closeModal,
|
|
5
|
-
setupModalOutsideClick,
|
|
6
5
|
uninstallTools,
|
|
7
6
|
showInstallLoadingModal,
|
|
8
|
-
appendInstallLoadingMessage,
|
|
9
|
-
hideInstallLoadingModal
|
|
7
|
+
// appendInstallLoadingMessage, // Removed
|
|
8
|
+
hideInstallLoadingModal,
|
|
9
|
+
// Potentially add updateOverallInstallStatus, addInstallationStep if needed from ./modal/index.js
|
|
10
10
|
} from './modal/index.js';
|
|
11
11
|
|
|
12
12
|
// Re-export all modal functionality
|
|
13
13
|
export {
|
|
14
14
|
showInstallModal,
|
|
15
15
|
closeModal,
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
uninstallTools,
|
|
17
|
+
showInstallLoadingModal, // Added to exports
|
|
18
|
+
hideInstallLoadingModal // Added to exports
|
|
19
|
+
// Potentially add updateOverallInstallStatus, addInstallationStep to exports
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
// Make certain functions available globally
|
|
21
23
|
window.showInstallModal = showInstallModal;
|
|
22
24
|
window.showInstallLoadingModal = showInstallLoadingModal;
|
|
23
|
-
window.appendInstallLoadingMessage = appendInstallLoadingMessage;
|
|
25
|
+
// window.appendInstallLoadingMessage = appendInstallLoadingMessage; // Removed
|
|
24
26
|
window.hideInstallLoadingModal = hideInstallLoadingModal;
|
|
25
27
|
|
|
26
|
-
//
|
|
27
|
-
document.addEventListener('
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
// Listen for the custom event to refresh the main modal content
|
|
29
|
+
document.addEventListener('refreshMainModalContent', () => {
|
|
30
|
+
console.log('[ModalJS] Received refreshMainModalContent event. Refreshing main modal.');
|
|
31
|
+
const lastSelectedCategory = localStorage.getItem('lastSelectedCategory');
|
|
32
|
+
const lastSelectedServerName = localStorage.getItem('lastSelectedServerName');
|
|
33
|
+
|
|
34
|
+
const isCategoryValid = lastSelectedCategory && lastSelectedCategory.trim() !== '' && lastSelectedCategory !== 'undefined' && lastSelectedCategory !== 'null';
|
|
35
|
+
const isServerNameValid = lastSelectedServerName && lastSelectedServerName.trim() !== '' && lastSelectedServerName !== 'undefined' && lastSelectedServerName !== 'null';
|
|
36
|
+
|
|
37
|
+
if (isCategoryValid && isServerNameValid) {
|
|
38
|
+
window.showInstallModal(lastSelectedCategory, lastSelectedServerName);
|
|
39
|
+
} else {
|
|
40
|
+
console.warn(`[ModalJS] Not refreshing modal. Category valid: ${isCategoryValid} (value: "${lastSelectedCategory}"), ServerName valid: ${isServerNameValid} (value: "${lastSelectedServerName}")`);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
@@ -10,17 +10,7 @@ function setupRealTimeValidation(formId) {
|
|
|
10
10
|
const form = document.getElementById(formId);
|
|
11
11
|
if (!form) return;
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
const showValidationMessage = (element, message, isError = true) => {
|
|
15
|
-
let messageDiv = element.nextElementSibling;
|
|
16
|
-
if (!messageDiv || !messageDiv.classList.contains('validation-message')) {
|
|
17
|
-
messageDiv = document.createElement('div');
|
|
18
|
-
messageDiv.className = `validation-message text-xs mt-1 ${isError ? 'text-red-500' : 'text-green-500'}`;
|
|
19
|
-
element.insertAdjacentElement('afterend', messageDiv);
|
|
20
|
-
}
|
|
21
|
-
messageDiv.textContent = message;
|
|
22
|
-
messageDiv.className = `validation-message text-xs mt-1 ${isError ? 'text-red-500' : 'text-green-500'}`;
|
|
23
|
-
};
|
|
13
|
+
if (!form) return;
|
|
24
14
|
|
|
25
15
|
// Name input validation (category or server)
|
|
26
16
|
const nameInput = form.querySelector('input[name="name"]');
|
|
@@ -1026,5 +1016,22 @@ export function resetOnboardFormDynamicContent(formId = 'onboardForm', serversLi
|
|
|
1026
1016
|
setupRealTimeValidation(formId);
|
|
1027
1017
|
}
|
|
1028
1018
|
|
|
1019
|
+
/**
|
|
1020
|
+
* Shows validation message under an input field
|
|
1021
|
+
* @param {HTMLElement} element - The element to show validation message for
|
|
1022
|
+
* @param {string} message - The validation message to display
|
|
1023
|
+
* @param {boolean} isError - Whether this is an error message
|
|
1024
|
+
*/
|
|
1025
|
+
export function showValidationMessage(element, message, isError = true) {
|
|
1026
|
+
let messageDiv = element.nextElementSibling;
|
|
1027
|
+
if (!messageDiv || !messageDiv.classList.contains('validation-message')) {
|
|
1028
|
+
messageDiv = document.createElement('div');
|
|
1029
|
+
messageDiv.className = `validation-message text-xs mt-1 ${isError ? 'text-red-500' : 'text-green-500'}`;
|
|
1030
|
+
element.insertAdjacentElement('afterend', messageDiv);
|
|
1031
|
+
}
|
|
1032
|
+
messageDiv.textContent = message;
|
|
1033
|
+
messageDiv.className = `validation-message text-xs mt-1 ${isError ? 'text-red-500' : 'text-green-500'}`;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1029
1036
|
// Export setupRealTimeValidation for external use if needed
|
|
1030
1037
|
export { setupRealTimeValidation };
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { showToast } from '../notifications.js';
|
|
3
3
|
import { getFormData } from './formProcessor.js';
|
|
4
4
|
import {
|
|
5
|
+
validateFormFields,
|
|
5
6
|
pollOperationStatus,
|
|
6
7
|
updateOperationDisplay,
|
|
7
8
|
getElementIdsByTab,
|
|
@@ -24,9 +25,9 @@ export async function handlePublish(event, activeTab, currentSelectedCategoryDat
|
|
|
24
25
|
const statusContentElement = document.getElementById(contentId);
|
|
25
26
|
// Ensure the progress toggle listener is attached when handlePublish is called
|
|
26
27
|
if (typeof ensureProgressToggleListener === 'function') { // Check if imported correctly
|
|
27
|
-
|
|
28
|
+
ensureProgressToggleListener(statusContentElement);
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
+
|
|
30
31
|
const onboardForm = document.getElementById(formId);
|
|
31
32
|
const validateButton = document.getElementById(validateButtonId);
|
|
32
33
|
const publishButton = document.getElementById(publishButtonId);
|
|
@@ -37,6 +38,37 @@ export async function handlePublish(event, activeTab, currentSelectedCategoryDat
|
|
|
37
38
|
return;
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
// Validate form fields
|
|
42
|
+
const validationResult = validateFormFields(onboardForm, activeTab);
|
|
43
|
+
if (!validationResult.isValid) {
|
|
44
|
+
showToast('Please fix all validation errors before proceeding.', 'error');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let hasErrors = false;
|
|
49
|
+
|
|
50
|
+
if (activeTab === 'create-category') {
|
|
51
|
+
const formData = getFormData(onboardForm, false);
|
|
52
|
+
if (!formData.mcpServers || formData.mcpServers.length === 0) {
|
|
53
|
+
showToast('At least one MCP server must be configured for a new category.', 'error');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check category name format
|
|
58
|
+
const nameInput = onboardForm.querySelector('input[name="name"]');
|
|
59
|
+
if (nameInput && nameInput.value.trim()) {
|
|
60
|
+
if (!/^[a-zA-Z0-9-_]+$/.test(nameInput.value.trim())) {
|
|
61
|
+
showValidationMessage(nameInput, 'Only alphanumeric characters, hyphens, and underscores allowed', true);
|
|
62
|
+
hasErrors = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (hasErrors) {
|
|
68
|
+
showToast('Please fix all validation errors before proceeding.', 'error');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
40
72
|
publishButton.disabled = true;
|
|
41
73
|
publishButton.innerHTML = "<i class='bx bx-loader-alt bx-spin mr-2'></i>Publishing...";
|
|
42
74
|
validateButton.disabled = true; // Also disable validate button during publish
|
|
@@ -116,7 +148,7 @@ export async function handlePublish(event, activeTab, currentSelectedCategoryDat
|
|
|
116
148
|
statusContentElement.innerHTML = `<p class="text-red-500">${errorMessage}</p>`;
|
|
117
149
|
}
|
|
118
150
|
showToast(errorMessage, 'error');
|
|
119
|
-
|
|
151
|
+
|
|
120
152
|
// Restore buttons to their original state fully
|
|
121
153
|
publishButton.disabled = false;
|
|
122
154
|
publishButton.innerHTML = "<i class='bx bx-cloud-upload mr-2'></i>Publish";
|
|
@@ -17,7 +17,7 @@ export const serverTemplate = (serverIndex, isReadOnly = false, serverData = nul
|
|
|
17
17
|
|
|
18
18
|
const disabledAttr = makeServerFullyEditable ? '' : 'disabled';
|
|
19
19
|
const readOnlyClasses = makeServerFullyEditable ? '' : 'bg-gray-100 cursor-not-allowed opacity-70';
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
// "Remove Server" button should be hidden if the server is NOT fully editable.
|
|
22
22
|
// A server is fully editable if the context isn't read-only OR if it's an adhoc server.
|
|
23
23
|
const hideRemoveServerButtonClass = !makeServerFullyEditable ? 'hidden' : '';
|
|
@@ -42,6 +42,10 @@ export const serverTemplate = (serverIndex, isReadOnly = false, serverData = nul
|
|
|
42
42
|
class="action-button-in-server p-1.5 text-sm text-red-600 hover:text-red-800 hover:bg-red-50 rounded-md flex items-center mr-2 ${hideRemoveServerButtonClass}" title="Remove Server">
|
|
43
43
|
<i class='bx bx-trash text-lg'></i>
|
|
44
44
|
</button>
|
|
45
|
+
<button type="button" onclick="event.stopPropagation(); window.duplicateServer(${serverIndex}, '${serversListId}')"
|
|
46
|
+
class="action-button-in-server duplicate-mcp-server-button p-1.5 text-sm text-blue-600 hover:text-blue-800 hover:bg-blue-50 rounded-md flex items-center mr-2" title="Duplicate Server">
|
|
47
|
+
<i class='bx bx-copy text-lg'></i>
|
|
48
|
+
</button>
|
|
45
49
|
<i class='bx bxs-chevron-down text-xl toggle-icon'></i>
|
|
46
50
|
</div>
|
|
47
51
|
</div>
|