imcp 0.0.3 → 0.0.5

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.
Files changed (93) hide show
  1. package/README.md +5 -6
  2. package/dist/cli/commands/install.js +2 -0
  3. package/dist/cli/commands/list.js +1 -0
  4. package/dist/cli/index.js +1 -2
  5. package/dist/core/ConfigurationLoader.d.ts +32 -0
  6. package/dist/core/ConfigurationLoader.js +213 -0
  7. package/dist/core/ConfigurationProvider.d.ts +2 -3
  8. package/dist/core/ConfigurationProvider.js +13 -182
  9. package/dist/core/InstallationService.d.ts +8 -0
  10. package/dist/core/InstallationService.js +124 -96
  11. package/dist/core/RequirementService.d.ts +1 -1
  12. package/dist/core/RequirementService.js +5 -9
  13. package/dist/core/constants.js +14 -1
  14. package/dist/core/installers/BaseInstaller.d.ts +5 -4
  15. package/dist/core/installers/BaseInstaller.js +17 -28
  16. package/dist/core/installers/ClientInstaller.js +159 -39
  17. package/dist/core/installers/CommandInstaller.d.ts +1 -0
  18. package/dist/core/installers/CommandInstaller.js +3 -0
  19. package/dist/core/installers/GeneralInstaller.d.ts +1 -0
  20. package/dist/core/installers/GeneralInstaller.js +3 -0
  21. package/dist/core/installers/InstallerFactory.d.ts +9 -7
  22. package/dist/core/installers/InstallerFactory.js +10 -8
  23. package/dist/core/installers/NpmInstaller.d.ts +1 -0
  24. package/dist/core/installers/NpmInstaller.js +3 -0
  25. package/dist/core/installers/PipInstaller.d.ts +6 -3
  26. package/dist/core/installers/PipInstaller.js +21 -8
  27. package/dist/core/installers/RequirementInstaller.d.ts +4 -3
  28. package/dist/core/installers/clients/ClientInstaller.d.ts +23 -0
  29. package/dist/core/installers/clients/ClientInstaller.js +573 -0
  30. package/dist/core/installers/clients/ExtensionInstaller.d.ts +26 -0
  31. package/dist/core/installers/clients/ExtensionInstaller.js +149 -0
  32. package/dist/core/installers/index.d.ts +8 -6
  33. package/dist/core/installers/index.js +8 -6
  34. package/dist/core/installers/requirements/BaseInstaller.d.ts +59 -0
  35. package/dist/core/installers/requirements/BaseInstaller.js +168 -0
  36. package/dist/core/installers/requirements/CommandInstaller.d.ts +37 -0
  37. package/dist/core/installers/requirements/CommandInstaller.js +173 -0
  38. package/dist/core/installers/requirements/GeneralInstaller.d.ts +33 -0
  39. package/dist/core/installers/requirements/GeneralInstaller.js +86 -0
  40. package/dist/core/installers/requirements/InstallerFactory.d.ts +54 -0
  41. package/dist/core/installers/requirements/InstallerFactory.js +97 -0
  42. package/dist/core/installers/requirements/NpmInstaller.d.ts +26 -0
  43. package/dist/core/installers/requirements/NpmInstaller.js +128 -0
  44. package/dist/core/installers/requirements/PipInstaller.d.ts +28 -0
  45. package/dist/core/installers/requirements/PipInstaller.js +128 -0
  46. package/{src/core/installers/RequirementInstaller.ts → dist/core/installers/requirements/RequirementInstaller.d.ts} +33 -38
  47. package/dist/core/installers/requirements/RequirementInstaller.js +3 -0
  48. package/dist/core/types.d.ts +4 -1
  49. package/dist/services/ServerService.js +1 -1
  50. package/dist/utils/clientUtils.d.ts +0 -6
  51. package/dist/utils/clientUtils.js +3 -2
  52. package/dist/utils/githubUtils.d.ts +11 -0
  53. package/dist/utils/githubUtils.js +88 -0
  54. package/dist/utils/osUtils.d.ts +17 -0
  55. package/dist/utils/osUtils.js +184 -0
  56. package/dist/web/public/css/modal.css +97 -3
  57. package/dist/web/public/index.html +21 -2
  58. package/dist/web/public/js/modal.js +177 -28
  59. package/dist/web/public/js/serverCategoryDetails.js +12 -10
  60. package/dist/web/public/js/serverCategoryList.js +20 -5
  61. package/dist/web/public/modal.html +27 -13
  62. package/dist/web/server.js +1 -1
  63. package/package.json +2 -1
  64. package/src/cli/commands/install.ts +4 -2
  65. package/src/cli/commands/list.ts +1 -0
  66. package/src/cli/index.ts +1 -1
  67. package/src/core/ConfigurationLoader.ts +251 -0
  68. package/src/core/ConfigurationProvider.ts +13 -195
  69. package/src/core/InstallationService.ts +140 -106
  70. package/src/core/RequirementService.ts +5 -10
  71. package/src/core/constants.ts +15 -1
  72. package/src/core/installers/{ClientInstaller.ts → clients/ClientInstaller.ts} +185 -46
  73. package/src/core/installers/clients/ExtensionInstaller.ts +162 -0
  74. package/src/core/installers/index.ts +9 -7
  75. package/src/core/installers/{BaseInstaller.ts → requirements/BaseInstaller.ts} +10 -118
  76. package/src/core/installers/{CommandInstaller.ts → requirements/CommandInstaller.ts} +7 -3
  77. package/src/core/installers/{GeneralInstaller.ts → requirements/GeneralInstaller.ts} +6 -2
  78. package/src/core/installers/{InstallerFactory.ts → requirements/InstallerFactory.ts} +11 -9
  79. package/src/core/installers/{NpmInstaller.ts → requirements/NpmInstaller.ts} +7 -4
  80. package/src/core/installers/{PipInstaller.ts → requirements/PipInstaller.ts} +26 -10
  81. package/src/core/installers/requirements/RequirementInstaller.ts +41 -0
  82. package/src/core/types.ts +4 -1
  83. package/src/services/ServerService.ts +1 -1
  84. package/src/utils/clientUtils.ts +4 -2
  85. package/src/utils/githubUtils.ts +103 -0
  86. package/src/utils/osUtils.ts +206 -15
  87. package/src/web/public/css/modal.css +97 -3
  88. package/src/web/public/index.html +21 -2
  89. package/src/web/public/js/modal.js +177 -28
  90. package/src/web/public/js/serverCategoryDetails.js +12 -10
  91. package/src/web/public/js/serverCategoryList.js +20 -5
  92. package/src/web/public/modal.html +27 -13
  93. package/src/web/server.ts +1 -1
