imcp 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +1 -45
- package/dist/core/installers/clients/BaseClientInstaller.d.ts +1 -5
- package/dist/core/installers/clients/BaseClientInstaller.js +40 -38
- package/dist/core/installers/clients/ClientInstaller.d.ts +9 -9
- package/dist/core/installers/clients/ClientInstaller.js +105 -99
- package/dist/core/installers/requirements/BaseInstaller.d.ts +9 -1
- package/dist/core/installers/requirements/CommandInstaller.d.ts +9 -1
- package/dist/core/installers/requirements/CommandInstaller.js +46 -12
- package/dist/core/installers/requirements/GeneralInstaller.d.ts +11 -1
- package/dist/core/installers/requirements/GeneralInstaller.js +46 -10
- package/dist/core/installers/requirements/InstallerFactory.d.ts +3 -1
- package/dist/core/installers/requirements/InstallerFactory.js +3 -2
- package/dist/core/installers/requirements/NpmInstaller.d.ts +4 -2
- package/dist/core/installers/requirements/NpmInstaller.js +38 -22
- package/dist/core/installers/requirements/PipInstaller.d.ts +3 -1
- package/dist/core/installers/requirements/PipInstaller.js +58 -36
- package/dist/core/installers/requirements/RequirementInstaller.d.ts +4 -1
- package/dist/core/loaders/InstallOperationManager.d.ts +115 -0
- package/dist/core/loaders/InstallOperationManager.js +311 -0
- package/dist/core/loaders/SystemSettingsManager.d.ts +54 -0
- package/dist/core/loaders/SystemSettingsManager.js +257 -0
- package/dist/core/metadatas/recordingConstants.d.ts +44 -0
- package/dist/core/metadatas/recordingConstants.js +45 -0
- package/dist/core/metadatas/types.d.ts +21 -0
- package/dist/core/onboard/InstallOperationManager.d.ts +23 -0
- package/dist/core/onboard/InstallOperationManager.js +144 -0
- package/dist/core/onboard/OnboardStatusManager.js +2 -1
- package/dist/core/validators/StdioServerValidator.js +4 -3
- package/dist/services/InstallationService.d.ts +2 -37
- package/dist/services/InstallationService.js +45 -313
- package/dist/services/MCPManager.d.ts +1 -1
- package/dist/services/MCPManager.js +4 -58
- package/dist/services/RequirementService.d.ts +85 -12
- package/dist/services/RequirementService.js +488 -49
- package/dist/services/ServerService.d.ts +0 -6
- package/dist/services/ServerService.js +0 -74
- package/dist/utils/adoUtils.js +6 -3
- package/dist/utils/logger.js +1 -1
- package/dist/utils/macroExpressionUtils.js +3 -25
- package/dist/utils/osUtils.d.ts +22 -1
- package/dist/utils/osUtils.js +92 -1
- package/dist/utils/versionUtils.d.ts +20 -1
- package/dist/utils/versionUtils.js +51 -4
- package/dist/web/public/css/modal.css +292 -1
- package/dist/web/public/css/serverDetails.css +14 -1
- package/dist/web/public/index.html +122 -20
- package/dist/web/public/js/flights/flights.js +1 -0
- package/dist/web/public/js/modal/index.js +8 -14
- package/dist/web/public/js/modal/installModal.js +3 -4
- package/dist/web/public/js/modal/installation.js +122 -137
- package/dist/web/public/js/modal/loadingModal.js +155 -25
- package/dist/web/public/js/modal/messageQueue.js +45 -101
- package/dist/web/public/js/modal/modalSetup.js +125 -43
- package/dist/web/public/js/modal/modalUtils.js +0 -12
- package/dist/web/public/js/modal.js +23 -10
- package/dist/web/public/js/onboard/publishHandler.js +22 -20
- package/dist/web/public/js/serverCategoryDetails.js +60 -11
- package/dist/web/public/js/serverCategoryList.js +2 -2
- package/dist/web/public/js/settings.js +314 -0
- package/dist/web/public/settings.html +135 -0
- package/dist/web/public/styles.css +32 -0
- package/dist/web/server.js +82 -0
- package/memory-bank/activeContext.md +13 -1
- package/memory-bank/decisionLog.md +63 -0
- package/memory-bank/progress.md +30 -0
- package/memory-bank/systemPatterns.md +7 -0
- package/package.json +1 -1
- package/src/cli/index.ts +1 -48
- package/src/core/installers/clients/BaseClientInstaller.ts +64 -50
- package/src/core/installers/clients/ClientInstaller.ts +130 -130
- package/src/core/installers/requirements/BaseInstaller.ts +9 -1
- package/src/core/installers/requirements/CommandInstaller.ts +47 -13
- package/src/core/installers/requirements/GeneralInstaller.ts +48 -10
- package/src/core/installers/requirements/InstallerFactory.ts +4 -3
- package/src/core/installers/requirements/NpmInstaller.ts +90 -68
- package/src/core/installers/requirements/PipInstaller.ts +81 -55
- package/src/core/installers/requirements/RequirementInstaller.ts +4 -3
- package/src/core/loaders/InstallOperationManager.ts +367 -0
- package/src/core/loaders/SystemSettingsManager.ts +278 -0
- package/src/core/metadatas/recordingConstants.ts +62 -0
- package/src/core/metadatas/types.ts +23 -0
- package/src/core/onboard/OnboardStatusManager.ts +2 -1
- package/src/core/validators/StdioServerValidator.ts +4 -3
- package/src/services/InstallationService.ts +54 -399
- package/src/services/MCPManager.ts +4 -77
- package/src/services/RequirementService.ts +564 -67
- package/src/services/ServerService.ts +0 -90
- package/src/utils/adoUtils.ts +6 -4
- package/src/utils/logger.ts +1 -1
- package/src/utils/macroExpressionUtils.ts +4 -21
- package/src/utils/osUtils.ts +92 -1
- package/src/utils/versionUtils.ts +71 -19
- package/src/web/public/css/modal.css +292 -1
- package/src/web/public/css/serverDetails.css +14 -1
- package/src/web/public/index.html +122 -20
- package/src/web/public/js/flights/flights.js +1 -1
- package/src/web/public/js/modal/index.js +8 -14
- package/src/web/public/js/modal/installModal.js +3 -4
- package/src/web/public/js/modal/installation.js +122 -137
- package/src/web/public/js/modal/loadingModal.js +155 -25
- package/src/web/public/js/modal/modalSetup.js +125 -43
- package/src/web/public/js/modal/modalUtils.js +0 -12
- package/src/web/public/js/modal.js +23 -10
- package/src/web/public/js/onboard/publishHandler.js +22 -20
- package/src/web/public/js/serverCategoryDetails.js +60 -11
- package/src/web/public/js/serverCategoryList.js +2 -2
- package/src/web/public/js/settings.js +314 -0
- package/src/web/public/settings.html +135 -0
- package/src/web/public/styles.css +32 -0
- package/src/web/server.ts +85 -0
- package/src/web/public/js/modal/messageQueue.js +0 -112
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseInstaller } from './BaseInstaller.js';
|
|
2
2
|
import { handleGitHubRelease } from '../../../utils/githubUtils.js';
|
|
3
3
|
import { handleArtifact } from '../../../utils/adoUtils.js';
|
|
4
|
+
import * as RecordingConstants from '../../metadatas/recordingConstants.js';
|
|
4
5
|
/**
|
|
5
6
|
* Installer implementation for general requirements (type 'other')
|
|
6
7
|
* This installer handles requirements that don't fit into specific package manager categories
|
|
@@ -50,8 +51,17 @@ export class GeneralInstaller extends BaseInstaller {
|
|
|
50
51
|
* @param requirement The requirement to install
|
|
51
52
|
* @returns The status of the installation, including the install path in updateInfo
|
|
52
53
|
*/
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Install the general requirement
|
|
56
|
+
* For type 'other', this doesn't actually install anything, but downloads
|
|
57
|
+
* or locates the asset and returns the path for the caller to use
|
|
58
|
+
* @param requirement The requirement to install
|
|
59
|
+
* @param options Optional install options
|
|
60
|
+
* @param recorder Optional InstallOperationManager for recording steps
|
|
61
|
+
* @returns The status of the installation, including the install path in updateInfo
|
|
62
|
+
*/
|
|
63
|
+
async install(requirement, recorder, options) {
|
|
64
|
+
const doInstall = async () => {
|
|
55
65
|
// For type 'other', a registry must be specified
|
|
56
66
|
if (!requirement.registry) {
|
|
57
67
|
throw new Error('Registry must be specified for requirement type "other"');
|
|
@@ -76,15 +86,41 @@ export class GeneralInstaller extends BaseInstaller {
|
|
|
76
86
|
version: requirement.version,
|
|
77
87
|
inProgress: false,
|
|
78
88
|
};
|
|
89
|
+
};
|
|
90
|
+
if (recorder) {
|
|
91
|
+
return recorder.recording(doInstall, {
|
|
92
|
+
stepName: RecordingConstants.STEP_GENERAL_INSTALLER_INSTALL,
|
|
93
|
+
inProgressMessage: `Installing general requirement: ${requirement.name}`,
|
|
94
|
+
endMessage: (result) => result.installed
|
|
95
|
+
? `Install completed for ${requirement.name}`
|
|
96
|
+
: `Install failed for ${requirement.name}`,
|
|
97
|
+
onError: (error) => {
|
|
98
|
+
return {
|
|
99
|
+
result: {
|
|
100
|
+
name: requirement.name,
|
|
101
|
+
type: 'other',
|
|
102
|
+
installed: false,
|
|
103
|
+
error: error instanceof Error ? error.message : String(error),
|
|
104
|
+
inProgress: false,
|
|
105
|
+
},
|
|
106
|
+
message: error instanceof Error ? error.message : String(error),
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
});
|
|
79
110
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
111
|
+
else {
|
|
112
|
+
try {
|
|
113
|
+
return await doInstall();
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
return {
|
|
117
|
+
name: requirement.name,
|
|
118
|
+
type: 'other',
|
|
119
|
+
installed: false,
|
|
120
|
+
error: error instanceof Error ? error.message : String(error),
|
|
121
|
+
inProgress: false,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
88
124
|
}
|
|
89
125
|
}
|
|
90
126
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RequirementConfig, RequirementStatus, ServerInstallOptions } from '../../metadatas/types.js';
|
|
2
2
|
import { RequirementInstaller } from './RequirementInstaller.js';
|
|
3
3
|
import { exec } from 'child_process';
|
|
4
|
+
import { InstallOperationManager } from '../../loaders/InstallOperationManager.js';
|
|
4
5
|
/**
|
|
5
6
|
* Factory for creating the appropriate installer for a requirement
|
|
6
7
|
*/
|
|
@@ -34,10 +35,11 @@ export declare class InstallerFactory {
|
|
|
34
35
|
/**
|
|
35
36
|
* Install a requirement using the appropriate installer
|
|
36
37
|
* @param requirement The requirement to install
|
|
38
|
+
* @param recorder Optional InstallOperationManager for recording steps
|
|
37
39
|
* @param options Installation options including python environment
|
|
38
40
|
* @returns The installation status
|
|
39
41
|
*/
|
|
40
|
-
install(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
42
|
+
install(requirement: RequirementConfig, recorder: InstallOperationManager, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
41
43
|
/**
|
|
42
44
|
* Check the installation status of a requirement
|
|
43
45
|
* @param requirement The requirement to check
|
|
@@ -50,10 +50,11 @@ export class InstallerFactory {
|
|
|
50
50
|
/**
|
|
51
51
|
* Install a requirement using the appropriate installer
|
|
52
52
|
* @param requirement The requirement to install
|
|
53
|
+
* @param recorder Optional InstallOperationManager for recording steps
|
|
53
54
|
* @param options Installation options including python environment
|
|
54
55
|
* @returns The installation status
|
|
55
56
|
*/
|
|
56
|
-
async install(requirement, options) {
|
|
57
|
+
async install(requirement, recorder, options) {
|
|
57
58
|
const installer = this.getInstaller(requirement);
|
|
58
59
|
if (!installer) {
|
|
59
60
|
return {
|
|
@@ -64,7 +65,7 @@ export class InstallerFactory {
|
|
|
64
65
|
inProgress: false
|
|
65
66
|
};
|
|
66
67
|
}
|
|
67
|
-
return await installer.install(requirement, options);
|
|
68
|
+
return await installer.install(requirement, recorder, options);
|
|
68
69
|
}
|
|
69
70
|
/**
|
|
70
71
|
* Check the installation status of a 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 NPM packages
|
|
5
6
|
*/
|
|
@@ -58,8 +59,9 @@ export declare class NpmInstaller extends BaseInstaller {
|
|
|
58
59
|
/**
|
|
59
60
|
* Install the NPM package.
|
|
60
61
|
* @param requirement The requirement to install.
|
|
61
|
-
* @param
|
|
62
|
+
* @param recorder Optional InstallOperationManager for recording steps.
|
|
63
|
+
* @param options Installation options.
|
|
62
64
|
* @returns The status of the installation.
|
|
63
65
|
*/
|
|
64
|
-
install(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
66
|
+
install(requirement: RequirementConfig, recorder: InstallOperationManager, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
65
67
|
}
|
|
@@ -7,6 +7,7 @@ import path from 'path';
|
|
|
7
7
|
import fs from 'fs/promises';
|
|
8
8
|
import { SETTINGS_DIR } from '../../metadatas/constants.js'; // Corrected path
|
|
9
9
|
import { Logger } from '../../../utils/logger.js';
|
|
10
|
+
import * as RecordingConstants from '../../metadatas/recordingConstants.js';
|
|
10
11
|
/**
|
|
11
12
|
* Installer implementation for NPM packages
|
|
12
13
|
*/
|
|
@@ -136,13 +137,13 @@ export class NpmInstaller extends BaseInstaller {
|
|
|
136
137
|
* @param targetDir Target directory for installation.
|
|
137
138
|
* @returns The installed version of the package.
|
|
138
139
|
*/
|
|
139
|
-
async _installPackage(requirement, packageSource, targetDir) {
|
|
140
|
+
async _installPackage(requirement, packageSource, targetDir, recorder) {
|
|
140
141
|
Logger.debug(`Installing NPM package from "${packageSource}" into "${targetDir}"`);
|
|
141
142
|
await fs.mkdir(targetDir, { recursive: true });
|
|
142
143
|
const installCommand = `npm install ${packageSource} --prefix "${targetDir}"`;
|
|
143
144
|
Logger.debug(`Executing install command: ${installCommand}`);
|
|
144
145
|
const requirementName = this._getRequirementName(requirement);
|
|
145
|
-
|
|
146
|
+
return await recorder.recording(async () => {
|
|
146
147
|
const { stdout: installStdout, stderr: installStderr } = await this.execPromise(installCommand);
|
|
147
148
|
Logger.debug(`NPM install stdout for ${packageSource} in ${targetDir}: ${installStdout}`);
|
|
148
149
|
if (installStderr && !installStderr.toLowerCase().includes('added') && !installStderr.toLowerCase().includes('updated') && !installStderr.toLowerCase().includes('found 0 vulnerabilities')) {
|
|
@@ -155,24 +156,28 @@ export class NpmInstaller extends BaseInstaller {
|
|
|
155
156
|
return { version: installedVersion };
|
|
156
157
|
}
|
|
157
158
|
else {
|
|
158
|
-
throw new Error(`Successfully ran npm install for ${packageSource}, but ${requirement.name} version could not be determined via npm list in ${targetDir}
|
|
159
|
+
throw new Error(`Successfully ran npm install for ${packageSource}, but ${requirement.name} version could not be determined via npm list in ${targetDir}, stderr: ${installStderr}`);
|
|
159
160
|
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
}, {
|
|
162
|
+
stepName: RecordingConstants.STEP_INSTALLATION_COMMAND_EXECUTION,
|
|
163
|
+
inProgressMessage: `Running: ${installCommand}`,
|
|
164
|
+
onError: (error) => {
|
|
165
|
+
Logger.error(`Error during NPM installation: ${error instanceof Error ? error.message : String(error)}`);
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
165
169
|
}
|
|
166
170
|
/**
|
|
167
171
|
* Install the NPM package.
|
|
168
172
|
* @param requirement The requirement to install.
|
|
169
|
-
* @param
|
|
173
|
+
* @param recorder Optional InstallOperationManager for recording steps.
|
|
174
|
+
* @param options Installation options.
|
|
170
175
|
* @returns The status of the installation.
|
|
171
176
|
*/
|
|
172
|
-
async install(requirement, options) {
|
|
177
|
+
async install(requirement, recorder, options) {
|
|
173
178
|
const targetDir = options?.settings?.folderName || this._getRequirementFolderPath(requirement);
|
|
174
179
|
await fs.mkdir(targetDir, { recursive: true });
|
|
175
|
-
|
|
180
|
+
return recorder.recording(async () => {
|
|
176
181
|
const status = await this.checkInstallation(requirement, { settings: { folderName: targetDir } });
|
|
177
182
|
if (status.installed && status.version && compareVersions(status.version, requirement.version) === 0 && !requirement.version.toLowerCase().includes('latest')) {
|
|
178
183
|
Logger.log(`${requirement.name}@${status.version} already installed in ${targetDir}.`);
|
|
@@ -192,10 +197,12 @@ export class NpmInstaller extends BaseInstaller {
|
|
|
192
197
|
resolvedVersion = adoResult.version;
|
|
193
198
|
}
|
|
194
199
|
else {
|
|
200
|
+
if (recorder)
|
|
201
|
+
await recorder.recordStep('NpmInstaller:RegistryConfig', 'failed', 'Invalid registry configuration for npm.');
|
|
195
202
|
throw new Error('Invalid registry configuration for npm.');
|
|
196
203
|
}
|
|
197
204
|
}
|
|
198
|
-
const finalInstallResult = await this._installPackage(requirement, packageToInstall, targetDir);
|
|
205
|
+
const finalInstallResult = await this._installPackage(requirement, packageToInstall, targetDir, recorder);
|
|
199
206
|
resolvedVersion = finalInstallResult.version;
|
|
200
207
|
return {
|
|
201
208
|
name: requirement.name,
|
|
@@ -205,16 +212,25 @@ export class NpmInstaller extends BaseInstaller {
|
|
|
205
212
|
inProgress: false,
|
|
206
213
|
npmPath: targetDir
|
|
207
214
|
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
}, {
|
|
216
|
+
stepName: RecordingConstants.STEP_NPM_INSTALLER_INSTALL,
|
|
217
|
+
inProgressMessage: `Installing npm package: ${requirement.name}`,
|
|
218
|
+
endMessage: (result) => result.installed
|
|
219
|
+
? `Install completed for ${requirement.name}`
|
|
220
|
+
: `Install failed for ${requirement.name}`,
|
|
221
|
+
onError: (error) => {
|
|
222
|
+
return {
|
|
223
|
+
result: {
|
|
224
|
+
name: requirement.name,
|
|
225
|
+
type: 'npm',
|
|
226
|
+
installed: false,
|
|
227
|
+
error: error instanceof Error ? error.message : String(error),
|
|
228
|
+
inProgress: false
|
|
229
|
+
},
|
|
230
|
+
message: error instanceof Error ? error.message : String(error),
|
|
231
|
+
};
|
|
232
|
+
},
|
|
233
|
+
});
|
|
218
234
|
}
|
|
219
235
|
}
|
|
220
236
|
//# sourceMappingURL=NpmInstaller.js.map
|
|
@@ -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 Python packages using pip
|
|
5
6
|
*/
|
|
@@ -30,8 +31,9 @@ export declare class PipInstaller extends BaseInstaller {
|
|
|
30
31
|
/**
|
|
31
32
|
* Install the Python package
|
|
32
33
|
* @param requirement The requirement to install
|
|
34
|
+
* @param recorder Optional InstallOperationManager for recording steps
|
|
33
35
|
* @param options Optional server install options
|
|
34
36
|
* @returns The status of the installation
|
|
35
37
|
*/
|
|
36
|
-
install(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
38
|
+
install(requirement: RequirementConfig, recorder: InstallOperationManager, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
37
39
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BaseInstaller } from './BaseInstaller.js';
|
|
2
2
|
import { handleGitHubRelease, getGitHubLatestVersion } from '../../../utils/githubUtils.js';
|
|
3
|
-
// Assuming getArtifactLatestVersion will be available in adoUtils.ts
|
|
4
3
|
import { handleArtifact as handleAdoArtifact, getArtifactLatestVersion } from '../../../utils/adoUtils.js';
|
|
5
4
|
import { compareVersions } from '../../../utils/versionUtils.js';
|
|
6
5
|
import { Logger } from '../../../utils/logger.js';
|
|
6
|
+
import * as RecordingConstants from '../../metadatas/recordingConstants.js';
|
|
7
7
|
/**
|
|
8
8
|
* Installer implementation for Python packages using pip
|
|
9
9
|
*/
|
|
@@ -105,21 +105,25 @@ export class PipInstaller extends BaseInstaller {
|
|
|
105
105
|
/**
|
|
106
106
|
* Install the Python package
|
|
107
107
|
* @param requirement The requirement to install
|
|
108
|
+
* @param recorder Optional InstallOperationManager for recording steps
|
|
108
109
|
* @param options Optional server install options
|
|
109
110
|
* @returns The status of the installation
|
|
110
111
|
*/
|
|
111
|
-
async install(requirement, options) {
|
|
112
|
-
|
|
112
|
+
async install(requirement, recorder, options) {
|
|
113
|
+
return await recorder.recording(async () => {
|
|
113
114
|
const status = await this.checkInstallation(requirement, options);
|
|
114
115
|
if (status.installed && status.version && compareVersions(status.version, requirement.version) === 0 && !requirement.version.toLowerCase().includes('latest')) {
|
|
115
116
|
Logger.log(`${requirement.name}==${status.version} already installed for ${this.getPythonCommand}.`);
|
|
116
117
|
return status;
|
|
117
118
|
}
|
|
118
119
|
const pipCmd = this.getPipCommand(options);
|
|
120
|
+
let command;
|
|
119
121
|
if (!requirement.registry) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
if (requirement.version && !requirement.version.includes('latest')) {
|
|
123
|
+
command = `${pipCmd} install ${requirement.name}==${requirement.version}`;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
command = `${pipCmd} install --upgrade ${requirement.name}`;
|
|
123
127
|
}
|
|
124
128
|
}
|
|
125
129
|
else {
|
|
@@ -127,45 +131,63 @@ export class PipInstaller extends BaseInstaller {
|
|
|
127
131
|
if (requirement.registry.githubRelease) {
|
|
128
132
|
const result = await handleGitHubRelease(requirement, requirement.registry.githubRelease);
|
|
129
133
|
packageSource = result.resolvedPath;
|
|
130
|
-
|
|
131
|
-
if (stderr && stderr.toLowerCase().includes('error')) {
|
|
132
|
-
throw new Error(stderr);
|
|
133
|
-
}
|
|
134
|
+
command = `${pipCmd} install "${packageSource}"`;
|
|
134
135
|
}
|
|
135
136
|
else if (requirement.registry.artifacts) {
|
|
136
137
|
const pythonCmd = this.getPythonCommand(options);
|
|
137
138
|
const adoArtifactResult = await handleAdoArtifact(requirement, requirement.registry.artifacts, pythonCmd);
|
|
138
|
-
|
|
139
|
-
if (stderr && stderr.toLowerCase().includes('error')) {
|
|
140
|
-
const checkStatus = await this.checkInstallation(requirement, options);
|
|
141
|
-
if (!checkStatus.installed) {
|
|
142
|
-
throw new Error(`Pip installation failed with: ${stderr}`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
139
|
+
command = `${pipCmd} install ${adoArtifactResult.package} --extra-index-url ${adoArtifactResult.registryUrl}`;
|
|
145
140
|
}
|
|
146
141
|
else {
|
|
142
|
+
await recorder.recordStep('PipInstaller:RegistryConfig', 'failed', 'Invalid registry configuration');
|
|
147
143
|
throw new Error('Invalid registry configuration');
|
|
148
144
|
}
|
|
149
145
|
}
|
|
150
|
-
return {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
146
|
+
return await recorder.recording(async () => {
|
|
147
|
+
const { stderr } = await this.execPromise(command);
|
|
148
|
+
if (stderr && stderr.toLowerCase().includes('error')) {
|
|
149
|
+
Logger.debug(`Pip installation error: ${stderr}`);
|
|
150
|
+
// wait for 5 seconds as python pip would be little delayed
|
|
151
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
152
|
+
const checkStatus = await this.checkInstallation(requirement, options);
|
|
153
|
+
if (!checkStatus.installed) {
|
|
154
|
+
Logger.error(`Package not found after the command, ${stderr}`);
|
|
155
|
+
throw new Error(`Pip installation failed with: ${stderr}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
name: requirement.name,
|
|
160
|
+
type: 'pip',
|
|
161
|
+
installed: true,
|
|
162
|
+
version: requirement.version, // This might need to be updated to actual installed version
|
|
163
|
+
inProgress: false,
|
|
164
|
+
pythonEnv: this.getPythonCommand(options)
|
|
165
|
+
};
|
|
166
|
+
}, {
|
|
167
|
+
stepName: `${RecordingConstants.STEP_INSTALL_COMMAND_PREFIX}: ${requirement.name} : ${requirement.version}`,
|
|
168
|
+
inProgressMessage: `Running: ${command}`,
|
|
169
|
+
endMessage: (result) => result.installed ? `Succeeded: ${command}` : `Failed: ${command}`,
|
|
170
|
+
});
|
|
171
|
+
}, {
|
|
172
|
+
stepName: RecordingConstants.STEP_PIP_INSTALLER_INSTALL,
|
|
173
|
+
inProgressMessage: `Installing pip package: ${requirement.name}`,
|
|
174
|
+
endMessage: (result) => result.installed
|
|
175
|
+
? `Install completed for ${requirement.name} with version ${result.version}`
|
|
176
|
+
: `Install failed for ${requirement.name}`,
|
|
177
|
+
onError: (error) => {
|
|
178
|
+
return {
|
|
179
|
+
result: {
|
|
180
|
+
name: requirement.name,
|
|
181
|
+
type: 'pip',
|
|
182
|
+
installed: false,
|
|
183
|
+
error: error instanceof Error ? error.message : String(error),
|
|
184
|
+
inProgress: false,
|
|
185
|
+
pythonEnv: this.getPythonCommand(options)
|
|
186
|
+
},
|
|
187
|
+
message: error instanceof Error ? error.message : String(error),
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
});
|
|
169
191
|
}
|
|
170
192
|
}
|
|
171
193
|
//# sourceMappingURL=PipInstaller.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RequirementConfig, RequirementStatus, ServerInstallOptions } from '../../metadatas/types.js';
|
|
2
|
+
import { InstallOperationManager } from '../../loaders/InstallOperationManager.js';
|
|
2
3
|
/**
|
|
3
4
|
* Interface for requirement installers.
|
|
4
5
|
* Implementations should handle specific requirement types.
|
|
@@ -14,9 +15,11 @@ export interface RequirementInstaller {
|
|
|
14
15
|
/**
|
|
15
16
|
* Install the requirement
|
|
16
17
|
* @param requirement The requirement to install
|
|
18
|
+
* @param options Optional install options
|
|
19
|
+
* @param recorder Optional InstallOperationManager for recording steps
|
|
17
20
|
* @returns The status of the installation
|
|
18
21
|
*/
|
|
19
|
-
install(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
22
|
+
install(requirement: RequirementConfig, recorder: InstallOperationManager, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
20
23
|
/**
|
|
21
24
|
* Check if the requirement is already installed
|
|
22
25
|
* @param requirement The requirement to check
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { InstallOperationDetails } from '../metadatas/types.js';
|
|
2
|
+
export declare class InstallOperationManager {
|
|
3
|
+
private installOperationStatus;
|
|
4
|
+
private statusLock;
|
|
5
|
+
private readonly categoryName;
|
|
6
|
+
private readonly serverName;
|
|
7
|
+
private readonly statusFilePath;
|
|
8
|
+
/**
|
|
9
|
+
* Creates an InstallOperationManager instance for a specific category and server.
|
|
10
|
+
* @param categoryName The name of the category.
|
|
11
|
+
* @param serverName The name of the server.
|
|
12
|
+
*/
|
|
13
|
+
constructor(categoryName: string, serverName: string);
|
|
14
|
+
/**
|
|
15
|
+
* Returns an InstallOperationManager instance for the given category and server.
|
|
16
|
+
* @param categoryName The name of the category.
|
|
17
|
+
* @param serverName The name of the server.
|
|
18
|
+
*/
|
|
19
|
+
private static instanceMap;
|
|
20
|
+
/**
|
|
21
|
+
* Returns a cached InstallOperationManager instance for the given category and server.
|
|
22
|
+
* If an instance does not exist, it will be created and cached.
|
|
23
|
+
* @param categoryName The name of the category.
|
|
24
|
+
* @param serverName The name of the server.
|
|
25
|
+
*/
|
|
26
|
+
static getInstance(categoryName: string, serverName: string): InstallOperationManager;
|
|
27
|
+
private withLock;
|
|
28
|
+
private loadStatuses;
|
|
29
|
+
private saveStatuses;
|
|
30
|
+
private get operationKey();
|
|
31
|
+
/**
|
|
32
|
+
* Record a step for this category/server instance.
|
|
33
|
+
*/
|
|
34
|
+
recordStep(stepName: string, status: 'pending' | 'in-progress' | 'completed' | 'failed', message?: string): Promise<InstallOperationDetails>;
|
|
35
|
+
/**
|
|
36
|
+
* Resets (deletes) the operation status for this category/server.
|
|
37
|
+
* This is useful to call before starting a new installation attempt.
|
|
38
|
+
*/
|
|
39
|
+
resetOperation(): Promise<InstallOperationManager>;
|
|
40
|
+
/**
|
|
41
|
+
* Gets the details for this category/server.
|
|
42
|
+
*/
|
|
43
|
+
getDetails(): Promise<InstallOperationDetails | undefined>;
|
|
44
|
+
/**
|
|
45
|
+
* Gets all operation details in this file (for this category/server).
|
|
46
|
+
*/
|
|
47
|
+
getAllDetails(): Promise<Record<string, InstallOperationDetails>>;
|
|
48
|
+
/**
|
|
49
|
+
* Explicitly mark the overall status of an operation as 'completed' or 'failed'.
|
|
50
|
+
* This can be used to force the final state regardless of step details.
|
|
51
|
+
* @param status 'completed' or 'failed'
|
|
52
|
+
* @param error Optional error message if failed
|
|
53
|
+
*/
|
|
54
|
+
markOverallStatus(status: 'completed' | 'failed', error?: string): Promise<InstallOperationDetails | undefined>;
|
|
55
|
+
/**
|
|
56
|
+
* Executes a function and records its step status as 'in-progress', 'completed', or 'failed'.
|
|
57
|
+
*
|
|
58
|
+
* Useful for wrapping tasks with consistent status tracking and flexible error handling.
|
|
59
|
+
*
|
|
60
|
+
* @template T The return type of the function being executed.
|
|
61
|
+
*
|
|
62
|
+
* @param fn The function to execute. Can be synchronous or asynchronous.
|
|
63
|
+
* @param options Optional configuration:
|
|
64
|
+
* - stepName: Custom name for the step. Defaults to the function name or 'unnamedStep'.
|
|
65
|
+
* - inProgressMessage: Optional message to log when the step starts.
|
|
66
|
+
* - endMessage: Optional static string or function to generate a final message from the result or error.
|
|
67
|
+
* - onResult: A function `(result: T) => boolean` that determines whether the step was successful. Defaults to always `true`.
|
|
68
|
+
* - onError: Optional function `(error) => { result: T; message: string } | never` that handles an error and may return a fallback result and message, or throw.
|
|
69
|
+
*
|
|
70
|
+
* @returns A promise that resolves with the result of the function if successful, or rethrows on failure.
|
|
71
|
+
*
|
|
72
|
+
* @throws The original error or any error thrown from `onError`.
|
|
73
|
+
*/
|
|
74
|
+
recording<T>(fn: () => T | Promise<T>, options?: {
|
|
75
|
+
stepName?: string;
|
|
76
|
+
inProgressMessage?: string;
|
|
77
|
+
endMessage?: string | ((data: T) => string | undefined);
|
|
78
|
+
onResult?: (result: T) => boolean;
|
|
79
|
+
onError?: (error: unknown) => {
|
|
80
|
+
result: T;
|
|
81
|
+
message: string;
|
|
82
|
+
} | Promise<{
|
|
83
|
+
result: T;
|
|
84
|
+
message: string;
|
|
85
|
+
}> | never;
|
|
86
|
+
}): Promise<T>;
|
|
87
|
+
/**
|
|
88
|
+
* Executes an asynchronous "fire-and-forget" task and records its step status as 'in-progress', 'completed', or 'failed'.
|
|
89
|
+
*
|
|
90
|
+
* Unlike `recording`, this does not await the result — it runs the task in the background.
|
|
91
|
+
* Useful for side-effecting operations like updates or notifications where progress should be logged but not block flow.
|
|
92
|
+
*
|
|
93
|
+
* @template T The resolved type of the asynchronous task.
|
|
94
|
+
*
|
|
95
|
+
* @param fn The async function to execute.
|
|
96
|
+
* @param options Optional configuration:
|
|
97
|
+
* - stepName: Custom name for the step. Defaults to the function name or 'unnamedStep'.
|
|
98
|
+
* - inProgressMessage: Message to log when the step starts.
|
|
99
|
+
* - endMessage: Static string or function to generate the final message from result or error.
|
|
100
|
+
* - onResult: A boolean or function `(result: T) => boolean` to determine if the step is successful.
|
|
101
|
+
* - onError: A function `(error) => string` or `Promise<string>` to handle errors and return a message.
|
|
102
|
+
* If it throws, the error is rethrown and logged.
|
|
103
|
+
* - onComplete: Optional callback to run after completion, regardless of success or failure.
|
|
104
|
+
*
|
|
105
|
+
* @returns void
|
|
106
|
+
*/
|
|
107
|
+
recordingAsync<T>(fn: () => Promise<T>, options?: {
|
|
108
|
+
stepName?: string;
|
|
109
|
+
inProgressMessage?: string;
|
|
110
|
+
endMessage?: string | ((result: T) => string);
|
|
111
|
+
onResult?: (result: T) => boolean;
|
|
112
|
+
onError?: (error: unknown) => string | Promise<string> | never;
|
|
113
|
+
onComplete?: () => void;
|
|
114
|
+
}): void;
|
|
115
|
+
}
|