imcp 0.0.14 → 0.0.16
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/core/ConfigurationProvider.d.ts +1 -0
- package/dist/core/ConfigurationProvider.js +15 -0
- package/dist/core/InstallationService.js +2 -7
- package/dist/core/MCPManager.d.ts +11 -2
- package/dist/core/MCPManager.js +24 -1
- package/dist/core/RequirementService.js +2 -8
- package/dist/core/installers/clients/BaseClientInstaller.d.ts +51 -0
- package/dist/core/installers/clients/BaseClientInstaller.js +160 -0
- package/dist/core/installers/clients/ClientInstaller.d.ts +16 -9
- package/dist/core/installers/clients/ClientInstaller.js +80 -527
- package/dist/core/installers/clients/ClientInstallerFactory.d.ts +20 -0
- package/dist/core/installers/clients/ClientInstallerFactory.js +37 -0
- package/dist/core/installers/clients/ClineInstaller.d.ts +18 -0
- package/dist/core/installers/clients/ClineInstaller.js +124 -0
- package/dist/core/installers/clients/GithubCopilotInstaller.d.ts +34 -0
- package/dist/core/installers/clients/GithubCopilotInstaller.js +162 -0
- package/dist/core/installers/clients/MSRooCodeInstaller.d.ts +15 -0
- package/dist/core/installers/clients/MSRooCodeInstaller.js +122 -0
- package/dist/core/installers/requirements/BaseInstaller.d.ts +11 -34
- package/dist/core/installers/requirements/BaseInstaller.js +5 -116
- package/dist/core/installers/requirements/CommandInstaller.d.ts +6 -1
- package/dist/core/installers/requirements/CommandInstaller.js +7 -0
- package/dist/core/installers/requirements/GeneralInstaller.d.ts +6 -1
- package/dist/core/installers/requirements/GeneralInstaller.js +9 -4
- package/dist/core/installers/requirements/NpmInstaller.d.ts +46 -7
- package/dist/core/installers/requirements/NpmInstaller.js +150 -58
- package/dist/core/installers/requirements/PipInstaller.d.ts +9 -0
- package/dist/core/installers/requirements/PipInstaller.js +66 -28
- package/dist/core/onboard/FeedOnboardService.d.ts +50 -13
- package/dist/core/onboard/FeedOnboardService.js +263 -88
- package/dist/core/onboard/OnboardProcessor.d.ts +79 -0
- package/dist/core/onboard/OnboardProcessor.js +290 -0
- package/dist/core/onboard/OnboardStatus.d.ts +49 -0
- package/dist/core/onboard/OnboardStatus.js +10 -0
- package/dist/core/onboard/OnboardStatusManager.d.ts +57 -0
- package/dist/core/onboard/OnboardStatusManager.js +176 -0
- package/dist/core/types.d.ts +4 -5
- package/dist/core/validators/FeedValidator.d.ts +8 -1
- package/dist/core/validators/FeedValidator.js +60 -7
- package/dist/core/validators/IServerValidator.d.ts +19 -0
- package/dist/core/validators/IServerValidator.js +2 -0
- package/dist/core/validators/SSEServerValidator.d.ts +15 -0
- package/dist/core/validators/SSEServerValidator.js +39 -0
- package/dist/core/validators/ServerValidatorFactory.d.ts +24 -0
- package/dist/core/validators/ServerValidatorFactory.js +45 -0
- package/dist/core/validators/StdioServerValidator.d.ts +46 -0
- package/dist/core/validators/StdioServerValidator.js +229 -0
- package/dist/services/InstallRequestValidator.d.ts +1 -1
- package/dist/services/ServerService.d.ts +9 -6
- package/dist/services/ServerService.js +18 -7
- package/dist/utils/adoUtils.d.ts +29 -0
- package/dist/utils/adoUtils.js +252 -0
- package/dist/utils/clientUtils.d.ts +0 -7
- package/dist/utils/clientUtils.js +0 -42
- package/dist/utils/githubUtils.d.ts +10 -0
- package/dist/utils/githubUtils.js +22 -0
- package/dist/utils/macroExpressionUtils.d.ts +38 -0
- package/dist/utils/macroExpressionUtils.js +116 -0
- package/dist/utils/osUtils.d.ts +4 -20
- package/dist/utils/osUtils.js +78 -23
- package/dist/web/contract/serverContract.d.ts +3 -1
- package/dist/web/public/css/notifications.css +48 -17
- package/dist/web/public/css/onboard.css +66 -3
- package/dist/web/public/index.html +84 -16
- package/dist/web/public/js/api.js +3 -6
- package/dist/web/public/js/flights/flights.js +127 -0
- package/dist/web/public/js/modal/installation.js +5 -5
- package/dist/web/public/js/modal/modalSetup.js +3 -2
- package/dist/web/public/js/notifications.js +66 -27
- package/dist/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
- package/dist/web/public/js/onboard/formProcessor.js +810 -255
- package/dist/web/public/js/onboard/index.js +328 -85
- package/dist/web/public/js/onboard/publishHandler.js +132 -0
- package/dist/web/public/js/onboard/state.js +61 -17
- package/dist/web/public/js/onboard/templates.js +217 -249
- package/dist/web/public/js/onboard/uiHandlers.js +679 -117
- package/dist/web/public/js/onboard/validationHandlers.js +378 -0
- package/dist/web/public/js/serverCategoryList.js +15 -2
- package/dist/web/public/onboard.html +191 -45
- package/dist/web/public/styles.css +91 -1
- package/dist/web/server.d.ts +0 -10
- package/dist/web/server.js +131 -22
- package/package.json +2 -2
- package/src/core/ConfigurationProvider.ts +15 -0
- package/src/core/InstallationService.ts +2 -7
- package/src/core/MCPManager.ts +26 -1
- package/src/core/RequirementService.ts +2 -9
- package/src/core/installers/clients/BaseClientInstaller.ts +196 -0
- package/src/core/installers/clients/ClientInstaller.ts +97 -608
- package/src/core/installers/clients/ClientInstallerFactory.ts +43 -0
- package/src/core/installers/clients/ClineInstaller.ts +135 -0
- package/src/core/installers/clients/GithubCopilotInstaller.ts +179 -0
- package/src/core/installers/clients/MSRooCodeInstaller.ts +133 -0
- package/src/core/installers/requirements/BaseInstaller.ts +13 -136
- package/src/core/installers/requirements/CommandInstaller.ts +9 -1
- package/src/core/installers/requirements/GeneralInstaller.ts +11 -4
- package/src/core/installers/requirements/NpmInstaller.ts +178 -61
- package/src/core/installers/requirements/PipInstaller.ts +68 -29
- package/src/core/onboard/FeedOnboardService.ts +346 -0
- package/src/core/onboard/OnboardProcessor.ts +305 -0
- package/src/core/onboard/OnboardStatus.ts +55 -0
- package/src/core/onboard/OnboardStatusManager.ts +188 -0
- package/src/core/types.ts +4 -5
- package/src/core/validators/FeedValidator.ts +79 -0
- package/src/core/validators/IServerValidator.ts +21 -0
- package/src/core/validators/SSEServerValidator.ts +43 -0
- package/src/core/validators/ServerValidatorFactory.ts +51 -0
- package/src/core/validators/StdioServerValidator.ts +259 -0
- package/src/services/InstallRequestValidator.ts +1 -1
- package/src/services/ServerService.ts +22 -7
- package/src/utils/adoUtils.ts +291 -0
- package/src/utils/clientUtils.ts +0 -44
- package/src/utils/githubUtils.ts +24 -0
- package/src/utils/macroExpressionUtils.ts +121 -0
- package/src/utils/osUtils.ts +89 -24
- package/src/web/contract/serverContract.ts +74 -0
- package/src/web/public/css/notifications.css +48 -17
- package/src/web/public/css/onboard.css +107 -0
- package/src/web/public/index.html +84 -16
- package/src/web/public/js/api.js +3 -6
- package/src/web/public/js/flights/flights.js +127 -0
- package/src/web/public/js/modal/installation.js +5 -5
- package/src/web/public/js/modal/modalSetup.js +3 -2
- package/src/web/public/js/notifications.js +66 -27
- package/src/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
- package/src/web/public/js/onboard/formProcessor.js +864 -0
- package/src/web/public/js/onboard/index.js +374 -0
- package/src/web/public/js/onboard/publishHandler.js +132 -0
- package/src/web/public/js/onboard/state.js +76 -0
- package/src/web/public/js/onboard/templates.js +343 -0
- package/src/web/public/js/onboard/uiHandlers.js +758 -0
- package/src/web/public/js/onboard/validationHandlers.js +378 -0
- package/src/web/public/js/serverCategoryList.js +15 -2
- package/src/web/public/onboard.html +296 -0
- package/src/web/public/styles.css +91 -1
- package/src/web/server.ts +167 -58
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import fs from 'fs/promises';
|
|
3
2
|
import { SETTINGS_DIR } from '../../constants.js';
|
|
4
|
-
import {
|
|
3
|
+
import { Logger } from '../../../utils/logger.js';
|
|
5
4
|
/**
|
|
6
5
|
* Abstract base class with common functionality for all requirement installers
|
|
7
6
|
*/
|
|
@@ -16,9 +15,10 @@ export class BaseInstaller {
|
|
|
16
15
|
* Check if updates are available for the requirement
|
|
17
16
|
* @param requirement The requirement to check
|
|
18
17
|
* @param currentStatus The current status of the requirement
|
|
18
|
+
* @param options Optional server install options.
|
|
19
19
|
* @returns The status of the requirement with update information
|
|
20
20
|
*/
|
|
21
|
-
async checkForUpdates(requirement, currentStatus) {
|
|
21
|
+
async checkForUpdates(requirement, currentStatus, options) {
|
|
22
22
|
try {
|
|
23
23
|
// If requirement is not installed, no need to check for updates
|
|
24
24
|
if (!currentStatus.installed) {
|
|
@@ -28,23 +28,7 @@ export class BaseInstaller {
|
|
|
28
28
|
if (!requirement.version.includes('latest')) {
|
|
29
29
|
return currentStatus;
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
// Check based on registry type
|
|
33
|
-
if (requirement.registry?.githubRelease) {
|
|
34
|
-
latestVersion = await this.getGitHubLatestVersion(requirement.registry.githubRelease.repository);
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
// Use common practice based on requirement type
|
|
38
|
-
switch (requirement.type) {
|
|
39
|
-
case 'npm':
|
|
40
|
-
latestVersion = await this.getNpmLatestVersion(requirement.name);
|
|
41
|
-
break;
|
|
42
|
-
case 'pip':
|
|
43
|
-
latestVersion = await this.getPipLatestVersion(requirement.name);
|
|
44
|
-
break;
|
|
45
|
-
// Add other types as needed
|
|
46
|
-
}
|
|
47
|
-
}
|
|
31
|
+
const latestVersion = await this.getLatestVersion(requirement, options);
|
|
48
32
|
// If we found a latest version and it's different from current
|
|
49
33
|
if (latestVersion && latestVersion !== currentStatus.version) {
|
|
50
34
|
return {
|
|
@@ -65,104 +49,9 @@ export class BaseInstaller {
|
|
|
65
49
|
}
|
|
66
50
|
catch (error) {
|
|
67
51
|
// Don't update status on error, just log it
|
|
68
|
-
|
|
52
|
+
Logger.log(`Error checking for updates for ${requirement.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
69
53
|
return currentStatus;
|
|
70
54
|
}
|
|
71
55
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Helper to handle artifact registry downloads
|
|
74
|
-
* @param requirement The requirement configuration
|
|
75
|
-
* @param registry The artifacts registry configuration
|
|
76
|
-
* @returns The registry URL
|
|
77
|
-
*/
|
|
78
|
-
async handleArtifactsRegistry(requirement, registry) {
|
|
79
|
-
if (!registry) {
|
|
80
|
-
throw new Error('Artifacts registry configuration is required');
|
|
81
|
-
}
|
|
82
|
-
const { registryUrl, assetName } = registry;
|
|
83
|
-
if (!registryUrl) {
|
|
84
|
-
throw new Error('Registry URL is required for artifacts downloads');
|
|
85
|
-
}
|
|
86
|
-
return registryUrl;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Helper to handle local path registry
|
|
90
|
-
* @param requirement The requirement configuration
|
|
91
|
-
* @param registry The local registry configuration
|
|
92
|
-
* @returns The local path or extracted asset path
|
|
93
|
-
*/
|
|
94
|
-
async handleLocalRegistry(requirement, registry) {
|
|
95
|
-
if (!registry) {
|
|
96
|
-
throw new Error('Local registry configuration is required');
|
|
97
|
-
}
|
|
98
|
-
const { localPath, assetName } = registry;
|
|
99
|
-
if (!localPath) {
|
|
100
|
-
throw new Error('Local path is required for local registry');
|
|
101
|
-
}
|
|
102
|
-
// Verify the local path exists
|
|
103
|
-
try {
|
|
104
|
-
await fs.access(localPath);
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
throw new Error(`Local path ${localPath} does not exist or is not accessible`);
|
|
108
|
-
}
|
|
109
|
-
// If the path is a zip file and assetName is specified, extract it
|
|
110
|
-
if (localPath.endsWith('.zip') && assetName) {
|
|
111
|
-
const extractDir = path.join(this.downloadsDir, path.basename(localPath, '.zip'));
|
|
112
|
-
await fs.mkdir(extractDir, { recursive: true });
|
|
113
|
-
// Extract the zip file
|
|
114
|
-
await extractZipFile(localPath, { dir: extractDir });
|
|
115
|
-
// Find the asset in the extracted directory
|
|
116
|
-
const assetPath = path.join(extractDir, assetName);
|
|
117
|
-
try {
|
|
118
|
-
await fs.access(assetPath);
|
|
119
|
-
return assetPath;
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
throw new Error(`Asset ${assetName} not found in extracted directory ${extractDir}`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return localPath;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Get the latest version available for a GitHub repository
|
|
129
|
-
* @param repository The GitHub repository in format 'owner/repo'
|
|
130
|
-
* @returns The latest version or tag
|
|
131
|
-
*/
|
|
132
|
-
async getGitHubLatestVersion(repository) {
|
|
133
|
-
try {
|
|
134
|
-
// Use GitHub CLI to get the latest release
|
|
135
|
-
const { stdout } = await this.execPromise(`gh release view --repo ${repository} --json tagName --jq .tagName`);
|
|
136
|
-
const latestTag = stdout.trim();
|
|
137
|
-
// Remove 'v' prefix if present
|
|
138
|
-
return latestTag.startsWith('v') ? latestTag.substring(1) : latestTag;
|
|
139
|
-
}
|
|
140
|
-
catch (error) {
|
|
141
|
-
// If gh command fails, try to get the latest tag
|
|
142
|
-
const { stdout } = await this.execPromise(`git ls-remote --tags --refs https://github.com/${repository}.git | sort -t '/' -k 3 -V | tail -n 1 | awk -F/ '{print $3}'`);
|
|
143
|
-
let latestTag = stdout.trim();
|
|
144
|
-
// Remove 'v' prefix if present
|
|
145
|
-
return latestTag.startsWith('v') ? latestTag.substring(1) : latestTag;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Get the latest version available for an NPM package
|
|
150
|
-
* @param packageName The name of the NPM package
|
|
151
|
-
* @returns The latest version
|
|
152
|
-
*/
|
|
153
|
-
async getNpmLatestVersion(packageName) {
|
|
154
|
-
const { stdout } = await this.execPromise(`npm view ${packageName} version`);
|
|
155
|
-
return stdout.trim();
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Get the latest version available for a pip package
|
|
159
|
-
* @param packageName The name of the pip package
|
|
160
|
-
* @returns The latest version
|
|
161
|
-
*/
|
|
162
|
-
async getPipLatestVersion(packageName, options) {
|
|
163
|
-
const pythonCmd = options?.settings?.pythonEnv || 'python';
|
|
164
|
-
const { stdout } = await this.execPromise(`${pythonCmd} -m pip index versions ${packageName} --pre=0 | grep -oP "(?<=Latest:\\s)[^\\s]+" | head -1`);
|
|
165
|
-
return stdout.trim();
|
|
166
|
-
}
|
|
167
56
|
}
|
|
168
57
|
//# sourceMappingURL=BaseInstaller.js.map
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { RequirementConfig, RequirementStatus } from '../../types.js';
|
|
1
|
+
import { RequirementConfig, RequirementStatus, ServerInstallOptions } from '../../types.js';
|
|
2
2
|
import { BaseInstaller } from './BaseInstaller.js';
|
|
3
3
|
/**
|
|
4
4
|
* Installer implementation for command-line tools
|
|
5
5
|
*/
|
|
6
6
|
export declare class CommandInstaller extends BaseInstaller {
|
|
7
|
+
/**
|
|
8
|
+
* Constructor for the CommandInstaller
|
|
9
|
+
* @param execPromise Function to execute commands
|
|
10
|
+
*/
|
|
11
|
+
getLatestVersion(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<string | undefined>;
|
|
7
12
|
/**
|
|
8
13
|
* Mapping of command names to their package IDs
|
|
9
14
|
* This handles special cases where the command name differs from the package ID
|
|
@@ -6,6 +6,13 @@ import { Logger } from '../../../utils/logger.js';
|
|
|
6
6
|
* Installer implementation for command-line tools
|
|
7
7
|
*/
|
|
8
8
|
export class CommandInstaller extends BaseInstaller {
|
|
9
|
+
/**
|
|
10
|
+
* Constructor for the CommandInstaller
|
|
11
|
+
* @param execPromise Function to execute commands
|
|
12
|
+
*/
|
|
13
|
+
getLatestVersion(requirement, options) {
|
|
14
|
+
throw new Error('Method not implemented.');
|
|
15
|
+
}
|
|
9
16
|
/**
|
|
10
17
|
* Mapping of command names to their package IDs
|
|
11
18
|
* This handles special cases where the command name differs from the package ID
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { RequirementConfig, RequirementStatus } from '../../types.js';
|
|
1
|
+
import { RequirementConfig, RequirementStatus, ServerInstallOptions } from '../../types.js';
|
|
2
2
|
import { BaseInstaller } from './BaseInstaller.js';
|
|
3
3
|
/**
|
|
4
4
|
* Installer implementation for general requirements (type 'other')
|
|
5
5
|
* This installer handles requirements that don't fit into specific package manager categories
|
|
6
6
|
*/
|
|
7
7
|
export declare class GeneralInstaller extends BaseInstaller {
|
|
8
|
+
/**
|
|
9
|
+
* Constructor for the GeneralInstaller
|
|
10
|
+
* @param execPromise Function to execute commands
|
|
11
|
+
*/
|
|
12
|
+
getLatestVersion(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<string | undefined>;
|
|
8
13
|
/**
|
|
9
14
|
* Check if this installer can handle the given requirement type
|
|
10
15
|
* @param requirement The requirement to check
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { BaseInstaller } from './BaseInstaller.js';
|
|
2
2
|
import { handleGitHubRelease } from '../../../utils/githubUtils.js';
|
|
3
|
+
import { handleArtifact } from '../../../utils/adoUtils.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
|
|
6
7
|
*/
|
|
7
8
|
export class GeneralInstaller extends BaseInstaller {
|
|
9
|
+
/**
|
|
10
|
+
* Constructor for the GeneralInstaller
|
|
11
|
+
* @param execPromise Function to execute commands
|
|
12
|
+
*/
|
|
13
|
+
getLatestVersion(requirement, options) {
|
|
14
|
+
throw new Error('Method not implemented.');
|
|
15
|
+
}
|
|
8
16
|
/**
|
|
9
17
|
* Check if this installer can handle the given requirement type
|
|
10
18
|
* @param requirement The requirement to check
|
|
@@ -54,10 +62,7 @@ export class GeneralInstaller extends BaseInstaller {
|
|
|
54
62
|
installPath = result.resolvedPath;
|
|
55
63
|
}
|
|
56
64
|
else if (requirement.registry.artifacts) {
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
else if (requirement.registry.local) {
|
|
60
|
-
installPath = await this.handleLocalRegistry(requirement, requirement.registry.local);
|
|
65
|
+
await handleArtifact(requirement, requirement.registry.artifacts);
|
|
61
66
|
}
|
|
62
67
|
else {
|
|
63
68
|
throw new Error('Invalid registry configuration');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RequirementConfig, RequirementStatus } from '../../types.js';
|
|
1
|
+
import { RequirementConfig, RequirementStatus, ServerInstallOptions } from '../../types.js';
|
|
2
2
|
import { BaseInstaller } from './BaseInstaller.js';
|
|
3
3
|
/**
|
|
4
4
|
* Installer implementation for NPM packages
|
|
@@ -12,15 +12,54 @@ export declare class NpmInstaller extends BaseInstaller {
|
|
|
12
12
|
canHandle(requirement: RequirementConfig): boolean;
|
|
13
13
|
supportCheckUpdates(): boolean;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Get the latest version available for the NPM package.
|
|
16
|
+
* @param requirement The requirement to check.
|
|
17
|
+
* @param options Optional server install options.
|
|
18
|
+
* @returns The latest version string, or undefined if not found or not applicable.
|
|
19
|
+
*/
|
|
20
|
+
getLatestVersion(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<string | undefined>;
|
|
21
|
+
/**
|
|
22
|
+
* Generates a dedicated folder path for a requirement.
|
|
23
|
+
* @param requirement The requirement configuration.
|
|
24
|
+
* @returns The path to the requirement's dedicated folder.
|
|
25
|
+
* @private
|
|
26
|
+
*/
|
|
27
|
+
private _getRequirementFolderPath;
|
|
28
|
+
/**
|
|
29
|
+
* Check if the NPM package is already installed in its dedicated folder.
|
|
16
30
|
* @param requirement The requirement to check
|
|
31
|
+
* @param options Installation options, may contain folderName.
|
|
17
32
|
* @returns The status of the requirement
|
|
18
33
|
*/
|
|
19
|
-
checkInstallation(requirement: RequirementConfig): Promise<RequirementStatus>;
|
|
34
|
+
checkInstallation(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
35
|
+
/**
|
|
36
|
+
* Retrieves the installed version of an NPM package from a given directory.
|
|
37
|
+
* @param requirementName The name of the NPM package.
|
|
38
|
+
* @param directory The directory to check for the package.
|
|
39
|
+
* @returns The installed version string, or undefined if not found or an error occurs.
|
|
40
|
+
* @private
|
|
41
|
+
*/
|
|
42
|
+
private _getInstalledVersion;
|
|
43
|
+
/**
|
|
44
|
+
* Get the name of the requirement, including registry information if applicable.
|
|
45
|
+
* @param requirement The requirement configuration.
|
|
46
|
+
* @returns The formatted requirement name.
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
private _getRequirementName;
|
|
50
|
+
/**
|
|
51
|
+
* Installs an NPM package into a dedicated local folder.
|
|
52
|
+
* @param requirement The requirement to install.
|
|
53
|
+
* @param packageSource This can be name@version, path to tgz, or path to folder.
|
|
54
|
+
* @param targetDir Target directory for installation.
|
|
55
|
+
* @returns The installed version of the package.
|
|
56
|
+
*/
|
|
57
|
+
private _installPackage;
|
|
20
58
|
/**
|
|
21
|
-
* Install the NPM package
|
|
22
|
-
* @param requirement The requirement to install
|
|
23
|
-
* @
|
|
59
|
+
* Install the NPM package.
|
|
60
|
+
* @param requirement The requirement to install.
|
|
61
|
+
* @param options Installation options, including pythonCommand for ADO (though not used by npm) and folderName.
|
|
62
|
+
* @returns The status of the installation.
|
|
24
63
|
*/
|
|
25
|
-
install(requirement: RequirementConfig): Promise<RequirementStatus>;
|
|
64
|
+
install(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
26
65
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { BaseInstaller } from './BaseInstaller.js';
|
|
2
2
|
import { compareVersions } from '../../../utils/versionUtils.js';
|
|
3
|
-
import { handleGitHubRelease } from '../../../utils/githubUtils.js';
|
|
3
|
+
import { handleGitHubRelease, getGitHubLatestVersion } from '../../../utils/githubUtils.js';
|
|
4
|
+
// Assuming getArtifactLatestVersion will be available in adoUtils.ts
|
|
5
|
+
import { handleArtifact as handleAdoArtifact, getArtifactLatestVersion } from '../../../utils/adoUtils.js';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
import { SETTINGS_DIR } from '../../constants.js'; // Corrected path
|
|
9
|
+
import { Logger } from '../../../utils/logger.js';
|
|
4
10
|
/**
|
|
5
11
|
* Installer implementation for NPM packages
|
|
6
12
|
*/
|
|
@@ -17,101 +23,187 @@ export class NpmInstaller extends BaseInstaller {
|
|
|
17
23
|
return true;
|
|
18
24
|
}
|
|
19
25
|
/**
|
|
20
|
-
*
|
|
26
|
+
* Get the latest version available for the NPM package.
|
|
27
|
+
* @param requirement The requirement to check.
|
|
28
|
+
* @param options Optional server install options.
|
|
29
|
+
* @returns The latest version string, or undefined if not found or not applicable.
|
|
30
|
+
*/
|
|
31
|
+
async getLatestVersion(requirement, options) {
|
|
32
|
+
if (requirement.registry) {
|
|
33
|
+
if (requirement.registry.githubRelease) {
|
|
34
|
+
return getGitHubLatestVersion(this.execPromise, requirement.registry.githubRelease.repository);
|
|
35
|
+
}
|
|
36
|
+
else if (requirement.registry.artifacts) {
|
|
37
|
+
// Assuming getArtifactLatestVersion exists and has a compatible signature
|
|
38
|
+
// This might need adjustment based on the actual implementation of getArtifactLatestVersion
|
|
39
|
+
const targetDir = options?.settings?.folderName || this._getRequirementFolderPath(requirement);
|
|
40
|
+
return getArtifactLatestVersion(requirement, requirement.registry.artifacts, options, targetDir);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Default: get common latest version from npm registry
|
|
44
|
+
const { stdout } = await this.execPromise(`npm view ${requirement.name} version`);
|
|
45
|
+
return stdout.trim();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Generates a dedicated folder path for a requirement.
|
|
49
|
+
* @param requirement The requirement configuration.
|
|
50
|
+
* @returns The path to the requirement's dedicated folder.
|
|
51
|
+
* @private
|
|
52
|
+
*/
|
|
53
|
+
_getRequirementFolderPath(requirement) {
|
|
54
|
+
return path.join(SETTINGS_DIR, 'npm_requirements', requirement.name, requirement.version.includes('latest') ? 'latest' : requirement.version);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if the NPM package is already installed in its dedicated folder.
|
|
21
58
|
* @param requirement The requirement to check
|
|
59
|
+
* @param options Installation options, may contain folderName.
|
|
22
60
|
* @returns The status of the requirement
|
|
23
61
|
*/
|
|
24
|
-
async checkInstallation(requirement) {
|
|
62
|
+
async checkInstallation(requirement, options) {
|
|
63
|
+
const requirementDir = options?.settings?.folderName || this._getRequirementFolderPath(requirement);
|
|
64
|
+
const requirementName = this._getRequirementName(requirement);
|
|
65
|
+
Logger.debug(`Checking installation for ${requirementName} in ${requirementDir}`);
|
|
25
66
|
try {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
67
|
+
const installedVersion = await this._getInstalledVersion(requirementName, requirementDir);
|
|
68
|
+
if (installedVersion) {
|
|
69
|
+
return {
|
|
70
|
+
name: requirement.name,
|
|
71
|
+
type: 'npm',
|
|
72
|
+
installed: true,
|
|
73
|
+
version: installedVersion,
|
|
74
|
+
npmPath: requirementDir,
|
|
75
|
+
inProgress: false,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// If package not found in dependencies or version is missing
|
|
29
79
|
return {
|
|
30
80
|
name: requirement.name,
|
|
31
81
|
type: 'npm',
|
|
32
|
-
installed:
|
|
33
|
-
|
|
34
|
-
|
|
82
|
+
installed: false,
|
|
83
|
+
inProgress: false,
|
|
84
|
+
error: `Package ${requirement.name} not found or version missing in npm list output in ${requirementDir}.`,
|
|
35
85
|
};
|
|
36
86
|
}
|
|
37
87
|
catch (error) {
|
|
88
|
+
// npm list command likely failed (e.g., package not installed, or requirementDir not an npm project)
|
|
89
|
+
Logger.debug(`Error checking installation for ${requirement.name} in ${requirementDir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
38
90
|
return {
|
|
39
91
|
name: requirement.name,
|
|
40
92
|
type: 'npm',
|
|
41
93
|
installed: false,
|
|
94
|
+
inProgress: false,
|
|
42
95
|
error: error instanceof Error ? error.message : String(error),
|
|
43
|
-
inProgress: false
|
|
44
96
|
};
|
|
45
97
|
}
|
|
46
98
|
}
|
|
47
99
|
/**
|
|
48
|
-
*
|
|
49
|
-
* @param
|
|
50
|
-
* @
|
|
100
|
+
* Retrieves the installed version of an NPM package from a given directory.
|
|
101
|
+
* @param requirementName The name of the NPM package.
|
|
102
|
+
* @param directory The directory to check for the package.
|
|
103
|
+
* @returns The installed version string, or undefined if not found or an error occurs.
|
|
104
|
+
* @private
|
|
51
105
|
*/
|
|
52
|
-
async
|
|
106
|
+
async _getInstalledVersion(requirementName, directory) {
|
|
53
107
|
try {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
108
|
+
const command = `npm list ${requirementName} --depth=0 --json --prefix "${directory}"`;
|
|
109
|
+
Logger.debug(`Getting installed version for ${requirementName} in ${directory} with command: ${command}`);
|
|
110
|
+
const { stdout } = await this.execPromise(command);
|
|
111
|
+
const listOutput = JSON.parse(stdout);
|
|
112
|
+
if (listOutput.dependencies && listOutput.dependencies[requirementName] && listOutput.dependencies[requirementName].version) {
|
|
113
|
+
return listOutput.dependencies[requirementName].version;
|
|
57
114
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
115
|
+
Logger.debug(`Package ${requirementName} not found in npm list output in ${directory}.`);
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
Logger.debug(`Error getting installed version for ${requirementName} in ${directory}: ${error instanceof Error ? error.message : String(error)}`);
|
|
120
|
+
return undefined; // Return undefined on error to indicate version couldn't be retrieved
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get the name of the requirement, including registry information if applicable.
|
|
125
|
+
* @param requirement The requirement configuration.
|
|
126
|
+
* @returns The formatted requirement name.
|
|
127
|
+
* @private
|
|
128
|
+
*/
|
|
129
|
+
_getRequirementName(requirement) {
|
|
130
|
+
return requirement.registry?.artifacts?.registryName ? `@${requirement.registry.artifacts.registryName}/${requirement.name}` : requirement.name;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Installs an NPM package into a dedicated local folder.
|
|
134
|
+
* @param requirement The requirement to install.
|
|
135
|
+
* @param packageSource This can be name@version, path to tgz, or path to folder.
|
|
136
|
+
* @param targetDir Target directory for installation.
|
|
137
|
+
* @returns The installed version of the package.
|
|
138
|
+
*/
|
|
139
|
+
async _installPackage(requirement, packageSource, targetDir) {
|
|
140
|
+
Logger.debug(`Installing NPM package from "${packageSource}" into "${targetDir}"`);
|
|
141
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
142
|
+
const installCommand = `npm install ${packageSource} --prefix "${targetDir}"`;
|
|
143
|
+
Logger.debug(`Executing install command: ${installCommand}`);
|
|
144
|
+
const requirementName = this._getRequirementName(requirement);
|
|
145
|
+
try {
|
|
146
|
+
const { stdout: installStdout, stderr: installStderr } = await this.execPromise(installCommand);
|
|
147
|
+
Logger.debug(`NPM install stdout for ${packageSource} in ${targetDir}: ${installStdout}`);
|
|
148
|
+
if (installStderr && !installStderr.toLowerCase().includes('added') && !installStderr.toLowerCase().includes('updated') && !installStderr.toLowerCase().includes('found 0 vulnerabilities')) {
|
|
149
|
+
// Log stderr if it's not just typical success noise
|
|
150
|
+
Logger.log(`NPM install stderr for ${packageSource} in ${targetDir}: ${installStderr}`);
|
|
151
|
+
}
|
|
152
|
+
const installedVersion = await this._getInstalledVersion(requirementName, targetDir);
|
|
153
|
+
if (installedVersion) {
|
|
154
|
+
Logger.log(`Successfully installed and verified ${requirementName}@${installedVersion} into ${targetDir}`);
|
|
155
|
+
return { version: installedVersion };
|
|
65
156
|
}
|
|
66
157
|
else {
|
|
67
|
-
|
|
68
|
-
|
|
158
|
+
throw new Error(`Successfully ran npm install for ${packageSource}, but ${requirement.name} version could not be determined via npm list in ${targetDir}.`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
Logger.error(`Failed to install or verify NPM package ${packageSource} into ${targetDir}`, error);
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Install the NPM package.
|
|
168
|
+
* @param requirement The requirement to install.
|
|
169
|
+
* @param options Installation options, including pythonCommand for ADO (though not used by npm) and folderName.
|
|
170
|
+
* @returns The status of the installation.
|
|
171
|
+
*/
|
|
172
|
+
async install(requirement, options) {
|
|
173
|
+
const targetDir = options?.settings?.folderName || this._getRequirementFolderPath(requirement);
|
|
174
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
175
|
+
try {
|
|
176
|
+
const status = await this.checkInstallation(requirement, { settings: { folderName: targetDir } });
|
|
177
|
+
if (status.installed && status.version && compareVersions(status.version, requirement.version) === 0 && !requirement.version.toLowerCase().includes('latest')) {
|
|
178
|
+
Logger.log(`${requirement.name}@${status.version} already installed in ${targetDir}.`);
|
|
179
|
+
return status;
|
|
180
|
+
}
|
|
181
|
+
let resolvedVersion = requirement.version;
|
|
182
|
+
let packageToInstall = `${requirement.name}@${requirement.version}`;
|
|
183
|
+
if (requirement.registry) {
|
|
69
184
|
if (requirement.registry.githubRelease) {
|
|
70
185
|
const result = await handleGitHubRelease(requirement, requirement.registry.githubRelease);
|
|
71
|
-
|
|
186
|
+
packageToInstall = result.resolvedPath;
|
|
72
187
|
resolvedVersion = result.resolvedVersion;
|
|
73
188
|
}
|
|
74
189
|
else if (requirement.registry.artifacts) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// Now install the package with the configured registry
|
|
79
|
-
const { stderr } = await this.execPromise(`npm install -g ${requirement.name}@${requirement.version}`);
|
|
80
|
-
if (stderr && !stderr.includes('added')) {
|
|
81
|
-
// Reset the registry to the default npm registry
|
|
82
|
-
await this.execPromise('npm config set registry https://registry.npmjs.org/');
|
|
83
|
-
throw new Error(stderr);
|
|
84
|
-
}
|
|
85
|
-
// Reset the registry to the default npm registry
|
|
86
|
-
await this.execPromise('npm config set registry https://registry.npmjs.org/');
|
|
87
|
-
return {
|
|
88
|
-
name: requirement.name,
|
|
89
|
-
type: 'npm',
|
|
90
|
-
installed: true,
|
|
91
|
-
version: requirement.version,
|
|
92
|
-
inProgress: false
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
else if (requirement.registry.local) {
|
|
96
|
-
packageSource = await this.handleLocalRegistry(requirement, requirement.registry.local);
|
|
190
|
+
const adoResult = await handleAdoArtifact(requirement, requirement.registry.artifacts, options?.settings?.pythonCommand, targetDir);
|
|
191
|
+
packageToInstall = `${adoResult.package}@${adoResult.version}`;
|
|
192
|
+
resolvedVersion = adoResult.version;
|
|
97
193
|
}
|
|
98
194
|
else {
|
|
99
|
-
throw new Error('Invalid registry configuration');
|
|
100
|
-
}
|
|
101
|
-
// Install from the package source
|
|
102
|
-
const { stderr } = await this.execPromise(`npm install -g "${packageSource}"`);
|
|
103
|
-
if (stderr && !stderr.includes('added')) {
|
|
104
|
-
const status = await this.checkInstallation(requirement);
|
|
105
|
-
if (!status.installed)
|
|
106
|
-
throw new Error(stderr);
|
|
195
|
+
throw new Error('Invalid registry configuration for npm.');
|
|
107
196
|
}
|
|
108
197
|
}
|
|
198
|
+
const finalInstallResult = await this._installPackage(requirement, packageToInstall, targetDir);
|
|
199
|
+
resolvedVersion = finalInstallResult.version;
|
|
109
200
|
return {
|
|
110
201
|
name: requirement.name,
|
|
111
202
|
type: 'npm',
|
|
112
203
|
installed: true,
|
|
113
204
|
version: resolvedVersion,
|
|
114
|
-
inProgress: false
|
|
205
|
+
inProgress: false,
|
|
206
|
+
npmPath: targetDir
|
|
115
207
|
};
|
|
116
208
|
}
|
|
117
209
|
catch (error) {
|
|
@@ -13,15 +13,24 @@ export declare class PipInstaller extends BaseInstaller {
|
|
|
13
13
|
*/
|
|
14
14
|
canHandle(requirement: RequirementConfig): boolean;
|
|
15
15
|
supportCheckUpdates(): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Get the latest version available for the pip package.
|
|
18
|
+
* @param requirement The requirement to check.
|
|
19
|
+
* @param options Optional server install options.
|
|
20
|
+
* @returns The latest version string, or undefined if not found or not applicable.
|
|
21
|
+
*/
|
|
22
|
+
getLatestVersion(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<string | undefined>;
|
|
16
23
|
/**
|
|
17
24
|
* Check if the Python package is already installed
|
|
18
25
|
* @param requirement The requirement to check
|
|
26
|
+
* @param options Optional server install options
|
|
19
27
|
* @returns The status of the requirement
|
|
20
28
|
*/
|
|
21
29
|
checkInstallation(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<RequirementStatus>;
|
|
22
30
|
/**
|
|
23
31
|
* Install the Python package
|
|
24
32
|
* @param requirement The requirement to install
|
|
33
|
+
* @param options Optional server install options
|
|
25
34
|
* @returns The status of the installation
|
|
26
35
|
*/
|
|
27
36
|
install(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<RequirementStatus>;
|