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
@@ -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
+ }
@@ -0,0 +1,311 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { SETTINGS_DIR } from '../metadatas/constants.js';
4
+ import { Logger } from '../../utils/logger.js';
5
+ const INSTALL_STATUS_DIR = path.join(SETTINGS_DIR, 'InstallOperationStatus');
6
+ export class InstallOperationManager {
7
+ installOperationStatus = {};
8
+ statusLock = Promise.resolve();
9
+ categoryName;
10
+ serverName;
11
+ statusFilePath;
12
+ /**
13
+ * Creates an InstallOperationManager instance for a specific category and server.
14
+ * @param categoryName The name of the category.
15
+ * @param serverName The name of the server.
16
+ */
17
+ constructor(categoryName, serverName) {
18
+ this.categoryName = categoryName;
19
+ this.serverName = serverName;
20
+ this.statusFilePath = path.join(INSTALL_STATUS_DIR, this.categoryName, `${this.serverName}.json`);
21
+ this.loadStatuses().catch(error => Logger.error('Failed to initialize InstallOperationManager:', error));
22
+ }
23
+ /**
24
+ * Returns an InstallOperationManager instance for the given category and server.
25
+ * @param categoryName The name of the category.
26
+ * @param serverName The name of the server.
27
+ */
28
+ static instanceMap = new Map();
29
+ /**
30
+ * Returns a cached InstallOperationManager instance for the given category and server.
31
+ * If an instance does not exist, it will be created and cached.
32
+ * @param categoryName The name of the category.
33
+ * @param serverName The name of the server.
34
+ */
35
+ static getInstance(categoryName, serverName) {
36
+ const key = `${categoryName}::${serverName}`;
37
+ if (!this.instanceMap.has(key)) {
38
+ this.instanceMap.set(key, new InstallOperationManager(categoryName, serverName));
39
+ }
40
+ return this.instanceMap.get(key);
41
+ }
42
+ async withLock(operation) {
43
+ const current = this.statusLock;
44
+ let resolve;
45
+ this.statusLock = new Promise(r => resolve = r);
46
+ try {
47
+ await current;
48
+ return await operation();
49
+ }
50
+ finally {
51
+ resolve();
52
+ }
53
+ }
54
+ async loadStatuses() {
55
+ await this.withLock(async () => {
56
+ try {
57
+ await fs.mkdir(path.dirname(this.statusFilePath), { recursive: true });
58
+ const data = await fs.readFile(this.statusFilePath, 'utf-8');
59
+ this.installOperationStatus = JSON.parse(data);
60
+ }
61
+ catch (error) {
62
+ if (error.code === 'ENOENT') {
63
+ this.installOperationStatus = {};
64
+ await this.saveStatuses(); // Create the file if it doesn't exist
65
+ }
66
+ else {
67
+ Logger.error('Failed to load install operation statuses:', error);
68
+ this.installOperationStatus = {}; // Initialize with empty statuses in case of other errors
69
+ }
70
+ }
71
+ });
72
+ }
73
+ async saveStatuses() {
74
+ await fs.mkdir(path.dirname(this.statusFilePath), { recursive: true });
75
+ await fs.writeFile(this.statusFilePath, JSON.stringify(this.installOperationStatus, null, 2));
76
+ }
77
+ get operationKey() {
78
+ return `${this.categoryName}-${this.serverName}`;
79
+ }
80
+ /**
81
+ * Record a step for this category/server instance.
82
+ */
83
+ async recordStep(stepName, status, message) {
84
+ return await this.withLock(async () => {
85
+ let operationDetails = this.installOperationStatus[this.operationKey];
86
+ const newStep = {
87
+ name: stepName,
88
+ status,
89
+ message,
90
+ timestamp: new Date().toISOString(),
91
+ };
92
+ if (!operationDetails) {
93
+ operationDetails = {
94
+ currentStep: stepName,
95
+ steps: [newStep],
96
+ lastUpdated: new Date().toISOString(),
97
+ error: status === 'failed' ? message : undefined,
98
+ overallStatus: status === 'failed' ? 'failed' : 'in-progress',
99
+ };
100
+ }
101
+ else {
102
+ // Check for an existing unfinished step with the same name
103
+ const existingStepIndex = operationDetails.steps.findIndex(s => s.name === stepName && s.status !== 'completed' && s.status !== 'failed');
104
+ if (existingStepIndex !== -1) {
105
+ // Update the existing unfinished step
106
+ operationDetails.steps[existingStepIndex] = newStep;
107
+ }
108
+ else {
109
+ operationDetails.steps.push(newStep);
110
+ }
111
+ operationDetails.currentStep = stepName;
112
+ operationDetails.lastUpdated = new Date().toISOString();
113
+ if (status === 'failed') {
114
+ operationDetails.overallStatus = 'failed';
115
+ if (!operationDetails.error) { // Store the first error
116
+ operationDetails.error = message;
117
+ }
118
+ // Mark all in-progress steps as cancelled
119
+ operationDetails.steps = operationDetails.steps.map(s => s.status === 'in-progress'
120
+ ? {
121
+ ...s,
122
+ status: 'canceled',
123
+ message: (s.message ? s.message + ' ' : '') + '[Canceled due to failure]',
124
+ timestamp: new Date().toISOString(),
125
+ }
126
+ : s);
127
+ }
128
+ else if (status === 'completed' && stepName.toLowerCase().includes('completed')) {
129
+ // Check if all steps are completed or if there are any failures
130
+ const allStepsCompleted = operationDetails.steps.every(s => s.status === 'completed' || (s.name === stepName && status === 'completed'));
131
+ operationDetails.overallStatus = allStepsCompleted ? 'completed' : 'in-progress';
132
+ if (allStepsCompleted) {
133
+ operationDetails.error = undefined; // Clear error on completion
134
+ }
135
+ }
136
+ else {
137
+ if (operationDetails.overallStatus !== 'failed') {
138
+ operationDetails.overallStatus = 'in-progress';
139
+ }
140
+ }
141
+ }
142
+ // Special handling for the "InstallCompleted" step or similar final step names
143
+ if (stepName === 'InstallCompleted') {
144
+ operationDetails.overallStatus = status === 'completed' ? 'completed' : 'failed';
145
+ if (status === 'completed')
146
+ operationDetails.error = undefined;
147
+ }
148
+ this.installOperationStatus[this.operationKey] = operationDetails;
149
+ await this.saveStatuses();
150
+ return operationDetails;
151
+ });
152
+ }
153
+ /**
154
+ * Resets (deletes) the operation status for this category/server.
155
+ * This is useful to call before starting a new installation attempt.
156
+ */
157
+ async resetOperation() {
158
+ await this.withLock(async () => {
159
+ if (this.installOperationStatus[this.operationKey]) {
160
+ delete this.installOperationStatus[this.operationKey];
161
+ Logger.info(`Reset installation operation status for ${this.operationKey}`);
162
+ await this.saveStatuses();
163
+ }
164
+ });
165
+ return this;
166
+ }
167
+ /**
168
+ * Gets the details for this category/server.
169
+ */
170
+ async getDetails() {
171
+ await this.loadStatuses(); // Ensure latest statuses are loaded
172
+ return this.installOperationStatus[this.operationKey];
173
+ }
174
+ /**
175
+ * Gets all operation details in this file (for this category/server).
176
+ */
177
+ async getAllDetails() {
178
+ await this.loadStatuses();
179
+ return this.installOperationStatus;
180
+ }
181
+ /**
182
+ * Explicitly mark the overall status of an operation as 'completed' or 'failed'.
183
+ * This can be used to force the final state regardless of step details.
184
+ * @param status 'completed' or 'failed'
185
+ * @param error Optional error message if failed
186
+ */
187
+ async markOverallStatus(status, error) {
188
+ return await this.withLock(async () => {
189
+ const operationDetails = this.installOperationStatus[this.operationKey];
190
+ if (!operationDetails)
191
+ return undefined;
192
+ operationDetails.overallStatus = status;
193
+ operationDetails.lastUpdated = new Date().toISOString();
194
+ if (status === 'failed') {
195
+ if (error) {
196
+ operationDetails.error = error;
197
+ }
198
+ // Mark all in-progress steps as cancelled
199
+ operationDetails.steps = operationDetails.steps.map(s => s.status === 'in-progress'
200
+ ? {
201
+ ...s,
202
+ status: 'canceled',
203
+ message: (s.message ? s.message + ' ' : '') + '[Canceled due to failure]',
204
+ timestamp: new Date().toISOString(),
205
+ }
206
+ : s);
207
+ }
208
+ if (status === 'completed') {
209
+ operationDetails.error = undefined;
210
+ }
211
+ await this.saveStatuses();
212
+ return operationDetails;
213
+ });
214
+ }
215
+ /**
216
+ * Executes a function and records its step status as 'in-progress', 'completed', or 'failed'.
217
+ *
218
+ * Useful for wrapping tasks with consistent status tracking and flexible error handling.
219
+ *
220
+ * @template T The return type of the function being executed.
221
+ *
222
+ * @param fn The function to execute. Can be synchronous or asynchronous.
223
+ * @param options Optional configuration:
224
+ * - stepName: Custom name for the step. Defaults to the function name or 'unnamedStep'.
225
+ * - inProgressMessage: Optional message to log when the step starts.
226
+ * - endMessage: Optional static string or function to generate a final message from the result or error.
227
+ * - onResult: A function `(result: T) => boolean` that determines whether the step was successful. Defaults to always `true`.
228
+ * - onError: Optional function `(error) => { result: T; message: string } | never` that handles an error and may return a fallback result and message, or throw.
229
+ *
230
+ * @returns A promise that resolves with the result of the function if successful, or rethrows on failure.
231
+ *
232
+ * @throws The original error or any error thrown from `onError`.
233
+ */
234
+ async recording(fn, options) {
235
+ const { stepName, inProgressMessage = 'Step in progress', endMessage, onResult = () => true, onError } = options || {};
236
+ const resolvedStepName = stepName ?? (fn.name || 'unnamedStep');
237
+ const getEndMessage = (data, fallback) => typeof endMessage === 'function' ? endMessage(data) || 'Step completed' : endMessage ?? fallback;
238
+ await this.recordStep(resolvedStepName, 'in-progress', inProgressMessage);
239
+ try {
240
+ const result = await Promise.resolve(fn());
241
+ const isSuccess = onResult(result);
242
+ await this.recordStep(resolvedStepName, isSuccess ? 'completed' : 'failed', getEndMessage(result, isSuccess ? 'Step completed successfully' : 'Step failed'));
243
+ return result;
244
+ }
245
+ catch (err) {
246
+ if (!onError) {
247
+ await this.recordStep(resolvedStepName, 'failed', err?.message || String(err));
248
+ throw err;
249
+ }
250
+ try {
251
+ const { result, message } = await onError(err);
252
+ await this.recordStep(resolvedStepName, 'failed', message);
253
+ return result;
254
+ }
255
+ catch (onErrorThrown) {
256
+ await this.recordStep(resolvedStepName, 'failed', onErrorThrown?.message || String(onErrorThrown));
257
+ throw onErrorThrown;
258
+ }
259
+ }
260
+ }
261
+ /**
262
+ * Executes an asynchronous "fire-and-forget" task and records its step status as 'in-progress', 'completed', or 'failed'.
263
+ *
264
+ * Unlike `recording`, this does not await the result — it runs the task in the background.
265
+ * Useful for side-effecting operations like updates or notifications where progress should be logged but not block flow.
266
+ *
267
+ * @template T The resolved type of the asynchronous task.
268
+ *
269
+ * @param fn The async function to execute.
270
+ * @param options Optional configuration:
271
+ * - stepName: Custom name for the step. Defaults to the function name or 'unnamedStep'.
272
+ * - inProgressMessage: Message to log when the step starts.
273
+ * - endMessage: Static string or function to generate the final message from result or error.
274
+ * - onResult: A boolean or function `(result: T) => boolean` to determine if the step is successful.
275
+ * - onError: A function `(error) => string` or `Promise<string>` to handle errors and return a message.
276
+ * If it throws, the error is rethrown and logged.
277
+ * - onComplete: Optional callback to run after completion, regardless of success or failure.
278
+ *
279
+ * @returns void
280
+ */
281
+ recordingAsync(fn, options) {
282
+ const { stepName, inProgressMessage = 'Step in progress', endMessage, onResult = () => true, onError, onComplete, } = options || {};
283
+ const resolvedStepName = stepName ?? (fn.name || 'unnamedStep');
284
+ const getMessage = (data, fallback) => typeof endMessage === 'function' ? endMessage(data) : endMessage ?? fallback;
285
+ this.recordStep(resolvedStepName, 'in-progress', inProgressMessage).then(() => {
286
+ fn()
287
+ .then(result => {
288
+ const isSuccess = onResult(result);
289
+ const message = getMessage(result, isSuccess ? 'Step completed successfully' : 'Step failed');
290
+ this.recordStep(resolvedStepName, isSuccess ? 'completed' : 'failed', message).then(onComplete).catch();
291
+ })
292
+ .catch(async (err) => {
293
+ if (onError) {
294
+ try {
295
+ const errorMessage = await onError(err);
296
+ await this.recordStep(resolvedStepName, 'failed', errorMessage);
297
+ }
298
+ catch (onErrorThrown) {
299
+ await this.recordStep(resolvedStepName, 'failed', onErrorThrown?.message || String(onErrorThrown));
300
+ throw onErrorThrown;
301
+ }
302
+ }
303
+ else {
304
+ const fallback = getMessage(err, err?.message || String(err));
305
+ await this.recordStep(resolvedStepName, 'failed', fallback);
306
+ }
307
+ });
308
+ });
309
+ }
310
+ }
311
+ //# sourceMappingURL=InstallOperationManager.js.map
@@ -0,0 +1,54 @@
1
+ import { SystemSettings } from '../metadatas/types.js';
2
+ export declare class SystemSettingsManager {
3
+ private static instance;
4
+ private settings;
5
+ private settingsFilePath;
6
+ private settingsLock;
7
+ private constructor();
8
+ static getInstance(): SystemSettingsManager;
9
+ private withLock;
10
+ private _loadSettingsInternal;
11
+ /**
12
+ * Loads settings from the file. This operation is atomic.
13
+ * If the settings file doesn't exist, it initializes with defaults and creates the file.
14
+ * @returns A promise that resolves when settings are loaded.
15
+ */
16
+ loadSettings(): Promise<void>;
17
+ private _applyDefaultsIfNeededInternal;
18
+ /**
19
+ * Applies default values to settings if they are not already set.
20
+ * This operation is atomic and saves settings if defaults are applied.
21
+ * @returns A promise that resolves when defaults are applied and saved if necessary.
22
+ */
23
+ applyDefaultsIfNeeded(): Promise<void>;
24
+ /**
25
+ * Retrieves the current system settings.
26
+ * If settings haven't been loaded or are empty, it attempts to load them.
27
+ * Paths within the settings are normalized (e.g., backslashes to forward slashes).
28
+ * @returns A promise that resolves to the `SystemSettings` object.
29
+ */
30
+ getSystemSettings(): Promise<SystemSettings>;
31
+ /**
32
+ * Creates or updates system settings with the provided partial settings.
33
+ * This operation is atomic and protected by a lock.
34
+ * It loads existing settings if not already loaded, merges the new settings,
35
+ * applies any necessary defaults, and then saves the updated settings.
36
+ * @param newSettings A `Partial<SystemSettings>` object containing the settings to update.
37
+ * @returns A promise that resolves to the fully updated and normalized `SystemSettings`.
38
+ */
39
+ createOrUpdateSystemSettings(newSettings: Partial<SystemSettings>): Promise<SystemSettings>;
40
+ /**
41
+ * Internal method to save settings to the file without acquiring a lock.
42
+ * Assumes the lock is already held by the calling public method.
43
+ * Normalizes paths before saving.
44
+ * @returns A promise that resolves when settings are written to disk.
45
+ */
46
+ private _saveSettingsInternal;
47
+ /**
48
+ * Saves the current settings to the file. This operation is atomic and protected by a lock.
49
+ * Paths are normalized before saving.
50
+ * @returns A promise that resolves when the settings are saved.
51
+ */
52
+ saveSettings(): Promise<void>;
53
+ }
54
+ export declare const systemSettingsManager: SystemSettingsManager;