imcp 0.0.1 → 0.0.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/commands/install.js +8 -8
- package/dist/cli/index.js +3 -2
- package/dist/core/ConfigurationProvider.d.ts +2 -0
- package/dist/core/ConfigurationProvider.js +49 -3
- package/dist/core/InstallationService.d.ts +8 -0
- package/dist/core/InstallationService.js +117 -0
- package/dist/core/MCPManager.d.ts +1 -0
- package/dist/core/MCPManager.js +42 -0
- package/dist/core/RequirementService.d.ts +7 -0
- package/dist/core/RequirementService.js +17 -0
- package/dist/core/constants.d.ts +5 -0
- package/dist/core/constants.js +9 -4
- package/dist/core/installers/BaseInstaller.js +26 -9
- package/dist/core/installers/GeneralInstaller.js +0 -5
- package/dist/core/installers/NpmInstaller.js +2 -1
- package/dist/core/types.d.ts +7 -6
- package/dist/services/ServerService.js +16 -0
- package/dist/utils/versionUtils.d.ts +12 -0
- package/dist/utils/versionUtils.js +26 -0
- package/dist/web/public/js/modal.js +231 -46
- package/dist/web/server.d.ts +6 -0
- package/dist/web/server.js +6 -1
- package/package.json +1 -1
- package/src/cli/commands/install.ts +11 -14
- package/src/cli/index.ts +4 -2
- package/src/core/ConfigurationProvider.ts +51 -3
- package/src/core/InstallationService.ts +131 -0
- package/src/core/MCPManager.ts +60 -1
- package/src/core/RequirementService.ts +21 -1
- package/src/core/constants.ts +11 -5
- package/src/core/installers/BaseInstaller.ts +33 -17
- package/src/core/installers/GeneralInstaller.ts +0 -5
- package/src/core/installers/NpmInstaller.ts +2 -1
- package/src/core/types.ts +8 -6
- package/src/services/ServerService.ts +22 -0
- package/src/utils/versionUtils.ts +29 -0
- package/src/web/public/js/modal.js +231 -46
- package/src/web/server.ts +16 -2
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for version comparison and management
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Compare two semantic version strings
|
|
6
|
+
* @param v1 First version
|
|
7
|
+
* @param v2 Second version
|
|
8
|
+
* @returns -1 if v1 < v2, 0 if v1 = v2, 1 if v1 > v2
|
|
9
|
+
* (or more specifically, a negative number if v1 < v2,
|
|
10
|
+
* a positive number if v1 > v2, 0 if equal)
|
|
11
|
+
*/
|
|
12
|
+
export function compareVersions(v1, v2) {
|
|
13
|
+
const v1Parts = v1.split('.').map(Number);
|
|
14
|
+
const v2Parts = v2.split('.').map(Number);
|
|
15
|
+
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
|
|
16
|
+
const v1Part = i < v1Parts.length ? v1Parts[i] : 0;
|
|
17
|
+
const v2Part = i < v2Parts.length ? v2Parts[i] : 0;
|
|
18
|
+
if (v1Part !== v2Part) {
|
|
19
|
+
// This returns the actual difference, which is:
|
|
20
|
+
// negative if v1Part < v2Part, positive if v1Part > v2Part
|
|
21
|
+
return v1Part - v2Part;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=versionUtils.js.map
|
|
@@ -4,6 +4,27 @@ import { showToast, showConfirm } from './notifications.js';
|
|
|
4
4
|
document.addEventListener('DOMContentLoaded', () => {
|
|
5
5
|
setupModalOutsideClick();
|
|
6
6
|
});
|
|
7
|
+
/**
|
|
8
|
+
* Simple version comparison function
|
|
9
|
+
* @param {string} v1 First version
|
|
10
|
+
* @param {string} v2 Second version
|
|
11
|
+
* @returns {number} 1 if v1 > v2, -1 if v1 < v2, 0 if equal
|
|
12
|
+
*/
|
|
13
|
+
function compareVersions(v1, v2) {
|
|
14
|
+
const v1Parts = v1.split('.').map(Number);
|
|
15
|
+
const v2Parts = v2.split('.').map(Number);
|
|
16
|
+
|
|
17
|
+
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
|
|
18
|
+
const v1Part = v1Parts[i] || 0;
|
|
19
|
+
const v2Part = v2Parts[i] || 0;
|
|
20
|
+
|
|
21
|
+
if (v1Part > v2Part) return 1;
|
|
22
|
+
if (v1Part < v2Part) return -1;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
7
28
|
/** Delayed message queue for loading modal */
|
|
8
29
|
let messageQueue = [];
|
|
9
30
|
let isAppending = false;
|
|
@@ -147,8 +168,8 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
147
168
|
const operationStatus = serverStatus.installedStatus[target] || { status: 'not-installed', type: 'check', target: 'server' };
|
|
148
169
|
|
|
149
170
|
// Determine client status
|
|
150
|
-
let statusText = '
|
|
151
|
-
let statusClass = '
|
|
171
|
+
let statusText = '';
|
|
172
|
+
let statusClass = '';
|
|
152
173
|
|
|
153
174
|
if (operationStatus.status === 'completed' && operationStatus.type === 'install') {
|
|
154
175
|
statusText = 'Installed';
|
|
@@ -163,6 +184,7 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
163
184
|
statusText = 'Failed';
|
|
164
185
|
statusClass = 'not-installed';
|
|
165
186
|
}
|
|
187
|
+
// Do not show not-installed status for targets that aren't installed
|
|
166
188
|
|
|
167
189
|
const isConfigured = operationStatus.status === 'completed' && operationStatus.type === 'install';
|
|
168
190
|
|
|
@@ -222,14 +244,16 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
222
244
|
// Add elements to client info
|
|
223
245
|
clientInfo.appendChild(clientName);
|
|
224
246
|
|
|
225
|
-
//
|
|
226
|
-
const statusBadge = document.createElement('span');
|
|
227
|
-
statusBadge.className = `status-badge ${statusClass}`;
|
|
228
|
-
statusBadge.textContent = statusText;
|
|
229
|
-
|
|
230
|
-
// Add components to client item
|
|
247
|
+
// Add client info (name) to the item first
|
|
231
248
|
clientItem.appendChild(clientInfo);
|
|
232
|
-
|
|
249
|
+
|
|
250
|
+
// Status badge - only show if we have status text
|
|
251
|
+
if (statusText) {
|
|
252
|
+
const statusBadge = document.createElement('span');
|
|
253
|
+
statusBadge.className = `status-badge ${statusClass}`;
|
|
254
|
+
statusBadge.textContent = statusText;
|
|
255
|
+
clientItem.appendChild(statusBadge);
|
|
256
|
+
}
|
|
233
257
|
|
|
234
258
|
// Add client item to target div
|
|
235
259
|
targetDiv.appendChild(clientItem);
|
|
@@ -260,6 +284,9 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
260
284
|
noEnvMessage.textContent = 'No environment variables required for this MCP server.';
|
|
261
285
|
envInputsDiv.appendChild(noEnvMessage);
|
|
262
286
|
} else {
|
|
287
|
+
// Get clientMcpSettings from the targets endpoint
|
|
288
|
+
let clientSettings = targetData.clientMcpSettings;
|
|
289
|
+
|
|
263
290
|
// Create inputs for each environment variable
|
|
264
291
|
Object.keys(envRequirements).forEach(key => {
|
|
265
292
|
const req = envRequirements[key];
|
|
@@ -277,7 +304,21 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
277
304
|
input.id = inputId;
|
|
278
305
|
input.name = key;
|
|
279
306
|
input.placeholder = req.Description || key;
|
|
280
|
-
|
|
307
|
+
|
|
308
|
+
// For default value, first check MSRooCode for the target server, then use the provided default
|
|
309
|
+
let defaultValue = req.Default || '';
|
|
310
|
+
|
|
311
|
+
// Check if we have settings from MSRooCode
|
|
312
|
+
if (clientSettings && clientSettings.MSRooCode &&
|
|
313
|
+
clientSettings.MSRooCode.mcpServers &&
|
|
314
|
+
clientSettings.MSRooCode.mcpServers[serverName] &&
|
|
315
|
+
clientSettings.MSRooCode.mcpServers[serverName].env &&
|
|
316
|
+
clientSettings.MSRooCode.mcpServers[serverName].env[key]) {
|
|
317
|
+
defaultValue = clientSettings.MSRooCode.mcpServers[serverName].env[key];
|
|
318
|
+
console.log(`Using MSRooCode value for ${key}: ${defaultValue}`);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
input.value = defaultValue;
|
|
281
322
|
input.required = req.Required;
|
|
282
323
|
input.className = 'input-field';
|
|
283
324
|
|
|
@@ -302,10 +343,36 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
302
343
|
if (serverRequirements.length > 0) {
|
|
303
344
|
const reqHtml = serverRequirements.map(req => {
|
|
304
345
|
const status = requirements[req.name] || {};
|
|
305
|
-
|
|
346
|
+
let statusClass = status.installed
|
|
306
347
|
? 'text-green-600 bg-green-50'
|
|
307
348
|
: 'text-yellow-600 bg-yellow-50';
|
|
308
|
-
|
|
349
|
+
let statusText = status.installed ? 'Installed' : 'Required';
|
|
350
|
+
let versionDisplay = status.version ? ` • <span class="font-medium">${status.version}</span>` : '';
|
|
351
|
+
let updateToggle = '';
|
|
352
|
+
|
|
353
|
+
// Check if there's an available update
|
|
354
|
+
if (status.installed && status.availableUpdate && status.availableUpdate.version) {
|
|
355
|
+
if (status.version && compareVersions(status.availableUpdate.version, status.version) > 0) {
|
|
356
|
+
// Show version update information with yellow color and icon
|
|
357
|
+
statusClass = 'text-yellow-600 bg-yellow-50';
|
|
358
|
+
statusText = `<span style="color: #f59e0b; font-weight: bold; margin-right: 5px;">↑</span>${status.availableUpdate.version}`;
|
|
359
|
+
|
|
360
|
+
// Create a toggle switch for update
|
|
361
|
+
updateToggle = `
|
|
362
|
+
<label class="inline-flex items-center cursor-pointer ml-2">
|
|
363
|
+
<input type="checkbox" class="toggle-update sr-only"
|
|
364
|
+
data-name="${req.name}"
|
|
365
|
+
data-version="${status.availableUpdate.version}"
|
|
366
|
+
data-category="${categoryName}"
|
|
367
|
+
data-server="${serverName}">
|
|
368
|
+
<div class="relative w-10 h-5 bg-gray-200 rounded-full toggle-bg">
|
|
369
|
+
<div class="absolute inset-y-0 left-0 w-5 h-5 bg-white rounded-full transition-transform duration-300 transform"></div>
|
|
370
|
+
</div>
|
|
371
|
+
<span class="ml-2 text-sm text-gray-700">Update</span>
|
|
372
|
+
</label>
|
|
373
|
+
`;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
309
376
|
|
|
310
377
|
return `
|
|
311
378
|
<div class="border border-gray-200 p-3 rounded-lg mb-2 hover:bg-gray-50">
|
|
@@ -313,12 +380,15 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
313
380
|
<div>
|
|
314
381
|
<div class="font-semibold text-gray-800">${req.name}</div>
|
|
315
382
|
<div class="text-sm text-gray-600 shadow-sm p-1 rounded bg-gray-50">
|
|
316
|
-
<span class="font-medium">${status.type || 'package'}</span>${
|
|
383
|
+
<span class="font-medium">${status.type || 'package'}</span>${versionDisplay}
|
|
317
384
|
</div>
|
|
318
385
|
</div>
|
|
319
|
-
<
|
|
320
|
-
${
|
|
321
|
-
|
|
386
|
+
<div class="flex items-center">
|
|
387
|
+
<span class="${statusClass} inline-flex items-center px-3 py-1 rounded-full text-sm">
|
|
388
|
+
${statusText}
|
|
389
|
+
</span>
|
|
390
|
+
${updateToggle}
|
|
391
|
+
</div>
|
|
322
392
|
</div>
|
|
323
393
|
</div>
|
|
324
394
|
`;
|
|
@@ -333,6 +403,24 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
333
403
|
modalRequirements.innerHTML = '<p class="text-gray-600">No additional dependencies required.</p>';
|
|
334
404
|
}
|
|
335
405
|
|
|
406
|
+
// Add event listeners for update toggles
|
|
407
|
+
setTimeout(() => {
|
|
408
|
+
const updateToggles = modalRequirements.querySelectorAll('.toggle-update');
|
|
409
|
+
updateToggles.forEach(toggle => {
|
|
410
|
+
toggle.addEventListener('change', function () {
|
|
411
|
+
// When toggled, update the visual appearance
|
|
412
|
+
const toggleBg = this.parentElement.querySelector('.toggle-bg');
|
|
413
|
+
if (this.checked) {
|
|
414
|
+
toggleBg.classList.add('bg-blue-500');
|
|
415
|
+
toggleBg.querySelector('div').classList.add('translate-x-5');
|
|
416
|
+
} else {
|
|
417
|
+
toggleBg.classList.remove('bg-blue-500');
|
|
418
|
+
toggleBg.querySelector('div').classList.remove('translate-x-5');
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
}, 100);
|
|
423
|
+
|
|
336
424
|
// Set up the install form submit handler
|
|
337
425
|
const installForm = document.getElementById('installForm');
|
|
338
426
|
installForm.onsubmit = (e) => {
|
|
@@ -347,13 +435,27 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
347
435
|
}
|
|
348
436
|
});
|
|
349
437
|
|
|
438
|
+
// Check for enabled update toggles and collect requirements to update
|
|
439
|
+
const requirementsToUpdate = [];
|
|
440
|
+
const updateToggles = modalRequirements.querySelectorAll('.toggle-update:checked');
|
|
441
|
+
updateToggles.forEach(toggle => {
|
|
442
|
+
requirementsToUpdate.push({
|
|
443
|
+
name: toggle.dataset.name,
|
|
444
|
+
version: toggle.dataset.version
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
|
|
350
448
|
// Get selected clients
|
|
351
449
|
const selectedTargets = window.selectedClients.length > 0 ?
|
|
352
450
|
window.selectedClients :
|
|
353
451
|
Array.from(document.querySelectorAll('.client-item.selected'))
|
|
354
452
|
.map(item => item.dataset.target);
|
|
355
453
|
|
|
356
|
-
if
|
|
454
|
+
// Check if we have any requirements selected for update
|
|
455
|
+
const hasRequirementsToUpdate = requirementsToUpdate.length > 0;
|
|
456
|
+
|
|
457
|
+
// Only require client selection if we don't have any requirements to update
|
|
458
|
+
if (selectedTargets.length === 0 && !hasRequirementsToUpdate) {
|
|
357
459
|
showToast('Please select at least one client to configure.', 'error');
|
|
358
460
|
return;
|
|
359
461
|
}
|
|
@@ -367,7 +469,19 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
367
469
|
const msg = serverStatus.installedStatus?.[target]?.message;
|
|
368
470
|
if (msg) installingMessage = msg;
|
|
369
471
|
}
|
|
370
|
-
|
|
472
|
+
|
|
473
|
+
// Add requirements to update to serverInstallOptions if any
|
|
474
|
+
const serverInstallOptions = {
|
|
475
|
+
targetClients: selectedTargets,
|
|
476
|
+
env: envVars
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// Only add requirements if we have any to update
|
|
480
|
+
if (requirementsToUpdate.length > 0) {
|
|
481
|
+
serverInstallOptions.requirements = requirementsToUpdate;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
handleBulkClientInstall(categoryName, serverName, selectedTargets, envVars, installingMessage, serverData, serverInstallOptions);
|
|
371
485
|
};
|
|
372
486
|
|
|
373
487
|
} catch (error) {
|
|
@@ -379,8 +493,8 @@ async function showInstallModal(categoryName, serverName, callback) {
|
|
|
379
493
|
}
|
|
380
494
|
|
|
381
495
|
// Function to handle bulk client installations
|
|
382
|
-
async function handleBulkClientInstall(categoryName, serverName, targets, envVars = {}, installingMessage = "Starting installation...", serverData = null) {
|
|
383
|
-
console.log('[LoadingModal] handleBulkClientInstall called', { categoryName, serverName, targets, envVars });
|
|
496
|
+
async function handleBulkClientInstall(categoryName, serverName, targets, envVars = {}, installingMessage = "Starting installation...", serverData = null, serverInstallOptions = null) {
|
|
497
|
+
console.log('[LoadingModal] handleBulkClientInstall called', { categoryName, serverName, targets, envVars, serverInstallOptions });
|
|
384
498
|
// Hide install modal, show loading modal
|
|
385
499
|
const installModal = document.getElementById('installModal');
|
|
386
500
|
console.log('[LoadingModal] installModal:', installModal);
|
|
@@ -401,26 +515,29 @@ async function handleBulkClientInstall(categoryName, serverName, targets, envVar
|
|
|
401
515
|
}
|
|
402
516
|
}
|
|
403
517
|
|
|
404
|
-
showInstallLoadingModal(
|
|
405
|
-
|
|
518
|
+
showInstallLoadingModal();
|
|
406
519
|
delayedAppendInstallLoadingMessage(installingMessage);
|
|
407
520
|
|
|
408
521
|
try {
|
|
409
522
|
delayedAppendInstallLoadingMessage("Installing, please wait...");
|
|
523
|
+
|
|
524
|
+
// Use serverInstallOptions if provided, otherwise build the traditional options
|
|
525
|
+
const requestBody = {
|
|
526
|
+
serverList: {
|
|
527
|
+
[serverName]: serverInstallOptions || {
|
|
528
|
+
targetClients: targets,
|
|
529
|
+
env: envVars
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
|
|
410
534
|
const response = await fetch(`/api/categories/${categoryName}/install`, {
|
|
411
535
|
method: 'POST',
|
|
412
536
|
headers: {
|
|
413
537
|
'Content-Type': 'application/json',
|
|
414
538
|
'Accept': 'application/json'
|
|
415
539
|
},
|
|
416
|
-
body: JSON.stringify(
|
|
417
|
-
serverList: {
|
|
418
|
-
[serverName]: {
|
|
419
|
-
targetClients: targets,
|
|
420
|
-
env: envVars
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
})
|
|
540
|
+
body: JSON.stringify(requestBody)
|
|
424
541
|
});
|
|
425
542
|
|
|
426
543
|
console.log('[LoadingModal] fetch install response:', response);
|
|
@@ -441,8 +558,9 @@ async function handleBulkClientInstall(categoryName, serverName, targets, envVar
|
|
|
441
558
|
}
|
|
442
559
|
|
|
443
560
|
// Optionally, you can refresh modal data here or trigger a callback
|
|
444
|
-
// Start polling for install status
|
|
445
|
-
|
|
561
|
+
// Start polling for install status, pass requirements if available
|
|
562
|
+
const requirements = serverInstallOptions?.requirements || [];
|
|
563
|
+
pollInstallStatus(categoryName, serverName, targets, 2000, 60, requirements);
|
|
446
564
|
} catch (error) {
|
|
447
565
|
console.error('[LoadingModal] Error applying configuration:', error);
|
|
448
566
|
delayedAppendInstallLoadingMessage(`<span style="color:red;">Error: ${error.message}</span>`);
|
|
@@ -455,33 +573,86 @@ async function handleBulkClientInstall(categoryName, serverName, targets, envVar
|
|
|
455
573
|
}
|
|
456
574
|
}
|
|
457
575
|
|
|
458
|
-
// Poll install status for the given server/targets
|
|
459
|
-
async function pollInstallStatus(categoryName, serverName, targets, interval = 2000, maxTries = 60) {
|
|
576
|
+
// Poll install status for the given server/targets and optional requirements
|
|
577
|
+
async function pollInstallStatus(categoryName, serverName, targets, interval = 2000, maxTries = 60, requirements = []) {
|
|
460
578
|
let tries = 0;
|
|
461
579
|
let lastMessages = {};
|
|
580
|
+
let lastRequirementMessages = {};
|
|
462
581
|
// Use global delayedAppendInstallLoadingMessage and queue logic
|
|
463
582
|
while (tries < maxTries) {
|
|
464
583
|
try {
|
|
465
584
|
const resp = await fetch(`/api/categories/${categoryName}`);
|
|
466
585
|
if (resp.ok) {
|
|
467
586
|
const data = await resp.json();
|
|
468
|
-
const
|
|
587
|
+
const installationStatus = data?.data?.installationStatus || {};
|
|
588
|
+
const serverStatuses = installationStatus.serversStatus || {};
|
|
589
|
+
const requirementsStatus = installationStatus.requirementsStatus || {};
|
|
469
590
|
const serverStatus = serverStatuses[serverName] || { installedStatus: {} };
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
591
|
+
|
|
592
|
+
// First check requirements status if we have any
|
|
593
|
+
let allRequirementsCompleted = true;
|
|
594
|
+
let hasRequirements = requirements.length > 0;
|
|
595
|
+
|
|
596
|
+
for (const req of requirements) {
|
|
597
|
+
const reqStatus = requirementsStatus[req.name] || {};
|
|
598
|
+
const opStatus = reqStatus.operationStatus || {};
|
|
599
|
+
const msg = opStatus.message;
|
|
600
|
+
const status = opStatus.status;
|
|
601
|
+
|
|
602
|
+
// Only append new messages for requirements
|
|
603
|
+
if (msg && lastRequirementMessages[req.name] !== msg) {
|
|
604
|
+
delayedAppendInstallLoadingMessage(`[Requirement: ${req.name}] ${msg}`);
|
|
605
|
+
lastRequirementMessages[req.name] = msg;
|
|
478
606
|
}
|
|
479
|
-
|
|
480
|
-
|
|
607
|
+
|
|
608
|
+
if (status !== "completed" && status !== "failed") {
|
|
609
|
+
allRequirementsCompleted = false;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// If a requirement failed, show an error
|
|
613
|
+
if (status === "failed") {
|
|
614
|
+
delayedAppendInstallLoadingMessage(`<span style="color:red;">Requirement update failed: ${req.name} - ${msg || 'Unknown error'}</span>`);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Now check target statuses
|
|
619
|
+
let allTargetsCompleted = true;
|
|
620
|
+
let hasTargets = targets && targets.length > 0;
|
|
621
|
+
|
|
622
|
+
if (hasTargets) {
|
|
623
|
+
for (const target of targets) {
|
|
624
|
+
const status = serverStatus.installedStatus?.[target]?.status;
|
|
625
|
+
const msg = serverStatus.installedStatus?.[target]?.message;
|
|
626
|
+
// Only append new messages for targets
|
|
627
|
+
if (msg && lastMessages[target] !== msg) {
|
|
628
|
+
delayedAppendInstallLoadingMessage(`[Target: ${target}] ${msg}`);
|
|
629
|
+
lastMessages[target] = msg;
|
|
630
|
+
}
|
|
631
|
+
if (status !== "completed") {
|
|
632
|
+
allTargetsCompleted = false;
|
|
633
|
+
}
|
|
481
634
|
}
|
|
482
635
|
}
|
|
636
|
+
|
|
637
|
+
// Complete if all operations are done
|
|
638
|
+
const allCompleted = (!hasRequirements || allRequirementsCompleted) &&
|
|
639
|
+
(!hasTargets || allTargetsCompleted);
|
|
640
|
+
|
|
483
641
|
if (allCompleted) {
|
|
484
|
-
|
|
642
|
+
// Compose completion message based on what was processed
|
|
643
|
+
let completionMessage = '';
|
|
644
|
+
|
|
645
|
+
if (hasRequirements && hasTargets) {
|
|
646
|
+
completionMessage = `Requirements updated and configuration applied successfully for ${targets.length} client(s).`;
|
|
647
|
+
} else if (hasRequirements) {
|
|
648
|
+
completionMessage = `Requirements updated successfully.`;
|
|
649
|
+
} else if (hasTargets) {
|
|
650
|
+
completionMessage = `Configuration applied successfully for ${targets.length} client(s).`;
|
|
651
|
+
} else {
|
|
652
|
+
completionMessage = `Operation completed successfully.`;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
delayedAppendInstallLoadingMessage(`<span style="color:green;">${completionMessage}</span>`);
|
|
485
656
|
setTimeout(() => {
|
|
486
657
|
hideInstallLoadingModal();
|
|
487
658
|
setTimeout(() => {
|
|
@@ -568,5 +739,19 @@ window.showInstallLoadingModal = showInstallLoadingModal;
|
|
|
568
739
|
window.appendInstallLoadingMessage = appendInstallLoadingMessage;
|
|
569
740
|
window.hideInstallLoadingModal = hideInstallLoadingModal;
|
|
570
741
|
|
|
742
|
+
// CSS styles for the toggle switch
|
|
743
|
+
const styleElement = document.createElement('style');
|
|
744
|
+
styleElement.textContent = `
|
|
745
|
+
.toggle-bg.bg-blue-500 {
|
|
746
|
+
background-color: #3b82f6;
|
|
747
|
+
}
|
|
748
|
+
.toggle-bg {
|
|
749
|
+
transition: background-color 0.3s;
|
|
750
|
+
}
|
|
751
|
+
.toggle-bg div {
|
|
752
|
+
transition: transform 0.3s;
|
|
753
|
+
}
|
|
754
|
+
`;
|
|
755
|
+
document.head.appendChild(styleElement);
|
|
571
756
|
|
|
572
757
|
export { showInstallModal, closeModal, setupModalOutsideClick, uninstallTools };
|
package/dist/web/server.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { ServerInstallOptions } from '../core/types.js';
|
|
2
|
+
export interface UpdateRequirementsRequestBody {
|
|
3
|
+
requirements: {
|
|
4
|
+
name: string;
|
|
5
|
+
updateVersion: string;
|
|
6
|
+
}[];
|
|
7
|
+
}
|
|
2
8
|
export interface InstallServersRequestBody {
|
|
3
9
|
serverList: Record<string, ServerInstallOptions>;
|
|
4
10
|
}
|
package/dist/web/server.js
CHANGED
|
@@ -5,6 +5,7 @@ import { SUPPORTED_CLIENT_NAMES } from '../core/constants.js';
|
|
|
5
5
|
import { serverService } from '../services/ServerService.js';
|
|
6
6
|
import { openBrowser } from '../utils/osUtils.js';
|
|
7
7
|
import { Logger } from '../utils/logger.js';
|
|
8
|
+
import { configProvider } from '../core/ConfigurationProvider.js';
|
|
8
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
10
|
const app = express();
|
|
10
11
|
// Middleware
|
|
@@ -13,9 +14,13 @@ app.use(express.json());
|
|
|
13
14
|
// Get available targets
|
|
14
15
|
app.get('/api/targets', async (req, res) => {
|
|
15
16
|
try {
|
|
17
|
+
// Get clientMcpSettings
|
|
18
|
+
const clientMcpSettings = await configProvider.getClientMcpSettings();
|
|
19
|
+
// Keep original format but add clientMcpSettings as additional property
|
|
16
20
|
const response = {
|
|
17
21
|
success: true,
|
|
18
|
-
data: SUPPORTED_CLIENT_NAMES
|
|
22
|
+
data: SUPPORTED_CLIENT_NAMES,
|
|
23
|
+
clientMcpSettings: clientMcpSettings
|
|
19
24
|
};
|
|
20
25
|
res.json(response);
|
|
21
26
|
}
|
package/package.json
CHANGED
|
@@ -27,11 +27,6 @@ Examples:
|
|
|
27
27
|
'--name <name>',
|
|
28
28
|
'Server name to install'
|
|
29
29
|
)
|
|
30
|
-
.option(
|
|
31
|
-
'--force',
|
|
32
|
-
'Force installation even if server already exists',
|
|
33
|
-
false
|
|
34
|
-
)
|
|
35
30
|
.option(
|
|
36
31
|
'--clients <clients>',
|
|
37
32
|
'Target clients (semicolon separated). Supported values: Cline, MSRooCode, GithubCopilot. If not specified, installs for all clients'
|
|
@@ -43,7 +38,6 @@ Examples:
|
|
|
43
38
|
.action(async (options: {
|
|
44
39
|
category: string;
|
|
45
40
|
name: string;
|
|
46
|
-
force: boolean;
|
|
47
41
|
clients?: string;
|
|
48
42
|
envs?: string;
|
|
49
43
|
verbose?: boolean;
|
|
@@ -55,19 +49,22 @@ Examples:
|
|
|
55
49
|
Logger.log('Local feeds not found, syncing from remote...');
|
|
56
50
|
await serverService.syncFeeds();
|
|
57
51
|
}
|
|
58
|
-
|
|
59
|
-
const { category, name, verbose, force, clients, envs } = options;
|
|
52
|
+
const { category, name, verbose, clients, envs } = options;
|
|
60
53
|
|
|
61
|
-
Logger.debug(`Install options: ${JSON.stringify({ category, name,
|
|
54
|
+
Logger.debug(`Install options: ${JSON.stringify({ category, name, verbose, clients, envs })}`);
|
|
62
55
|
|
|
63
56
|
const serverName = name.trim();
|
|
64
57
|
Logger.debug(`Server name: ${serverName}`);
|
|
65
58
|
|
|
66
59
|
if (!await serverService.validateServerName(category, serverName)) {
|
|
67
|
-
Logger.error(
|
|
68
|
-
category
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
Logger.error(
|
|
61
|
+
'Invalid server name or category provided.\n' +
|
|
62
|
+
'This could be because:\n' +
|
|
63
|
+
' 1. The server name or category is misspelled\n' +
|
|
64
|
+
' 2. Your local feeds are outdated\n\n' +
|
|
65
|
+
'Try running "imcp pull" to update your local feeds from remote.',
|
|
66
|
+
{ category, serverName }
|
|
67
|
+
);
|
|
71
68
|
process.exit(1);
|
|
72
69
|
}
|
|
73
70
|
|
|
@@ -111,7 +108,7 @@ Examples:
|
|
|
111
108
|
Logger.log(`Installing server: ${serverName}`);
|
|
112
109
|
|
|
113
110
|
const installOptions: ServerInstallOptions = {
|
|
114
|
-
force:
|
|
111
|
+
force: false,
|
|
115
112
|
...(parsedClients?.length && { targetClients: parsedClients }),
|
|
116
113
|
...(Object.keys(parsedEnvs).length > 0 && { env: parsedEnvs })
|
|
117
114
|
};
|
package/src/cli/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import axios from 'axios';
|
|
|
12
12
|
import path from 'path';
|
|
13
13
|
import { fileURLToPath } from 'url';
|
|
14
14
|
import fs from 'fs';
|
|
15
|
+
import { compareVersions } from '../utils/versionUtils.js';
|
|
15
16
|
|
|
16
17
|
// Custom error interface for Commander.js errors
|
|
17
18
|
// ANSI color codes
|
|
@@ -32,7 +33,6 @@ async function main(): Promise<void> {
|
|
|
32
33
|
program
|
|
33
34
|
.name('imcp')
|
|
34
35
|
.description('IMCP (Install Model Context Protocol) CLI')
|
|
35
|
-
.version('0.0.1')
|
|
36
36
|
.option('--verbose', 'Show detailed logs for all commands');
|
|
37
37
|
|
|
38
38
|
// Parse global options first
|
|
@@ -84,6 +84,7 @@ process.on('unhandledRejection', (error: unknown) => {
|
|
|
84
84
|
/**
|
|
85
85
|
* Check if there's a newer version of the package available
|
|
86
86
|
*/
|
|
87
|
+
|
|
87
88
|
async function checkForUpdates(): Promise<void> {
|
|
88
89
|
try {
|
|
89
90
|
// Get the current package version
|
|
@@ -101,7 +102,8 @@ async function checkForUpdates(): Promise<void> {
|
|
|
101
102
|
if (npmResponse.data && npmResponse.data['dist-tags'] && npmResponse.data['dist-tags'].latest) {
|
|
102
103
|
const latestVersion = npmResponse.data['dist-tags'].latest;
|
|
103
104
|
|
|
104
|
-
|
|
105
|
+
// Compare versions properly to ensure we're only notifying for newer versions
|
|
106
|
+
if (latestVersion && compareVersions(latestVersion, currentVersion) > 0) {
|
|
105
107
|
console.log(`${COLORS.yellow}Update available for ${packageName}: ${currentVersion} → ${latestVersion}${COLORS.reset}`);
|
|
106
108
|
console.log(`${COLORS.yellow}Run \`npm install -g ${packageName}@latest\` to update${COLORS.reset}`);
|
|
107
109
|
}
|