@@ -4,8 +4,9 @@ import { exec } from 'child_process';
4
4
  import util from 'util';
5
5
  import { createInstallerFactory } from './installers/index.js';
6
6
  import { SUPPORTED_CLIENTS } from './constants.js';
7
- import { ClientInstaller } from './installers/ClientInstaller.js';
7
+ import { ClientInstaller } from './installers/clients/ClientInstaller.js';
8
8
  import { ConfigurationProvider } from './ConfigurationProvider.js';
9
+ import { Logger } from '../utils/logger.js';
9
10
  const execPromise = util.promisify(exec);
10
11
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
12
  /**
@@ -54,94 +55,9 @@ export class InstallationService {
54
55
  }
55
56
  // Create new ClientInstaller instance for handling installation
56
57
  const clientInstaller = new ClientInstaller(categoryName, serverName, clients);
57
- // Check requirements readiness
58
- const requirementsReady = await configProvider.isRequirementsReady(categoryName, serverName);
59
- if (!requirementsReady) {
60
- // Get feed configuration to get requirements
61
- const feedConfig = await configProvider.getFeedConfiguration(categoryName);
62
- if (!feedConfig) {
63
- return {
64
- success: false,
65
- message: 'Feed configuration not found',
66
- status: [{
67
- status: 'failed',
68
- type: 'install',
69
- target: 'server',
70
- message: 'Feed configuration not found'
71
- }]
72
- };
73
- }
74
- // Find server config
75
- const serverConfig = feedConfig.mcpServers.find((s) => s.name === serverName);
76
- if (!serverConfig?.dependencies?.requirements) {
77
- return {
78
- success: false,
79
- message: 'Server configuration or requirements not found',
80
- status: [{
81
- status: 'failed',
82
- type: 'install',
83
- target: 'server',
84
- message: 'Server configuration or requirements not found'
85
- }]
86
- };
87
- }
88
- // Install requirements asynchronously
89
- for (const requirement of serverConfig.dependencies.requirements) {
90
- // Create full RequirementConfig from dependency requirement
91
- const feeds = await configProvider.getFeedConfiguration(categoryName);
92
- const requirementConfig = feeds?.requirements?.find((r) => r.name === requirement.name) || {
93
- name: requirement.name,
94
- version: requirement.version,
95
- type: 'npm' // Default to npm, can be enhanced to determine from requirement or config
96
- };
97
- const installer = this.installerFactory.getInstaller(requirementConfig);
98
- if (!installer) {
99
- await configProvider.updateRequirementStatus(categoryName, requirement.name, {
100
- name: requirement.name,
101
- type: requirementConfig.type,
102
- installed: false,
103
- error: `No installer found for requirement type: ${requirementConfig.type}`,
104
- operationStatus: {
105
- status: 'failed',
106
- type: 'install',
107
- target: 'requirement',
108
- message: `No installer found for requirement type: ${requirementConfig.type}`,
109
- operationId: this.generateOperationId()
110
- }
111
- });
112
- continue;
113
- }
114
- // Create operation status for requirement
115
- const operationStatus = {
116
- status: 'pending',
117
- type: 'install',
118
- target: 'requirement',
119
- message: `Installing requirement: ${requirement.name}`,
120
- operationId: this.generateOperationId()
121
- };
122
- // Update requirement status
123
- await configProvider.updateRequirementStatus(categoryName, requirement.name, {
124
- name: requirement.name,
125
- type: requirementConfig.type,
126
- installed: false,
127
- inProgress: true,
128
- operationStatus
129
- });
130
- // Start async installation
131
- installer.install(requirementConfig).then(async (installStatus) => {
132
- const status = {
133
- ...installStatus,
134
- operationStatus: {
135
- status: installStatus.installed ? 'completed' : 'failed',
136
- type: 'install',
137
- target: 'requirement',
138
- message: installStatus.installed ? `Requirement ${requirement.name} installed successfully` : `Failed to install ${requirement.name}`,
139
- operationId: operationStatus.operationId
140
- }
141
- };
142
- await configProvider.updateRequirementStatus(categoryName, requirement.name, status);
143
- });
144
- }
58
+ const requirementsResult = await this.checkAndInstallRequirements(categoryName, serverName, options);
59
+ if (requirementsResult) {
60
+ return requirementsResult;
145
61
  }
146
62
  // Process client installation regardless of requirements state
147
63
  // Each client installer will check requirements before actual installation
@@ -191,9 +107,9 @@ export class InstallationService {
191
107
  // Update requirement status to indicate update in progress
192
108
  await configProvider.updateRequirementStatus(categoryName, reqToUpdate.name, {
193
109
  ...currentStatus,
194
- name: reqToUpdate.name, // Ensure name is included
195
- type: currentStatus.type || 'unknown', // Ensure type is included
196
- installed: currentStatus.installed || false, // Ensure installed is included
110
+ name: reqToUpdate.name,
111
+ type: currentStatus.type || 'unknown',
112
+ installed: currentStatus.installed || false,
197
113
  inProgress: true,
198
114
  operationStatus: {
199
115
  status: 'in-progress',
@@ -212,9 +128,9 @@ export class InstallationService {
212
128
  // Update requirement status
213
129
  await configProvider.updateRequirementStatus(categoryName, reqToUpdate.name, {
214
130
  ...updatedStatus,
215
- name: reqToUpdate.name, // Ensure name is always included
216
- type: updatedStatus.type || currentStatus.type || 'unknown', // Ensure type is included
217
- installed: updatedStatus.installed, // This should be defined from updatedStatus
131
+ name: reqToUpdate.name,
132
+ type: updatedStatus.type || currentStatus.type || 'unknown',
133
+ installed: updatedStatus.installed,
218
134
  inProgress: false,
219
135
  operationStatus: {
220
136
  status: updatedStatus.installed ? 'completed' : 'failed',
@@ -224,7 +140,6 @@ export class InstallationService {
224
140
  ? `Successfully updated ${reqToUpdate.name} to version ${reqToUpdate.version}`
225
141
  : `Failed to update ${reqToUpdate.name} to version ${reqToUpdate.version}`
226
142
  },
227
- // Clear availableUpdate if update was successful
228
143
  availableUpdate: updatedStatus.installed ? undefined : currentStatus.availableUpdate
229
144
  });
230
145
  console.log(`Requirement ${reqToUpdate.name} updated to version ${reqToUpdate.version}`);
@@ -255,6 +170,119 @@ export class InstallationService {
255
170
  await updateCheckTracker.endOperation(operationKey);
256
171
  }
257
172
  }
173
+ /**
174
+ * Checks and installs requirements for a server if needed
175
+ * @param categoryName The category name
176
+ * @param serverName The server name
177
+ * @param options The installation options
178
+ * @returns A failure result if requirements check fails, null if requirements are satisfied
179
+ */
180
+ async checkAndInstallRequirements(categoryName, serverName, options) {
181
+ const configProvider = ConfigurationProvider.getInstance();
182
+ // Get feed configuration to get requirements
183
+ const feedConfig = await configProvider.getFeedConfiguration(categoryName);
184
+ if (!feedConfig) {
185
+ return {
186
+ success: false,
187
+ message: 'Feed configuration not found',
188
+ status: [{
189
+ status: 'failed',
190
+ type: 'install',
191
+ target: 'server',
192
+ message: 'Feed configuration not found'
193
+ }]
194
+ };
195
+ }
196
+ // Find server config and verify requirements
197
+ const serverConfig = feedConfig.mcpServers.find((s) => s.name === serverName);
198
+ if (!serverConfig?.dependencies?.requirements) {
199
+ Logger.debug(`No requirements for ${serverName}`);
200
+ return null;
201
+ }
202
+ // Check all requirements installation status
203
+ const requirementStatuses = await Promise.all(serverConfig.dependencies.requirements.map(async (req) => {
204
+ const reqConfig = feedConfig.requirements?.find((r) => r.name === req.name) || {
205
+ name: req.name,
206
+ version: req.version,
207
+ type: 'npm'
208
+ };
209
+ return await this.installerFactory.checkInstallation(reqConfig, options);
210
+ }));
211
+ // If all requirements are installed and ready, no need to proceed with installation
212
+ if (requirementStatuses.every(status => status.installed)) {
213
+ // Check if requirements are ready via ConfigurationProvider
214
+ const requirementsReady = await configProvider.isRequirementsReady(categoryName, serverName);
215
+ // Update requirement status if not ready
216
+ if (!requirementsReady) {
217
+ for (const status of requirementStatuses) {
218
+ await configProvider.updateRequirementStatus(categoryName, status.name, status);
219
+ }
220
+ }
221
+ return null;
222
+ }
223
+ // Sort requirements by order for installation
224
+ const sortedRequirements = [...serverConfig.dependencies.requirements].sort((a, b) => {
225
+ const orderA = a.order ?? Infinity;
226
+ const orderB = b.order ?? Infinity;
227
+ return orderA - orderB;
228
+ });
229
+ // Chain installations in sequence while keeping them non-blocking
230
+ await sortedRequirements.reduce((chain, requirement) => {
231
+ return chain.then(async () => {
232
+ const feeds = await configProvider.getFeedConfiguration(categoryName);
233
+ const requirementConfig = feeds?.requirements?.find((r) => r.name === requirement.name) || {
234
+ name: requirement.name,
235
+ version: requirement.version,
236
+ type: 'npm'
237
+ };
238
+ const installer = this.installerFactory.getInstaller(requirementConfig);
239
+ if (!installer) {
240
+ await configProvider.updateRequirementStatus(categoryName, requirement.name, {
241
+ name: requirement.name,
242
+ type: requirementConfig.type,
243
+ installed: false,
244
+ error: `No installer found for requirement type: ${requirementConfig.type}`,
245
+ operationStatus: {
246
+ status: 'failed',
247
+ type: 'install',
248
+ target: 'requirement',
249
+ message: `No installer found for requirement type: ${requirementConfig.type}`,
250
+ operationId: this.generateOperationId()
251
+ }
252
+ });
253
+ return;
254
+ }
255
+ const operationStatus = {
256
+ status: 'pending',
257
+ type: 'install',
258
+ target: 'requirement',
259
+ message: `Installing requirement: ${requirement.name}`,
260
+ operationId: this.generateOperationId()
261
+ };
262
+ await configProvider.updateRequirementStatus(categoryName, requirement.name, {
263
+ name: requirement.name,
264
+ type: requirementConfig.type,
265
+ installed: false,
266
+ inProgress: true,
267
+ operationStatus
268
+ });
269
+ return installer.install(requirementConfig, options).then(async (installStatus) => {
270
+ const status = {
271
+ ...installStatus,
272
+ operationStatus: {
273
+ status: installStatus.installed ? 'completed' : 'failed',
274
+ type: 'install',
275
+ target: 'requirement',
276
+ message: installStatus.installed ? `Requirement ${requirement.name} installed successfully` : `Failed to install ${requirement.name}`,
277
+ operationId: operationStatus.operationId
278
+ }
279
+ };
280
+ await configProvider.updateRequirementStatus(categoryName, requirement.name, status);
281
+ });
282
+ });
283
+ }, Promise.resolve());
284
+ return null;
285
+ }
258
286
  }
