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
package/dist/cli/index.js
CHANGED
|
@@ -2,17 +2,7 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { createServeCommand } from './commands/serve.js';
|
|
4
4
|
import { Logger } from '../utils/logger.js';
|
|
5
|
-
import
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { fileURLToPath } from 'url';
|
|
8
|
-
import fs from 'fs';
|
|
9
|
-
import { compareVersions } from '../utils/versionUtils.js';
|
|
10
|
-
// Custom error interface for Commander.js errors
|
|
11
|
-
// ANSI color codes
|
|
12
|
-
const COLORS = {
|
|
13
|
-
reset: '\x1b[0m',
|
|
14
|
-
yellow: '\x1b[33m'
|
|
15
|
-
};
|
|
5
|
+
import { checkForUpdates } from '../utils/versionUtils.js';
|
|
16
6
|
async function main() {
|
|
17
7
|
// Initialize the MCP manager
|
|
18
8
|
// await mcpManager.initialize();
|
|
@@ -64,40 +54,6 @@ process.on('unhandledRejection', (error) => {
|
|
|
64
54
|
}
|
|
65
55
|
process.exit(1);
|
|
66
56
|
});
|
|
67
|
-
/**
|
|
68
|
-
* Check if there's a newer version of the package available
|
|
69
|
-
*/
|
|
70
|
-
async function checkForUpdates() {
|
|
71
|
-
try {
|
|
72
|
-
// Get the current package version
|
|
73
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
74
|
-
const __dirname = path.dirname(__filename);
|
|
75
|
-
const packagePath = path.resolve(__dirname, '../../package.json');
|
|
76
|
-
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
77
|
-
const currentVersion = packageJson.name && packageJson.version ? packageJson.version : '0.0.0';
|
|
78
|
-
const packageName = packageJson.name || 'imcp';
|
|
79
|
-
try {
|
|
80
|
-
// Get the latest version from npm registry (only for published packages)
|
|
81
|
-
const npmResponse = await axios.get(`https://registry.npmjs.org/${packageName}`);
|
|
82
|
-
if (npmResponse.data && npmResponse.data['dist-tags'] && npmResponse.data['dist-tags'].latest) {
|
|
83
|
-
const latestVersion = npmResponse.data['dist-tags'].latest;
|
|
84
|
-
// Compare versions properly to ensure we're only notifying for newer versions
|
|
85
|
-
if (latestVersion && compareVersions(latestVersion, currentVersion) > 0) {
|
|
86
|
-
console.log(`${COLORS.yellow}Update available for ${packageName}: ${currentVersion} → ${latestVersion}${COLORS.reset}`);
|
|
87
|
-
console.log(`${COLORS.yellow}Run \`npm install -g ${packageName}@latest\` to update${COLORS.reset}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
catch (npmError) {
|
|
92
|
-
// Log the npm error
|
|
93
|
-
Logger.debug(`Failed to check npm registry: ${npmError instanceof Error ? npmError.message : String(npmError)}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
catch (error) {
|
|
97
|
-
// Silently fail - don't interrupt the command if update check fails
|
|
98
|
-
Logger.debug(`Failed to check for updates: ${error instanceof Error ? error.message : String(error)}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
57
|
// Start the CLI
|
|
102
58
|
main().catch((error) => {
|
|
103
59
|
if (error instanceof Error) {
|
|
@@ -40,10 +40,6 @@ export declare abstract class BaseClientInstaller {
|
|
|
40
40
|
* Override in child classes to provide custom SSE configuration
|
|
41
41
|
*/
|
|
42
42
|
protected handleSseMode(settings: any, serverName: string, installConfig: any): void;
|
|
43
|
-
/**
|
|
44
|
-
* Get the NPM path on Windows
|
|
45
|
-
*/
|
|
46
|
-
private getNpmPath;
|
|
47
43
|
/**
|
|
48
44
|
* Checks if VS Code or VS Code Insiders is installed and installs the client extension.
|
|
49
45
|
* @param operationId The operation ID for tracking.
|
|
@@ -66,7 +62,7 @@ export declare abstract class BaseClientInstaller {
|
|
|
66
62
|
* @param serverConfig Server configuration
|
|
67
63
|
* @param options Installation options including environment variables and arguments
|
|
68
64
|
*/
|
|
69
|
-
install(serverConfig: McpConfig, options: ServerInstallOptions): Promise<OperationStatus>;
|
|
65
|
+
install(serverConfig: McpConfig, options: ServerInstallOptions, categoryName?: string): Promise<OperationStatus>;
|
|
70
66
|
/**
|
|
71
67
|
* Abstract method that must be implemented by client-specific installers
|
|
72
68
|
*/
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Logger } from '../../../utils/logger.js';
|
|
2
2
|
import { exec } from 'child_process';
|
|
3
3
|
import { promisify } from 'util';
|
|
4
|
-
import { isCommandAvailable } from '../../../utils/osUtils.js';
|
|
4
|
+
import { isCommandAvailable, getNpmExecutablePath } from '../../../utils/osUtils.js';
|
|
5
5
|
import { ExtensionInstaller } from './ExtensionInstaller.js';
|
|
6
6
|
import { SUPPORTED_CLIENTS } from '../../metadatas/constants.js';
|
|
7
7
|
import { MACRO_EXPRESSIONS, MacroResolverFunctions } from '../../../utils/macroExpressionUtils.js';
|
|
8
|
+
import { InstallOperationManager } from '../../loaders/InstallOperationManager.js';
|
|
9
|
+
import * as RecordingConstants from '../../metadatas/recordingConstants.js';
|
|
8
10
|
const execAsync = promisify(exec);
|
|
9
11
|
/**
|
|
10
12
|
* Base class for client installers with shared functionality
|
|
@@ -96,7 +98,7 @@ export class BaseClientInstaller {
|
|
|
96
98
|
*/
|
|
97
99
|
async handleWindowsNpx(config) {
|
|
98
100
|
if (process.platform === 'win32' && config.command === 'npx') {
|
|
99
|
-
const npmPath = await
|
|
101
|
+
const npmPath = await getNpmExecutablePath();
|
|
100
102
|
return {
|
|
101
103
|
...config,
|
|
102
104
|
command: 'cmd',
|
|
@@ -146,19 +148,6 @@ export class BaseClientInstaller {
|
|
|
146
148
|
url: installConfig.url
|
|
147
149
|
};
|
|
148
150
|
}
|
|
149
|
-
/**
|
|
150
|
-
* Get the NPM path on Windows
|
|
151
|
-
*/
|
|
152
|
-
async getNpmPath() {
|
|
153
|
-
try {
|
|
154
|
-
const { stdout } = await execAsync('powershell -Command "get-command npm | Select-Object -ExpandProperty Source"');
|
|
155
|
-
return stdout.trim().replace(/\\npm\.cmd$/, '');
|
|
156
|
-
}
|
|
157
|
-
catch (error) {
|
|
158
|
-
Logger.error('Error getting npm path:', error);
|
|
159
|
-
return 'C:\\Program Files\\nodejs';
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
151
|
/**
|
|
163
152
|
* Checks if VS Code or VS Code Insiders is installed and installs the client extension.
|
|
164
153
|
* @param operationId The operation ID for tracking.
|
|
@@ -238,44 +227,57 @@ export class BaseClientInstaller {
|
|
|
238
227
|
* @param serverConfig Server configuration
|
|
239
228
|
* @param options Installation options including environment variables and arguments
|
|
240
229
|
*/
|
|
241
|
-
async install(serverConfig, options) {
|
|
230
|
+
async install(serverConfig, options, categoryName) {
|
|
242
231
|
const operationId = this.generateOperationId();
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
232
|
+
const recorder = InstallOperationManager.getInstance(categoryName || serverConfig.name, serverConfig.name);
|
|
233
|
+
return await recorder.recording(async () => {
|
|
234
|
+
await recorder.recording(() => this.checkVSCodeAndInstallExtension(operationId), {
|
|
235
|
+
stepName: RecordingConstants.STEP_CHECK_VSCODE_AND_INSTALL_EXTENSION,
|
|
236
|
+
onResult: (result) => result?.status !== 'failed'
|
|
237
|
+
});
|
|
238
|
+
const installConfig = await recorder.recording(() => this.setupInstallConfig(serverConfig, options), {
|
|
239
|
+
stepName: RecordingConstants.STEP_SETUP_INSTALLATION_CONFIG
|
|
240
|
+
});
|
|
249
241
|
if (serverConfig.mode) {
|
|
250
242
|
installConfig.mode = serverConfig.mode;
|
|
251
243
|
}
|
|
252
|
-
|
|
253
|
-
|
|
244
|
+
const results = await recorder.recording(() => this.updateVSCodeSettings(serverConfig.name, installConfig), {
|
|
245
|
+
stepName: RecordingConstants.STEP_UPDATE_VSCODE_SETTINGS,
|
|
246
|
+
onResult: (result) => result?.some(r => r.success)
|
|
247
|
+
});
|
|
254
248
|
// Determine overall success
|
|
255
249
|
const anySuccess = results.some(r => r.success);
|
|
256
250
|
const successPaths = results.filter(r => r.success).map(r => r.path);
|
|
257
251
|
const errors = results.filter(r => !r.success).map(r => r.error);
|
|
252
|
+
const finalMessage = anySuccess
|
|
253
|
+
? `Successfully installed ${this.clientName} client. Updated settings in: ${successPaths.join(', ')}`
|
|
254
|
+
: `Failed to install ${this.clientName} client. Errors: ${errors.join('; ')}`;
|
|
258
255
|
return {
|
|
259
256
|
status: anySuccess ? 'completed' : 'failed',
|
|
260
257
|
type: 'install',
|
|
261
258
|
target: 'server',
|
|
262
|
-
message:
|
|
263
|
-
? `Successfully installed ${this.clientName} client. Updated settings in: ${successPaths.join(', ')}`
|
|
264
|
-
: `Failed to install ${this.clientName} client. Errors: ${errors.join('; ')}`,
|
|
259
|
+
message: finalMessage,
|
|
265
260
|
operationId,
|
|
266
261
|
error: anySuccess ? undefined : errors.join('; ')
|
|
267
262
|
};
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
263
|
+
}, {
|
|
264
|
+
stepName: RecordingConstants.STEP_INSTALLATION,
|
|
265
|
+
onResult: (result) => result?.status !== 'failed',
|
|
266
|
+
endMessage: (result) => result?.message,
|
|
267
|
+
onError: (error) => {
|
|
268
|
+
const errorMsg = `Unexpected error installing ${this.clientName} client: ${error instanceof Error ? error.message : String(error)}`;
|
|
269
|
+
return {
|
|
270
|
+
result: {
|
|
271
|
+
status: 'failed',
|
|
272
|
+
type: 'install',
|
|
273
|
+
target: 'server',
|
|
274
|
+
message: errorMsg,
|
|
275
|
+
operationId,
|
|
276
|
+
error: error instanceof Error ? error.message : String(error)
|
|
277
|
+
}, message: errorMsg
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
});
|
|
279
281
|
}
|
|
280
282
|
}
|
|
281
283
|
//# sourceMappingURL=BaseClientInstaller.js.map
|
|
@@ -10,21 +10,21 @@ export declare class ClientInstaller {
|
|
|
10
10
|
private configProvider;
|
|
11
11
|
constructor(categoryName: string, serverName: string, clients: string[]);
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*/
|
|
15
|
-
private generateOperationId;
|
|
16
|
-
/**
|
|
17
|
-
* Check if server requirements are ready
|
|
18
|
-
* Waits for requirements to be ready with timeout
|
|
13
|
+
* Install all specified clients
|
|
19
14
|
*/
|
|
20
|
-
|
|
15
|
+
install(options: ServerInstallOptions): Promise<ServerOperationResult>;
|
|
21
16
|
/**
|
|
22
17
|
* Install client with requirements checking
|
|
23
18
|
*/
|
|
24
19
|
private installClient;
|
|
25
20
|
private processInstallation;
|
|
26
21
|
/**
|
|
27
|
-
|
|
22
|
+
* Generate a unique operation ID for tracking installations
|
|
23
|
+
*/
|
|
24
|
+
private generateOperationId;
|
|
25
|
+
/**
|
|
26
|
+
* Check if server requirements are ready
|
|
27
|
+
* Waits for requirements to be ready with timeout
|
|
28
28
|
*/
|
|
29
|
-
|
|
29
|
+
private checkRequirements;
|
|
30
30
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ConfigurationProvider } from '../../loaders/ConfigurationProvider.js';
|
|
2
|
+
import { Logger } from '../../../utils/logger.js';
|
|
2
3
|
import { ClientInstallerFactory } from './ClientInstallerFactory.js';
|
|
4
|
+
import { InstallOperationManager } from '../../loaders/InstallOperationManager.js';
|
|
3
5
|
/**
|
|
4
6
|
* Main client installer class that orchestrates client installation process
|
|
5
7
|
* Handles requirements checking and delegates to specific client installers
|
|
@@ -16,43 +18,24 @@ export class ClientInstaller {
|
|
|
16
18
|
this.configProvider = ConfigurationProvider.getInstance();
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
|
-
*
|
|
20
|
-
*/
|
|
21
|
-
generateOperationId() {
|
|
22
|
-
return `install-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Check if server requirements are ready
|
|
26
|
-
* Waits for requirements to be ready with timeout
|
|
21
|
+
* Install all specified clients
|
|
27
22
|
*/
|
|
28
|
-
async
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
45
|
-
requirementsReady = await this.configProvider.isRequirementsReady(this.categoryName, this.serverName);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
var requirementsStatus = await this.configProvider.GetServerRequirementStatus(this.categoryName, this.serverName);
|
|
49
|
-
// Find first non-empty npmPath from requirements status
|
|
50
|
-
const npmPathRequirement = requirementsStatus.find(status => status.npmPath && status.npmPath.length > 0);
|
|
51
|
-
if (npmPathRequirement && npmPathRequirement.npmPath) {
|
|
52
|
-
options.settings = options.settings || {};
|
|
53
|
-
options.settings.npmPath = npmPathRequirement.npmPath;
|
|
54
|
-
}
|
|
55
|
-
return requirementsReady;
|
|
23
|
+
async install(options) {
|
|
24
|
+
const initialStatuses = [];
|
|
25
|
+
// Start installation for each client asynchronously
|
|
26
|
+
const installPromises = this.clients.map(async (clientName) => {
|
|
27
|
+
const status = await this.installClient(clientName, options);
|
|
28
|
+
initialStatuses.push(status);
|
|
29
|
+
return status;
|
|
30
|
+
});
|
|
31
|
+
// Wait for all installations to complete
|
|
32
|
+
await Promise.all(installPromises);
|
|
33
|
+
// Return result
|
|
34
|
+
return {
|
|
35
|
+
success: true,
|
|
36
|
+
message: 'Client installations completed',
|
|
37
|
+
status: initialStatuses
|
|
38
|
+
};
|
|
56
39
|
}
|
|
57
40
|
/**
|
|
58
41
|
* Install client with requirements checking
|
|
@@ -77,82 +60,105 @@ export class ClientInstaller {
|
|
|
77
60
|
message: `Initializing installation for client: ${clientName}`,
|
|
78
61
|
operationId
|
|
79
62
|
};
|
|
80
|
-
|
|
63
|
+
// Async installation process
|
|
64
|
+
this.processInstallation(clientName, operationId, options)
|
|
65
|
+
.then((status) => {
|
|
66
|
+
if (status.status === 'completed' || status.status === 'failed') {
|
|
67
|
+
InstallOperationManager
|
|
68
|
+
.getInstance(this.categoryName, this.serverName)
|
|
69
|
+
.markOverallStatus(status.status);
|
|
70
|
+
}
|
|
71
|
+
this.configProvider.updateServerOperationStatus(this.categoryName, this.serverName, clientName, status);
|
|
72
|
+
})
|
|
73
|
+
.catch((error) => {
|
|
74
|
+
this.configProvider.updateServerOperationStatus(this.categoryName, this.serverName, clientName, {
|
|
75
|
+
status: 'failed',
|
|
76
|
+
type: 'install',
|
|
77
|
+
target: 'server',
|
|
78
|
+
message: `Error installing client ${clientName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
79
|
+
operationId,
|
|
80
|
+
error: error instanceof Error ? error.message : String(error)
|
|
81
|
+
});
|
|
82
|
+
Logger.error(`Error installing client ${clientName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
83
|
+
InstallOperationManager
|
|
84
|
+
.getInstance(this.categoryName, this.serverName)
|
|
85
|
+
.markOverallStatus('failed', error);
|
|
86
|
+
});
|
|
81
87
|
// Update server status with initial client installation status
|
|
82
88
|
await this.configProvider.updateServerOperationStatus(this.categoryName, this.serverName, clientName, initialStatus);
|
|
83
89
|
return initialStatus;
|
|
84
90
|
}
|
|
85
91
|
async processInstallation(clientName, operationId, options) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
if (!requirementsReady) {
|
|
90
|
-
const failedStatus = {
|
|
91
|
-
status: 'failed',
|
|
92
|
-
type: 'install',
|
|
93
|
-
target: 'server',
|
|
94
|
-
message: `Requirements not ready for client: ${clientName} after timeout`,
|
|
95
|
-
operationId
|
|
96
|
-
};
|
|
97
|
-
await this.configProvider.updateServerOperationStatus(this.categoryName, this.serverName, clientName, failedStatus);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
// Create client-specific installer
|
|
101
|
-
const installer = ClientInstallerFactory.getInstaller(clientName);
|
|
102
|
-
if (!installer) {
|
|
103
|
-
throw new Error(`Failed to create installer for client: ${clientName}`);
|
|
104
|
-
}
|
|
105
|
-
const serverConfig = await this.configProvider.getServerMcpConfig(this.categoryName, this.serverName);
|
|
106
|
-
if (!serverConfig) {
|
|
107
|
-
throw new Error(`Server configuration not found for category: ${this.categoryName}, server: ${this.serverName}`);
|
|
108
|
-
}
|
|
109
|
-
// If we've reached here, requirements are ready - update status to in-progress
|
|
110
|
-
const inProgressStatus = {
|
|
111
|
-
status: 'in-progress',
|
|
112
|
-
type: 'install',
|
|
113
|
-
target: 'server',
|
|
114
|
-
message: `Installing client: ${clientName}`,
|
|
115
|
-
operationId: operationId
|
|
116
|
-
};
|
|
117
|
-
await this.configProvider.updateServerOperationStatus(this.categoryName, this.serverName, clientName, inProgressStatus);
|
|
118
|
-
// Install client
|
|
119
|
-
const status = await installer.install(serverConfig, options);
|
|
120
|
-
await this.configProvider.updateServerOperationStatus(this.categoryName, this.serverName, clientName, status);
|
|
121
|
-
if (status.status === 'completed') {
|
|
122
|
-
await this.configProvider.reloadClientMCPSettings();
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
const errorStatus = {
|
|
92
|
+
const requirementsReady = await this.checkRequirements(operationId, clientName, options);
|
|
93
|
+
if (!requirementsReady) {
|
|
94
|
+
const failedStatus = {
|
|
127
95
|
status: 'failed',
|
|
128
96
|
type: 'install',
|
|
129
97
|
target: 'server',
|
|
130
|
-
message: `
|
|
131
|
-
operationId
|
|
132
|
-
error: error instanceof Error ? error.message : String(error)
|
|
98
|
+
message: `Requirements not ready for client: ${clientName} after timeout`,
|
|
99
|
+
operationId
|
|
133
100
|
};
|
|
134
|
-
|
|
101
|
+
return failedStatus;
|
|
102
|
+
}
|
|
103
|
+
// Create client-specific installer
|
|
104
|
+
const installer = ClientInstallerFactory.getInstaller(clientName);
|
|
105
|
+
if (!installer) {
|
|
106
|
+
throw new Error(`Failed to create installer for client: ${clientName}`);
|
|
135
107
|
}
|
|
108
|
+
const serverConfig = await this.configProvider.getServerMcpConfig(this.categoryName, this.serverName);
|
|
109
|
+
if (!serverConfig) {
|
|
110
|
+
throw new Error(`Server configuration not found for category: ${this.categoryName}, server: ${this.serverName}`);
|
|
111
|
+
}
|
|
112
|
+
// If we've reached here, requirements are ready - update status to in-progress
|
|
113
|
+
const inProgressStatus = {
|
|
114
|
+
status: 'in-progress',
|
|
115
|
+
type: 'install',
|
|
116
|
+
target: 'server',
|
|
117
|
+
message: `Installing client: ${clientName}`,
|
|
118
|
+
operationId: operationId
|
|
119
|
+
};
|
|
120
|
+
await this.configProvider.updateServerOperationStatus(this.categoryName, this.serverName, clientName, inProgressStatus);
|
|
121
|
+
// Install client
|
|
122
|
+
return await installer.install(serverConfig, options, this.categoryName);
|
|
136
123
|
}
|
|
137
124
|
/**
|
|
138
|
-
|
|
125
|
+
* Generate a unique operation ID for tracking installations
|
|
126
|
+
*/
|
|
127
|
+
generateOperationId() {
|
|
128
|
+
return `install-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Check if server requirements are ready
|
|
132
|
+
* Waits for requirements to be ready with timeout
|
|
139
133
|
*/
|
|
140
|
-
async
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
134
|
+
async checkRequirements(operationId, clientName, options) {
|
|
135
|
+
let requirementsReady = await this.configProvider.isRequirementsReady(this.categoryName, this.serverName);
|
|
136
|
+
if (!requirementsReady) {
|
|
137
|
+
const pendingStatus = {
|
|
138
|
+
status: 'pending',
|
|
139
|
+
type: 'install',
|
|
140
|
+
target: 'server',
|
|
141
|
+
message: `Waiting for requirements to be ready for client: ${clientName}`,
|
|
142
|
+
operationId
|
|
143
|
+
};
|
|
144
|
+
await this.configProvider.updateServerOperationStatus(this.categoryName, this.serverName, clientName, pendingStatus);
|
|
145
|
+
// Set up periodic checking with timeout
|
|
146
|
+
const startTime = Date.now();
|
|
147
|
+
const timeoutMs = 5 * 60 * 1000; // 5 minutes
|
|
148
|
+
const intervalMs = 5 * 1000; // 5 seconds
|
|
149
|
+
while (!requirementsReady && (Date.now() - startTime) < timeoutMs) {
|
|
150
|
+
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
151
|
+
requirementsReady = await this.configProvider.isRequirementsReady(this.categoryName, this.serverName);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
var requirementsStatus = await this.configProvider.GetServerRequirementStatus(this.categoryName, this.serverName);
|
|
155
|
+
// Find first non-empty npmPath from requirements status
|
|
156
|
+
const npmPathRequirement = requirementsStatus.find(status => status.npmPath && status.npmPath.length > 0);
|
|
157
|
+
if (npmPathRequirement && npmPathRequirement.npmPath) {
|
|
158
|
+
options.settings = options.settings || {};
|
|
159
|
+
options.settings.npmPath = npmPathRequirement.npmPath;
|
|
160
|
+
}
|
|
161
|
+
return requirementsReady;
|
|
156
162
|
}
|
|
157
163
|
}
|
|
158
164
|
//# sourceMappingURL=ClientInstaller.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RequirementConfig, RequirementStatus, ServerInstallOptions } from '../../metadatas/types.js';
|
|
2
2
|
import { RequirementInstaller } from './RequirementInstaller.js';
|
|
3
|
+
import { InstallOperationManager } from '../../loaders/InstallOperationManager.js';
|
|
3
4
|
/**
|
|
4
5
|
* Abstract base class with common functionality for all requirement installers
|
|
5
6
|
*/
|
|
@@ -15,7 +16,14 @@ export declare abstract class BaseInstaller implements RequirementInstaller {
|
|
|
15
16
|
}>);
|
|
16
17
|
abstract canHandle(requirement: RequirementConfig): boolean;
|
|
17
18
|
abstract supportCheckUpdates(): boolean;
|
|
18
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Install the requirement
|
|
21
|
+
* @param requirement The requirement to install
|
|
22
|
+
* @param options Optional install options
|
|
23
|
+
* @param recorder Optional InstallOperationManager for recording steps
|
|
24
|
+
* @returns The status of the installation
|
|
25
|
+
*/
|
|
26
|
+
abstract install(requirement: RequirementConfig, recorder: InstallOperationManager, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
19
27
|
abstract checkInstallation(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
20
28
|
/**
|
|
21
29
|
* Get the latest version available for the requirement.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RequirementConfig, RequirementStatus, ServerInstallOptions } from '../../metadatas/types.js';
|
|
2
2
|
import { BaseInstaller } from './BaseInstaller.js';
|
|
3
|
+
import { InstallOperationManager } from '../../loaders/InstallOperationManager.js';
|
|
3
4
|
/**
|
|
4
5
|
* Installer implementation for command-line tools
|
|
5
6
|
*/
|
|
@@ -38,5 +39,12 @@ export declare class CommandInstaller extends BaseInstaller {
|
|
|
38
39
|
* @param requirement The requirement to install
|
|
39
40
|
* @returns The status of the installation
|
|
40
41
|
*/
|
|
41
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Install the command
|
|
44
|
+
* @param requirement The requirement to install
|
|
45
|
+
* @param options Optional install options
|
|
46
|
+
* @param recorder Optional InstallOperationManager for recording steps
|
|
47
|
+
* @returns The status of the installation
|
|
48
|
+
*/
|
|
49
|
+
install(requirement: RequirementConfig, recorder: InstallOperationManager, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
42
50
|
}
|
|
@@ -2,6 +2,7 @@ import { OSType } from '../../metadatas/types.js';
|
|
|
2
2
|
import { BaseInstaller } from './BaseInstaller.js';
|
|
3
3
|
import { getOSType, refreshPathEnv } from '../../../utils/osUtils.js';
|
|
4
4
|
import { Logger } from '../../../utils/logger.js';
|
|
5
|
+
import * as RecordingConstants from '../../metadatas/recordingConstants.js';
|
|
5
6
|
/**
|
|
6
7
|
* Installer implementation for command-line tools
|
|
7
8
|
*/
|
|
@@ -122,8 +123,15 @@ export class CommandInstaller extends BaseInstaller {
|
|
|
122
123
|
* @param requirement The requirement to install
|
|
123
124
|
* @returns The status of the installation
|
|
124
125
|
*/
|
|
125
|
-
|
|
126
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Install the command
|
|
128
|
+
* @param requirement The requirement to install
|
|
129
|
+
* @param options Optional install options
|
|
130
|
+
* @param recorder Optional InstallOperationManager for recording steps
|
|
131
|
+
* @returns The status of the installation
|
|
132
|
+
*/
|
|
133
|
+
async install(requirement, recorder, options) {
|
|
134
|
+
const doInstall = async () => {
|
|
127
135
|
const status = await this.checkInstallation(requirement);
|
|
128
136
|
if (status.installed) {
|
|
129
137
|
return status;
|
|
@@ -132,20 +140,20 @@ export class CommandInstaller extends BaseInstaller {
|
|
|
132
140
|
const osType = getOSType();
|
|
133
141
|
let installCommand;
|
|
134
142
|
if (osType === OSType.Windows) {
|
|
135
|
-
// Windows installation using winget
|
|
136
143
|
installCommand = `winget install --id ${packageId}`;
|
|
137
144
|
if (requirement.version && requirement.version !== 'latest') {
|
|
138
145
|
installCommand += ` --version ${requirement.version}`;
|
|
139
146
|
}
|
|
140
147
|
}
|
|
141
148
|
else if (osType === OSType.MacOS) {
|
|
142
|
-
// macOS installation using Homebrew
|
|
143
149
|
installCommand = `brew install ${packageId}`;
|
|
144
150
|
if (requirement.version && requirement.version !== 'latest') {
|
|
145
151
|
installCommand += `@${requirement.version}`;
|
|
146
152
|
}
|
|
147
153
|
}
|
|
148
154
|
else {
|
|
155
|
+
if (recorder)
|
|
156
|
+
await recorder.recordStep('CommandInstaller:UnsupportedOS', 'failed', `Unsupported OS for ${requirement.name}`);
|
|
149
157
|
throw new Error(`Unsupported operating system for installing ${requirement.name}`);
|
|
150
158
|
}
|
|
151
159
|
// Execute the installation command
|
|
@@ -165,15 +173,41 @@ export class CommandInstaller extends BaseInstaller {
|
|
|
165
173
|
version: updatedStatus.version || requirement.version,
|
|
166
174
|
inProgress: false
|
|
167
175
|
};
|
|
176
|
+
};
|
|
177
|
+
if (recorder) {
|
|
178
|
+
return recorder.recording(doInstall, {
|
|
179
|
+
stepName: RecordingConstants.STEP_COMMAND_INSTALLER_INSTALL,
|
|
180
|
+
inProgressMessage: `Installing command: ${requirement.name}`,
|
|
181
|
+
endMessage: (result) => result.installed
|
|
182
|
+
? `Install completed for ${requirement.name}`
|
|
183
|
+
: `Install failed for ${requirement.name}`,
|
|
184
|
+
onError: (error) => {
|
|
185
|
+
return {
|
|
186
|
+
result: {
|
|
187
|
+
name: requirement.name,
|
|
188
|
+
type: 'command',
|
|
189
|
+
installed: false,
|
|
190
|
+
error: error instanceof Error ? error.message : String(error),
|
|
191
|
+
inProgress: false,
|
|
192
|
+
},
|
|
193
|
+
message: error instanceof Error ? error.message : String(error),
|
|
194
|
+
};
|
|
195
|
+
},
|
|
196
|
+
});
|
|
168
197
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
198
|
+
else {
|
|
199
|
+
try {
|
|
200
|
+
return await doInstall();
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
return {
|
|
204
|
+
name: requirement.name,
|
|
205
|
+
type: 'command',
|
|
206
|
+
installed: false,
|
|
207
|
+
error: error instanceof Error ? error.message : String(error),
|
|
208
|
+
inProgress: false,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
177
211
|
}
|
|
178
212
|
}
|
|
179
213
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RequirementConfig, RequirementStatus, ServerInstallOptions } from '../../metadatas/types.js';
|
|
2
2
|
import { BaseInstaller } from './BaseInstaller.js';
|
|
3
|
+
import { InstallOperationManager } from '../../loaders/InstallOperationManager.js';
|
|
3
4
|
/**
|
|
4
5
|
* Installer implementation for general requirements (type 'other')
|
|
5
6
|
* This installer handles requirements that don't fit into specific package manager categories
|
|
@@ -34,5 +35,14 @@ export declare class GeneralInstaller extends BaseInstaller {
|
|
|
34
35
|
* @param requirement The requirement to install
|
|
35
36
|
* @returns The status of the installation, including the install path in updateInfo
|
|
36
37
|
*/
|
|
37
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Install the general requirement
|
|
40
|
+
* For type 'other', this doesn't actually install anything, but downloads
|
|
41
|
+
* or locates the asset and returns the path for the caller to use
|
|
42
|
+
* @param requirement The requirement to install
|
|
43
|
+
* @param options Optional install options
|
|
44
|
+
* @param recorder Optional InstallOperationManager for recording steps
|
|
45
|
+
* @returns The status of the installation, including the install path in updateInfo
|
|
46
|
+
*/
|
|
47
|
+
install(requirement: RequirementConfig, recorder: InstallOperationManager, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
38
48
|
}
|