imcp 0.0.19 → 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.
Files changed (183) hide show
  1. package/.roo/rules-code/rules.md +88 -0
  2. package/dist/cli/index.js +1 -45
  3. package/dist/core/installers/clients/BaseClientInstaller.d.ts +1 -5
  4. package/dist/core/installers/clients/BaseClientInstaller.js +40 -38
  5. package/dist/core/installers/clients/ClientInstaller.d.ts +9 -9
  6. package/dist/core/installers/clients/ClientInstaller.js +105 -99
  7. package/dist/core/installers/requirements/BaseInstaller.d.ts +9 -1
  8. package/dist/core/installers/requirements/CommandInstaller.d.ts +9 -1
  9. package/dist/core/installers/requirements/CommandInstaller.js +46 -12
  10. package/dist/core/installers/requirements/GeneralInstaller.d.ts +11 -1
  11. package/dist/core/installers/requirements/GeneralInstaller.js +46 -10
  12. package/dist/core/installers/requirements/InstallerFactory.d.ts +3 -1
  13. package/dist/core/installers/requirements/InstallerFactory.js +3 -2
  14. package/dist/core/installers/requirements/NpmInstaller.d.ts +4 -2
  15. package/dist/core/installers/requirements/NpmInstaller.js +38 -22
  16. package/dist/core/installers/requirements/PipInstaller.d.ts +3 -1
  17. package/dist/core/installers/requirements/PipInstaller.js +58 -36
  18. package/dist/core/installers/requirements/RequirementInstaller.d.ts +4 -1
  19. package/dist/core/loaders/InstallOperationManager.d.ts +115 -0
  20. package/dist/core/loaders/InstallOperationManager.js +311 -0
  21. package/dist/core/loaders/SystemSettingsManager.d.ts +54 -0
  22. package/dist/core/loaders/SystemSettingsManager.js +257 -0
  23. package/dist/core/metadatas/constants.d.ts +7 -0
  24. package/dist/core/metadatas/constants.js +7 -0
  25. package/dist/core/metadatas/recordingConstants.d.ts +44 -0
  26. package/dist/core/metadatas/recordingConstants.js +45 -0
  27. package/dist/core/metadatas/types.d.ts +21 -0
  28. package/dist/core/onboard/FeedOnboardService.d.ts +7 -3
  29. package/dist/core/onboard/FeedOnboardService.js +52 -5
  30. package/dist/core/onboard/InstallOperationManager.d.ts +23 -0
  31. package/dist/core/onboard/InstallOperationManager.js +144 -0
  32. package/dist/core/onboard/OnboardStatusManager.js +2 -1
  33. package/dist/core/validators/StdioServerValidator.js +4 -3
  34. package/dist/services/InstallationService.d.ts +2 -37
  35. package/dist/services/InstallationService.js +45 -313
  36. package/dist/services/MCPManager.d.ts +1 -1
  37. package/dist/services/MCPManager.js +53 -47
  38. package/dist/services/RequirementService.d.ts +85 -12
  39. package/dist/services/RequirementService.js +488 -49
  40. package/dist/services/ServerService.d.ts +0 -6
  41. package/dist/services/ServerService.js +0 -74
  42. package/dist/services/TelemetryService.d.ts +15 -0
  43. package/dist/services/TelemetryService.js +54 -0
  44. package/dist/utils/adoUtils.js +6 -3
  45. package/dist/utils/githubAuth.js +65 -0
  46. package/dist/utils/logger.d.ts +16 -0
  47. package/dist/utils/logger.js +78 -1
  48. package/dist/utils/macroExpressionUtils.js +3 -25
  49. package/dist/utils/osUtils.d.ts +22 -1
  50. package/dist/utils/osUtils.js +92 -1
  51. package/dist/utils/versionUtils.d.ts +20 -0
  52. package/dist/utils/versionUtils.js +76 -0
  53. package/dist/web/public/css/modal.css +292 -1
  54. package/dist/web/public/css/serverCategoryList.css +120 -0
  55. package/dist/web/public/css/serverDetails.css +14 -1
  56. package/dist/web/public/index.html +126 -21
  57. package/dist/web/public/js/flights/flights.js +1 -1
  58. package/dist/web/public/js/modal/index.js +8 -14
  59. package/dist/web/public/js/modal/installModal.js +3 -4
  60. package/dist/web/public/js/modal/installation.js +122 -137
  61. package/dist/web/public/js/modal/loadingModal.js +155 -25
  62. package/dist/web/public/js/modal/messageQueue.js +45 -101
  63. package/dist/web/public/js/modal/modalSetup.js +125 -43
  64. package/dist/web/public/js/modal/modalUtils.js +0 -12
  65. package/dist/web/public/js/modal.js +23 -10
  66. package/dist/web/public/js/onboard/formProcessor.js +18 -11
  67. package/dist/web/public/js/onboard/publishHandler.js +35 -3
  68. package/dist/web/public/js/onboard/templates.js +5 -1
  69. package/dist/web/public/js/onboard/uiHandlers.js +266 -39
  70. package/dist/web/public/js/onboard/validationHandlers.js +71 -39
  71. package/dist/web/public/js/serverCategoryDetails.js +60 -11
  72. package/dist/web/public/js/serverCategoryList.js +93 -9
  73. package/dist/web/public/js/settings.js +314 -0
  74. package/dist/web/public/onboard.html +2 -2
  75. package/dist/web/public/settings.html +135 -0
  76. package/dist/web/public/styles.css +32 -0
  77. package/dist/web/server.js +93 -1
  78. package/{src/web/public/js/onboard → docs}/ONBOARDING_PAGE_DESIGN.md +15 -125
  79. package/docs/Telemetry.md +136 -0
  80. package/memory-bank/activeContext.md +26 -0
  81. package/memory-bank/decisionLog.md +91 -0
  82. package/memory-bank/productContext.md +41 -0
  83. package/memory-bank/progress.md +35 -0
  84. package/memory-bank/systemPatterns.md +10 -0
  85. package/package.json +2 -1
  86. package/src/cli/index.ts +1 -48
  87. package/src/core/installers/clients/BaseClientInstaller.ts +64 -50
  88. package/src/core/installers/clients/ClientInstaller.ts +130 -130
  89. package/src/core/installers/requirements/BaseInstaller.ts +9 -1
  90. package/src/core/installers/requirements/CommandInstaller.ts +47 -13
  91. package/src/core/installers/requirements/GeneralInstaller.ts +48 -10
  92. package/src/core/installers/requirements/InstallerFactory.ts +4 -3
  93. package/src/core/installers/requirements/NpmInstaller.ts +90 -68
  94. package/src/core/installers/requirements/PipInstaller.ts +81 -55
  95. package/src/core/installers/requirements/RequirementInstaller.ts +4 -3
  96. package/src/core/loaders/InstallOperationManager.ts +367 -0
  97. package/src/core/loaders/SystemSettingsManager.ts +278 -0
  98. package/src/core/metadatas/constants.ts +9 -0
  99. package/src/core/metadatas/recordingConstants.ts +62 -0
  100. package/src/core/metadatas/types.ts +23 -0
  101. package/src/core/onboard/FeedOnboardService.ts +59 -5
  102. package/src/core/onboard/OnboardStatusManager.ts +2 -1
  103. package/src/core/validators/StdioServerValidator.ts +4 -3
  104. package/src/services/InstallationService.ts +54 -399
  105. package/src/services/MCPManager.ts +61 -64
  106. package/src/services/RequirementService.ts +564 -67
  107. package/src/services/ServerService.ts +0 -90
  108. package/src/services/TelemetryService.ts +59 -0
  109. package/src/utils/adoUtils.ts +6 -4
  110. package/src/utils/githubAuth.ts +84 -1
  111. package/src/utils/logger.ts +83 -1
  112. package/src/utils/macroExpressionUtils.ts +4 -21
  113. package/src/utils/osUtils.ts +92 -1
  114. package/src/utils/versionUtils.ts +98 -13
  115. package/src/web/public/css/modal.css +292 -1
  116. package/src/web/public/css/serverCategoryList.css +120 -0
  117. package/src/web/public/css/serverDetails.css +14 -1
  118. package/src/web/public/index.html +126 -21
  119. package/src/web/public/js/flights/flights.js +1 -1
  120. package/src/web/public/js/modal/index.js +8 -14
  121. package/src/web/public/js/modal/installModal.js +3 -4
  122. package/src/web/public/js/modal/installation.js +122 -137
  123. package/src/web/public/js/modal/loadingModal.js +155 -25
  124. package/src/web/public/js/modal/modalSetup.js +125 -43
  125. package/src/web/public/js/modal/modalUtils.js +0 -12
  126. package/src/web/public/js/modal.js +23 -10
  127. package/src/web/public/js/onboard/formProcessor.js +18 -11
  128. package/src/web/public/js/onboard/publishHandler.js +35 -3
  129. package/src/web/public/js/onboard/templates.js +5 -1
  130. package/src/web/public/js/onboard/uiHandlers.js +266 -39
  131. package/src/web/public/js/onboard/validationHandlers.js +71 -39
  132. package/src/web/public/js/serverCategoryDetails.js +60 -11
  133. package/src/web/public/js/serverCategoryList.js +93 -9
  134. package/src/web/public/js/settings.js +314 -0
  135. package/src/web/public/onboard.html +2 -2
  136. package/src/web/public/settings.html +135 -0
  137. package/src/web/public/styles.css +32 -0
  138. package/src/web/server.ts +96 -1
  139. package/dist/cli/commands/start.d.ts +0 -2
  140. package/dist/cli/commands/start.js +0 -32
  141. package/dist/cli/commands/sync.d.ts +0 -2
  142. package/dist/cli/commands/sync.js +0 -17
  143. package/dist/core/ConfigurationLoader.d.ts +0 -32
  144. package/dist/core/ConfigurationLoader.js +0 -236
  145. package/dist/core/ConfigurationProvider.d.ts +0 -35
  146. package/dist/core/ConfigurationProvider.js +0 -375
  147. package/dist/core/InstallationService.d.ts +0 -50
  148. package/dist/core/InstallationService.js +0 -350
  149. package/dist/core/MCPManager.d.ts +0 -28
  150. package/dist/core/MCPManager.js +0 -188
  151. package/dist/core/RequirementService.d.ts +0 -40
  152. package/dist/core/RequirementService.js +0 -110
  153. package/dist/core/ServerSchemaLoader.d.ts +0 -11
  154. package/dist/core/ServerSchemaLoader.js +0 -43
  155. package/dist/core/ServerSchemaProvider.d.ts +0 -17
  156. package/dist/core/ServerSchemaProvider.js +0 -120
  157. package/dist/core/constants.d.ts +0 -47
  158. package/dist/core/constants.js +0 -94
  159. package/dist/core/installers/BaseInstaller.d.ts +0 -74
  160. package/dist/core/installers/BaseInstaller.js +0 -253
  161. package/dist/core/installers/ClientInstaller.d.ts +0 -23
  162. package/dist/core/installers/ClientInstaller.js +0 -564
  163. package/dist/core/installers/CommandInstaller.d.ts +0 -37
  164. package/dist/core/installers/CommandInstaller.js +0 -173
  165. package/dist/core/installers/GeneralInstaller.d.ts +0 -33
  166. package/dist/core/installers/GeneralInstaller.js +0 -85
  167. package/dist/core/installers/InstallerFactory.d.ts +0 -54
  168. package/dist/core/installers/InstallerFactory.js +0 -97
  169. package/dist/core/installers/NpmInstaller.d.ts +0 -26
  170. package/dist/core/installers/NpmInstaller.js +0 -127
  171. package/dist/core/installers/PipInstaller.d.ts +0 -28
  172. package/dist/core/installers/PipInstaller.js +0 -127
  173. package/dist/core/installers/RequirementInstaller.d.ts +0 -33
  174. package/dist/core/installers/RequirementInstaller.js +0 -3
  175. package/dist/core/types.d.ts +0 -166
  176. package/dist/core/types.js +0 -16
  177. package/dist/services/InstallRequestValidator.d.ts +0 -21
  178. package/dist/services/InstallRequestValidator.js +0 -99
  179. package/dist/web/public/js/modal/installHandler.js +0 -227
  180. package/dist/web/public/js/modal/loadingUI.js +0 -74
  181. package/dist/web/public/js/modal/modalUI.js +0 -214
  182. package/dist/web/public/js/modal/version.js +0 -20
  183. package/src/web/public/js/modal/messageQueue.js +0 -112