259
287
  // Export a singleton instance (optional)
260
288
  // export const installationService = new InstallationService();
@@ -28,7 +28,7 @@ export declare class RequirementService {
28
28
  * @param requirement The requirement to check for updates
29
29
  * @returns Updated status with available updates information
30
30
  */
31
- checkRequirementForUpdates(requirement: RequirementConfig): Promise<RequirementStatus>;
31
+ checkRequirementForUpdates(requirement: RequirementConfig, currentStatus: RequirementStatus): Promise<RequirementStatus>;
32
32
  /**
33
33
  * Update a requirement to a new version
34
34
  * @param requirement The requirement configuration
@@ -45,21 +45,17 @@ export class RequirementService {
45
45
  * @param requirement The requirement to check for updates
46
46
  * @returns Updated status with available updates information
47
47
  */
48
- async checkRequirementForUpdates(requirement) {
48
+ async checkRequirementForUpdates(requirement, currentStatus) {
49
49
  // Validate requirement
50
50
  this.validateRequirement(requirement);
51
51
  // Get current status
52
- const status = await this.checkRequirementStatus(requirement);
53
52
  // Check for updates using the appropriate installer
54
53
  const installer = this.installerFactory.getInstaller(requirement);
55
- if (!installer) {
56
- return status;
57
- }
58
- // If the installer supports update checking, use it
59
- if ('checkForUpdates' in installer) {
60
- return await installer.checkForUpdates(requirement, status);
54
+ if (!installer || !installer.supportCheckUpdates()) {
55
+ return currentStatus;
61
56
  }
62
- return status;
57
+ const status = await this.checkRequirementStatus(requirement);
58
+ return await installer.checkForUpdates(requirement, status);
63
59
  }
64
60
  /**
65
61
  * Update a requirement to a new version
@@ -53,14 +53,27 @@ const CODE_INSIDER_STRORAGE_DIR = (() => {
53
53
  */
54
54
  export const SUPPORTED_CLIENTS = {
55
55
  'MSRooCode': {
56
+ extension: {
57
+ extensionId: 'microsoftai.ms-roo-cline',
58
+ leastVersion: '0.0.8',
59
+ repository: 'ai-microsoft/roo-cline',
60
+ assetName: 'ms-roo-cline-${version}.vsix',
61
+ private: true
62
+ },
56
63
  codeSettingPath: path.join(CODE_STRORAGE_DIR, 'globalStorage', 'microsoftai.ms-roo-cline', 'settings', 'cline_mcp_settings.json'),
57
- codeInsiderSettingPath: path.join(CODE_INSIDER_STRORAGE_DIR, 'globalStorage', 'microsoftai.ms-roo-clinev', 'settings', 'cline_mcp_settings.json'),
64
+ codeInsiderSettingPath: path.join(CODE_INSIDER_STRORAGE_DIR, 'globalStorage', 'microsoftai.ms-roo-cline', 'settings', 'cline_mcp_settings.json'),
58
65
  },
59
66
  'Cline': {
67
+ extension: {
68
+ extensionId: 'saoudrizwan.claude-dev',
69
+ },
60
70
  codeSettingPath: path.join(CODE_STRORAGE_DIR, 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'),
61
71
  codeInsiderSettingPath: path.join(CODE_INSIDER_STRORAGE_DIR, 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'),
62
72
  },
63
73
  'GithubCopilot': {
74
+ extension: {
75
+ extensionId: 'github.copilot',
76
+ },
64
77
  codeSettingPath: path.join(CODE_STRORAGE_DIR, 'settings.json'),
65
78
  codeInsiderSettingPath: path.join(CODE_INSIDER_STRORAGE_DIR, 'settings.json'),
66
79
  },
@@ -1,4 +1,4 @@
1
- import { RequirementConfig, RequirementStatus, RegistryConfig } from '../types.js';
1
+ import { RequirementConfig, RequirementStatus, RegistryConfig, ServerInstallOptions } from '../types.js';
2
2
  import { RequirementInstaller } from './RequirementInstaller.js';
3
3
  /**
4
4
  * Abstract base class with common functionality for all requirement installers
@@ -14,8 +14,9 @@ export declare abstract class BaseInstaller implements RequirementInstaller {
14
14
  stderr: string;
15
15
  }>);
16
16
  abstract canHandle(requirement: RequirementConfig): boolean;
17
- abstract install(requirement: RequirementConfig): Promise<RequirementStatus>;
18
- abstract checkInstallation(requirement: RequirementConfig): Promise<RequirementStatus>;
17
+ abstract supportCheckUpdates(): boolean;
18
+ abstract install(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<RequirementStatus>;
19
+ abstract checkInstallation(requirement: RequirementConfig, options?: ServerInstallOptions): Promise<RequirementStatus>;
19
20
  /**
20
21
  * Check if updates are available for the requirement
21
22
  * @param requirement The requirement to check
@@ -69,5 +70,5 @@ export declare abstract class BaseInstaller implements RequirementInstaller {
69
70
  * @param packageName The name of the pip package
70
71
  * @returns The latest version
71
72
  */
72
- protected getPipLatestVersion(packageName: string): Promise<string>;
73
+ protected getPipLatestVersion(packageName: string, options?: ServerInstallOptions): Promise<string>;
73
74
  }
@@ -96,31 +96,19 @@ export class BaseInstaller {
96
96
  let resolvedAssetsName = assetsName || '';
97
97
  const { stdout } = await this.execPromise(`gh release view --repo ${repository} --json tagName --jq .tagName`);
98
98
  const latestTag = stdout.trim();
99
- // Handle latest version detection
100
- if (version.includes('${latest}') || version === 'latest') {
101
- let latestVersion = latestTag;
102
- if (latestVersion.startsWith('v') && version.startsWith('v')) {
103
- latestVersion = latestVersion.substring(1); // Remove 'v' prefix if present
104
- // Replace ${latest} in version and asset names
105
- version = version.replace('${latest}', latestVersion);
106
- if (assetsName) {
107
- resolvedAssetsName = assetsName.replace('${latest}', latestVersion).replace('${version}', version);
108
- }
109
- if (assetName) {
110
- resolvedAssetName = assetName.replace('${latest}', latestVersion).replace('${version}', version);
111
- }
112
- }
113
- }
114
- else {
115
- if (assetsName) {
116
- resolvedAssetsName = assetsName.replace('${latest}', version).replace('${version}', version);
117
- }
118
- if (assetName) {
119
- resolvedAssetName = assetName.replace('${latest}', version).replace('${version}', version);
120
- }
121
- Logger.debug(`Downloading ${requirement.name} from GitHub release ${repository} version ${version}`);
122
- Logger.debug(`ResolvedAssetsName} ${resolvedAssetName}; ResolvedAsetName} ${resolvedAssetName}`);
123
- }
99
+ let latestVersion = latestTag;
100
+ const tagWithVPrefix = latestVersion.startsWith('v');
101
+ if (tagWithVPrefix)
102
+ latestVersion = latestVersion.substring(1); // Remove 'v' prefix if present
103
+ version = version.includes("latest") ? latestVersion : version;
104
+ if (assetsName) {
105
+ resolvedAssetsName = assetsName.replace('${latest}', version).replace('${version}', version);
106
+ }
107
+ if (assetName) {
108
+ resolvedAssetName = assetName.replace('${latest}', version).replace('${version}', version);
109
+ }
110
+ Logger.debug(`Downloading ${requirement.name} from GitHub release ${repository} version ${version}`);
111
+ Logger.debug(`ResolvedAssetsName} ${resolvedAssetName}; ResolvedAsetName} ${resolvedAssetName}`);
124
112
  const pattern = resolvedAssetsName ? resolvedAssetsName : resolvedAssetName;
125
113
  Logger.debug(`Resolved pattern: ${pattern}`);
126
114
  if (!pattern) {
@@ -129,7 +117,7 @@ export class BaseInstaller {
129
117
  // Download the release asset
130
118
  const downloadPath = path.join(this.downloadsDir, path.basename(pattern));
131
119
  if (!await this.fileExists(downloadPath)) {
132
- await this.execPromise(`gh release download ${version.startsWith('v') ? version : `v${version}`} --repo ${repository} --pattern "${pattern}" -O "${downloadPath}"`);
120
+ await this.execPromise(`gh release download ${tagWithVPrefix ? `v${version}` : version} --repo ${repository} --pattern "${pattern}" -O "${downloadPath}"`);
133
121
  }
134
122
  // Handle zip file extraction if the downloaded file is a zip
135
123
  if (downloadPath.endsWith('.zip')) {
@@ -256,8 +244,9 @@ export class BaseInstaller {
256
244
  * @param packageName The name of the pip package
257
245
  * @returns The latest version
258
246
  */
259
- async getPipLatestVersion(packageName) {
260
- const { stdout } = await this.execPromise(`pip index versions ${packageName} --pre=0 | grep -oP "(?<=Latest:\\s)[^\\s]+" | head -1`);
247
+ async getPipLatestVersion(packageName, options) {
248
+ const pythonCmd = options?.settings?.pythonEnv || 'python';
249
+ const { stdout } = await this.execPromise(`${pythonCmd} -m pip index versions ${packageName} --pre=0 | grep -oP "(?<=Latest:\\s)[^\\s]+" | head -1`);
261
250
  return stdout.trim();
262
251
  }
263
252
  }