imcp 0.1.1 → 0.1.2

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