@@ -1,72 +1,78 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
- import { exec } from 'child_process';
5
- import util from 'util';
6
1
  import {
7
2
  ServerInstallOptions,
8
- ServerOperationResult,
9
- FeedConfiguration,
10
- RequirementConfig,
11
- OperationStatus,
12
- RequirementStatus,
13
- McpConfig
3
+ ServerOperationResult
14
4
  } from '../core/metadatas/types.js';
15
- import { RequirementInstaller, InstallerFactory, createInstallerFactory } from '../core/installers/index.js';
16
- import { SUPPORTED_CLIENTS } from '../core/metadatas/constants.js';
17
5
  import { ClientInstaller } from '../core/installers/clients/ClientInstaller.js';
18
6
  import { ConfigurationProvider } from '../core/loaders/ConfigurationProvider.js';
19
7
  import { Logger } from '../utils/logger.js';
20
-
21
- const execPromise = util.promisify(exec);
22
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+ import { requirementService } from './RequirementService.js';
9
+ import { InstallOperationManager } from '../core/loaders/InstallOperationManager.js';
10
+ import * as RecordingConstants from '../core/metadatas/recordingConstants.js';
23
11
 
24
12
  /**
25
13
  * Handles the actual installation process for an MCP server.
26
14
  */
27
15
  export class InstallationService {
28
- private activeInstallations: Map<string, OperationStatus> = new Map();
29
- private installerFactory: InstallerFactory;
30
16
 
31
17
  constructor() {
32
- this.installerFactory = createInstallerFactory();
33
- }
34
-
35
- private generateOperationId(): string {
36
- return `install-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
18
+ // Constructor is now empty after removing installerFactory initialization
37
19
  }
38
20
 
39
21
  /**
40
- * Installs a server based on the provided options and feed configuration.
22
+ * Installs a server based on the provided options.
23
+ * @param categoryName The category name of the server.
41
24
  * @param serverName The name of the server to install.
42
25
  * @param options The installation options.
43
26
  * @returns A result object indicating success or failure.
44
27
  */
45
28
  async install(categoryName: string, serverName: string, options: ServerInstallOptions): Promise<ServerOperationResult> {
29
+ // Reset any previous operation status for this server before starting a new one.
30
+ const recoder = await InstallOperationManager.getInstance(categoryName, serverName).resetOperation();
46
31
  const configProvider = ConfigurationProvider.getInstance();
47
- const clients = options.targetClients || Object.keys(SUPPORTED_CLIENTS);
32
+
33
+ const clients = options.targetClients || [];
48
34
 
49
35
  // Process updates for requirements if specified in options
50
- // Fire off requirement updates in the background without awaiting completion
51
36
  if (options.requirements && options.requirements.length > 0) {
52
- // Start the process but don't await it - it will run in the background
53
- this.processRequirementUpdates(categoryName, serverName, options)
54
- .catch(error => {
55
- console.error(`Error in background requirement updates: ${error instanceof Error ? error.message : String(error)}`);
56
- });
37
+ recoder.recordingAsync(
38
+ () => requirementService.processRequirementUpdates(categoryName, serverName, options),
39
+ {
40
+ stepName: RecordingConstants.STEP_PROCESS_REQUIREMENT_UPDATES_SERVICE,
41
+ onError: (error) => {
42
+ const errorMsg = `Error in background requirement updates: ${error instanceof Error ? error.message : String(error)}`;
43
+ Logger.error(errorMsg);
44
+ return errorMsg;
45
+ },
46
+ onComplete: () => {
47
+ if (clients.length === 0) recoder.markOverallStatus('completed', 'Requirement updates completed.');
48
+ }
49
+ }
50
+ );
57
51
  }
58
52
 
59
- // Check if server is already ready
60
- const isReady = await configProvider.isServerReady(categoryName, serverName, clients);
53
+ if (!clients || clients.length === 0) {
54
+ const message = 'No clients specified for installation.';
55
+ return { success: true, message };
56
+ }
57
+
58
+ // Check if the server is already installed and ready
59
+ const readyMessage = 'Server and clients are already installed and ready';
60
+ const isReady = await recoder.recording(
61
+ () => configProvider.isServerReady(categoryName, serverName, clients),
62
+ {
63
+ stepName: RecordingConstants.STEP_CHECK_SERVER_READINESS,
64
+ inProgressMessage: 'Checking if server is already ready.',
65
+ endMessage: (ready: boolean) => ready ? 'Server and clients are already installed and ready' : 'Server is not ready. Proceeding with installation.',
66
+ }
67
+ )
61
68
  if (isReady) {
62
69
  return {
63
- success: true,
64
- message: 'Server and clients are already installed and ready',
70
+ success: true, message: readyMessage,
65
71
  status: [{
66
72
  status: 'completed',
67
73
  type: 'install',
68
74
  target: 'server',
69
- message: 'Server and clients are already installed and ready'
75
+ message: readyMessage,
70
76
  }]
71
77
  };
72
78
  }
@@ -74,374 +80,23 @@ export class InstallationService {
74
80
  // Create new ClientInstaller instance for handling installation
75
81
  const clientInstaller = new ClientInstaller(categoryName, serverName, clients);
76
82
 
77
- const requirementsResult = await this.checkAndInstallRequirements(categoryName, serverName, options);
78
- if (requirementsResult) {
79
- return requirementsResult;
80
- }
81
-
82
- // Process client installation regardless of requirements state
83
- // Each client installer will check requirements before actual installation
84
- return await clientInstaller.install(options);
85
- }
86
-
87
- /**
88
- * Process requirement updates specified in serverInstallOptions
89
- * All updates are processed in parallel for maximum efficiency
90
- * @param categoryName The category name
91
- * @param serverName The server name
92
- * @param requirements The requirements to update
93
- */
94
- private async processRequirementUpdates(categoryName: string, serverName: string, options: ServerInstallOptions): Promise<void> {
95
- // Use UpdateCheckTracker to prevent concurrent updates
96
- const updateCheckTracker = await import('../utils/UpdateCheckTracker.js').then(m => m.updateCheckTracker);
97
- const operationKey = `requirement-updates-${categoryName}-${serverName}`;
98
-
99
- // Check if there's already an update operation in progress for this server
100
- const canProceed = await updateCheckTracker.startOperation(operationKey);
101
- if (!canProceed) {
102
- console.log(`Requirement updates for ${categoryName}/${serverName} already in progress, skipping`);
103
- return;
104
- }
105
-
106
- try {
107
- const configProvider = ConfigurationProvider.getInstance();
108
- const feedConfig = await configProvider.getFeedConfiguration(categoryName);
109
-
110
- if (!feedConfig) {
111
- console.error(`Feed configuration not found for category: ${categoryName}`);
112
- return;
113
- }
114
-
115
- // Import the RequirementService
116
- const { requirementService } = await import('./RequirementService.js');
117
-
118
- // Create an array of promises to update all requirements in parallel
119
- const updatePromises = options.requirements?.map(async (reqToUpdate) => {
120
- try {
121
- // Find the full requirement config
122
- const reqConfig = feedConfig.requirements?.find((r: RequirementConfig) => r.name === reqToUpdate.name);
123
-
124
- if (!reqConfig) {
125
- console.error(`Requirement configuration not found for: ${reqToUpdate.name}`);
126
- return;
127
- }
128
-
129
- // Get current status
130
- const currentStatus = await configProvider.getRequirementStatus(categoryName, reqToUpdate.name);
131
-
132
- if (!currentStatus) {
133
- console.error(`No current status found for requirement: ${reqToUpdate.name}`);
134
- return;
135
- }
136
-
137
- // Update requirement status to indicate update in progress
138
- await configProvider.updateRequirementStatus(categoryName, reqToUpdate.name, {
139
- ...currentStatus,
140
- name: reqToUpdate.name,
141
- type: currentStatus.type || 'unknown',
142
- installed: currentStatus.installed || false,
143
- inProgress: true,
144
- operationStatus: {
145
- status: 'in-progress',
146
- type: 'update',
147
- target: 'requirement',
148
- message: `Updating ${reqToUpdate.name} from ${currentStatus.version || 'unknown'} to ${reqToUpdate.version}`
149
- }
150
- });
151
-
152
-
153
- // For pip requirements, check if we have a stored pythonEnv
154
- if (reqConfig.type === 'pip' && currentStatus.pythonEnv && !options?.settings?.pythonEnv) {
155
- options = {
156
- ...options,
157
- settings: { ...options?.settings, pythonEnv: currentStatus.pythonEnv }
158
- };
159
- }
160
-
161
- // Update the requirement with options for pip environment
162
- const updatedStatus = await requirementService.updateRequirement(reqConfig, reqToUpdate.version, options);
163
-
164
- // Update requirement status
165
- await configProvider.updateRequirementStatus(categoryName, reqToUpdate.name, {
166
- ...updatedStatus,
167
- name: reqToUpdate.name,
168
- type: updatedStatus.type || currentStatus.type || 'unknown',
169
- installed: updatedStatus.installed,
170
- inProgress: false,
171
- operationStatus: {
172
- status: updatedStatus.installed ? 'completed' : 'failed',
173
- type: 'update',
174
- target: 'requirement',
175
- message: updatedStatus.installed
176
- ? `Successfully updated ${reqToUpdate.name} to version ${reqToUpdate.version}`
177
- : `Failed to update ${reqToUpdate.name} to version ${reqToUpdate.version}`
178
- },
179
- availableUpdate: updatedStatus.installed ? undefined : currentStatus.availableUpdate
180
- });
181
-
182
- console.log(`Requirement ${reqToUpdate.name} updated to version ${reqToUpdate.version}`);
183
- } catch (error) {
184
- console.error(`Error updating requirement ${reqToUpdate.name}:`, error);
185
-
186
- // Update status to indicate failure
187
- await configProvider.updateRequirementStatus(categoryName, reqToUpdate.name, {
188
- name: reqToUpdate.name,
189
- type: 'unknown',
190
- installed: false,
191
- inProgress: false,
192
- error: error instanceof Error ? error.message : String(error),
193
- operationStatus: {
194
- status: 'failed',
195
- type: 'update',
196
- target: 'requirement',
197
- message: `Error updating requirement: ${error instanceof Error ? error.message : String(error)}`
198
- }
199
- });
200
- }
201
- });
202
-
203
- // Wait for all updates to complete in parallel if there are any
204
- if (updatePromises) {
205
- await Promise.all(updatePromises);
206
- }
207
- } finally {
208
- // Always release the lock when done, even if there was an error
209
- await updateCheckTracker.endOperation(operationKey);
210
- }
211
- }
212
-
213
- /**
214
- * Checks and installs requirements for a server if needed
215
- * @param categoryName The category name
216
- * @param serverName The server name
217
- * @param options The installation options
218
- * @returns A failure result if requirements check fails, null if requirements are satisfied
219
- */
220
- private async checkAndInstallRequirements(categoryName: string, serverName: string, options: ServerInstallOptions): Promise<ServerOperationResult | null> {
221
- const configProvider = ConfigurationProvider.getInstance();
222
-
223
- // Get feed configuration to get requirements
224
- const feedConfig = await configProvider.getFeedConfiguration(categoryName);
225
- if (!feedConfig) {
226
- return {
227
- success: false,
228
- message: 'Feed configuration not found',
229
- status: [{
230
- status: 'failed',
231
- type: 'install',
232
- target: 'server',
233
- message: 'Feed configuration not found'
234
- }]
235
- };
236
- }
237
-
238
- // Find server config and verify requirements
239
- const serverConfig = feedConfig.mcpServers.find((s: McpConfig) => s.name === serverName);
240
- if (!serverConfig?.dependencies?.requirements) {
241
- Logger.debug(`No requirements for ${serverName}`);
242
- return null;
243
- }
83
+ // Check and install requirements using RequirementService
84
+ const requirementsResult = await requirementService.checkAndInstallRequirements(categoryName, serverName, options);
244
85
 
245
- // Check all requirements installation status
246
- const requirementStatuses = await Promise.all(
247
- serverConfig.dependencies.requirements.map(async (req) => {
248
- const reqConfig = feedConfig.requirements?.find((r: RequirementConfig) => r.name === req.name) || {
249
- name: req.name,
250
- version: req.version,
251
- type: 'npm'
252
- };
253
- return await this.installerFactory.checkInstallation(reqConfig, options);
86
+ // trigger a backend requirement check
87
+ await requirementService.checkServerRequirementForUpdateAsync(categoryName, serverName)
88
+ .then(() => {
89
+ Logger.info(`Requirement check for ${categoryName}:${serverName} completed successfully.`);
254
90
  })
255
- );
256
-
257
- // If all requirements are installed and ready, no need to proceed with installation
258
- if (requirementStatuses.every(status => status.installed)) {
259
- // Check if requirements are ready via ConfigurationProvider
260
- const requirementsReady = await configProvider.isRequirementsReady(categoryName, serverName);
261
-
262
- // Update requirement status if not ready
263
- if (!requirementsReady) {
264
- for (const status of requirementStatuses) {
265
- await configProvider.updateRequirementStatus(categoryName, status.name, status);
266
- }
267
- }
268
- return null;
269
- }
270
-
271
- // Sort requirements by order for installation
272
- const sortedRequirements = [...serverConfig.dependencies.requirements].sort((a, b) => {
273
- const orderA = a.order ?? Infinity;
274
- const orderB = b.order ?? Infinity;
275
- return orderA - orderB;
276
- });
277
-
278
- // Start requirements installation in background
279
- this.installRequirementsInBackground(categoryName, sortedRequirements, options)
280
- .catch(error => {
281
- Logger.error(`Error in background requirement installations: ${error instanceof Error ? error.message : String(error)}`);
91
+ .catch((error) => {
92
+ Logger.error(`Requirement check for ${categoryName}:${serverName} failed: ${error instanceof Error ? error.message : String(error)}`);
282
93
  });
283
94
 
284
- // Return immediately while installation continues in background
285
- return null;
286
- }
287
-
288
- /**
289
- * Installs requirements in background without blocking the main thread
290
- * Requirements with the same order are installed in parallel
291
- */
292
- private async installRequirementsInBackground(
293
- categoryName: string,
294
- sortedRequirements: Array<{ name: string; version: string; order?: number }>,
295
- options: ServerInstallOptions
296
- ): Promise<void> {
297
- const configProvider = ConfigurationProvider.getInstance();
298
-
299
- // Group requirements by order
300
- type RequirementType = { name: string; version: string; order?: number };
301
- const requirementGroups = sortedRequirements.reduce<Record<number, RequirementType[]>>((groups, req) => {
302
- const order = req.order ?? Infinity;
303
- if (!groups[order]) {
304
- groups[order] = [];
305
- }
306
- groups[order].push(req);
307
- return groups;
308
- }, {});
309
-
310
- // Process each group in sequence, but requirements within group in parallel
311
- const orderKeys = Object.keys(requirementGroups).map(Number).sort((a, b) => a - b);
312
- for (const order of orderKeys) {
313
- const group = requirementGroups[order];
314
-
315
- await Promise.all(group.map(async requirement => {
316
- try {
317
- const feeds = await configProvider.getFeedConfiguration(categoryName);
318
- const requirementConfig = feeds?.requirements?.find((r: RequirementConfig) => r.name === requirement.name) || {
319
- name: requirement.name,
320
- version: requirement.version,
321
- type: 'npm'
322
- };
323
-
324
- // For pip requirements, check if we need to use stored pythonEnv
325
- const currentStatus = await configProvider.getRequirementStatus(categoryName, requirement.name);
326
- if (requirementConfig.type === 'pip' && currentStatus?.pythonEnv && !options?.settings?.pythonEnv) {
327
- options = {
328
- ...options,
329
- settings: { ...options?.settings, pythonEnv: currentStatus.pythonEnv }
330
- };
331
- }
332
-
333
- const installer = this.installerFactory.getInstaller(requirementConfig);
334
- if (!installer) {
335
- await this.updateRequirementFailureStatus(
336
- categoryName,
337
- requirement.name,
338
- requirementConfig.type,
339
- `No installer found for requirement type: ${requirementConfig.type}`
340
- );
341
- return;
342
- }
343
-
344
- const operationId = this.generateOperationId();
345
- await this.updateRequirementProgressStatus(
346
- categoryName,
347
- requirement.name,
348
- requirementConfig.type,
349
- operationId
350
- );
351
-
352
- const installStatus = await installer.install(requirementConfig, options);
353
- await this.updateRequirementCompletionStatus(
354
- categoryName,
355
- requirement.name,
356
- installStatus,
357
- operationId
358
- );
359
- } catch (error) {
360
- await this.updateRequirementFailureStatus(
361
- categoryName,
362
- requirement.name,
363
- 'unknown',
364
- error instanceof Error ? error.message : String(error)
365
- );
366
- }
367
- }));
95
+ if (requirementsResult && !requirementsResult.success) {
96
+ await recoder.recordStep('RequirementInstallationCheck', 'failed', requirementsResult.error?.message || requirementsResult.message || 'Requirement installation failed.');
97
+ return requirementsResult;
368
98
  }
369
- }
370
-
371
- /**
372
- * Helper to update requirement status for failure case
373
- */
374
- private async updateRequirementFailureStatus(
375
- categoryName: string,
376
- requirementName: string,
377
- requirementType: string,
378
- errorMessage: string
379
- ): Promise<void> {
380
- const configProvider = ConfigurationProvider.getInstance();
381
- await configProvider.updateRequirementStatus(categoryName, requirementName, {
382
- name: requirementName,
383
- type: requirementType,
384
- installed: false,
385
- error: errorMessage,
386
- operationStatus: {
387
- status: 'failed',
388
- type: 'install',
389
- target: 'requirement',
390
- message: `Error installing requirement: ${errorMessage}`,
391
- operationId: this.generateOperationId()
392
- }
393
- });
394
- }
395
99
 
396
- /**
397
- * Helper to update requirement status for in-progress case
398
- */
399
- private async updateRequirementProgressStatus(
400
- categoryName: string,
401
- requirementName: string,
402
- requirementType: string,
403
- operationId: string
404
- ): Promise<void> {
405
- const configProvider = ConfigurationProvider.getInstance();
406
- await configProvider.updateRequirementStatus(categoryName, requirementName, {
407
- name: requirementName,
408
- type: requirementType,
409
- installed: false,
410
- inProgress: true,
411
- operationStatus: {
412
- status: 'in-progress',
413
- type: 'install',
414
- target: 'requirement',
415
- message: `Installing requirement: ${requirementName}`,
416
- operationId
417
- }
418
- });
419
- }
420
-
421
- /**
422
- * Helper to update requirement status for completion case
423
- */
424
- private async updateRequirementCompletionStatus(
425
- categoryName: string,
426
- requirementName: string,
427
- installStatus: RequirementStatus,
428
- operationId: string
429
- ): Promise<void> {
430
- const configProvider = ConfigurationProvider.getInstance();
431
- await configProvider.updateRequirementStatus(categoryName, requirementName, {
432
- ...installStatus,
433
- operationStatus: {
434
- status: installStatus.installed ? 'completed' : 'failed',
435
- type: 'install',
436
- target: 'requirement',
437
- message: installStatus.installed
438
- ? `Requirement ${requirementName} installed successfully`
439
- : `Failed to install ${requirementName}`,
440
- operationId
441
- }
442
- });
100
+ return await clientInstaller.install(options);
443
101
  }
444
102
  }
445
-
446
- // Export a singleton instance (optional)
447
- // export const installationService = new InstallationService();