imcp 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/cli/commands/install.js +8 -8
- package/dist/cli/index.js +3 -2
- package/dist/core/ConfigurationProvider.d.ts +2 -0
- package/dist/core/ConfigurationProvider.js +49 -3
- package/dist/core/InstallationService.d.ts +8 -0
- package/dist/core/InstallationService.js +117 -0
- package/dist/core/MCPManager.d.ts +1 -0
- package/dist/core/MCPManager.js +42 -0
- package/dist/core/RequirementService.d.ts +7 -0
- package/dist/core/RequirementService.js +17 -0
- package/dist/core/constants.d.ts +5 -0
- package/dist/core/constants.js +29 -6
- package/dist/core/installers/BaseInstaller.js +26 -9
- package/dist/core/installers/ClientInstaller.d.ts +6 -0
- package/dist/core/installers/ClientInstaller.js +149 -12
- package/dist/core/installers/GeneralInstaller.js +0 -5
- package/dist/core/installers/NpmInstaller.js +2 -1
- package/dist/core/types.d.ts +7 -6
- package/dist/services/ServerService.js +16 -0
- package/dist/utils/clientUtils.js +3 -1
- package/dist/utils/versionUtils.d.ts +12 -0
- package/dist/utils/versionUtils.js +26 -0
- package/dist/web/public/css/modal.css +89 -9
- package/dist/web/public/index.html +12 -6
- package/dist/web/public/js/modal.js +357 -97
- package/dist/web/public/modal.html +20 -11
- package/dist/web/server.d.ts +6 -0
- package/dist/web/server.js +6 -1
- package/package.json +1 -1
- package/src/cli/commands/install.ts +11 -14
- package/src/cli/index.ts +4 -2
- package/src/core/ConfigurationProvider.ts +51 -3
- package/src/core/InstallationService.ts +131 -0
- package/src/core/MCPManager.ts +60 -1
- package/src/core/RequirementService.ts +21 -1
- package/src/core/constants.ts +32 -7
- package/src/core/installers/BaseInstaller.ts +33 -17
- package/src/core/installers/ClientInstaller.ts +148 -12
- package/src/core/installers/GeneralInstaller.ts +0 -5
- package/src/core/installers/NpmInstaller.ts +2 -1
- package/src/core/types.ts +8 -6
- package/src/services/ServerService.ts +22 -0
- package/src/utils/clientUtils.ts +3 -1
- package/src/utils/versionUtils.ts +29 -0
- package/src/web/public/css/modal.css +89 -9
- package/src/web/public/index.html +12 -6
- package/src/web/public/js/modal.js +357 -97
- package/src/web/public/modal.html +20 -11
- package/src/web/server.ts +16 -2
package/README.md
CHANGED
|
@@ -75,13 +75,13 @@ Options:
|
|
|
75
75
|
Examples:
|
|
76
76
|
```bash
|
|
77
77
|
# Install a server
|
|
78
|
-
imcp install --category ai-coder-tools --name
|
|
78
|
+
imcp install --category ai-coder-tools --name git-tools
|
|
79
79
|
|
|
80
80
|
# Install with specific client targets
|
|
81
|
-
imcp install --category ai-coder-tools --name
|
|
81
|
+
imcp install --category ai-coder-tools --name git-tools --clients "MSRooCode;GithubCopilot"
|
|
82
82
|
|
|
83
83
|
# Install with environment variables
|
|
84
|
-
imcp install --category ai-coder-tools --name
|
|
84
|
+
imcp install --category ai-coder-tools --name git-tools --envs "GITHUB_TOKEN=abc123;API_KEY=xyz789"
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
### pull
|
|
@@ -19,7 +19,6 @@ Examples:
|
|
|
19
19
|
`)
|
|
20
20
|
.requiredOption('--category <category>', 'Server category')
|
|
21
21
|
.requiredOption('--name <name>', 'Server name to install')
|
|
22
|
-
.option('--force', 'Force installation even if server already exists', false)
|
|
23
22
|
.option('--clients <clients>', 'Target clients (semicolon separated). Supported values: Cline, MSRooCode, GithubCopilot. If not specified, installs for all clients')
|
|
24
23
|
.option('--envs <envs>', 'Environment variables (semicolon separated key=value pairs)')
|
|
25
24
|
.action(async (options) => {
|
|
@@ -30,15 +29,16 @@ Examples:
|
|
|
30
29
|
Logger.log('Local feeds not found, syncing from remote...');
|
|
31
30
|
await serverService.syncFeeds();
|
|
32
31
|
}
|
|
33
|
-
const { category, name, verbose,
|
|
34
|
-
Logger.debug(`Install options: ${JSON.stringify({ category, name,
|
|
32
|
+
const { category, name, verbose, clients, envs } = options;
|
|
33
|
+
Logger.debug(`Install options: ${JSON.stringify({ category, name, verbose, clients, envs })}`);
|
|
35
34
|
const serverName = name.trim();
|
|
36
35
|
Logger.debug(`Server name: ${serverName}`);
|
|
37
36
|
if (!await serverService.validateServerName(category, serverName)) {
|
|
38
|
-
Logger.error('Invalid server name or category provided'
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
Logger.error('Invalid server name or category provided.\n' +
|
|
38
|
+
'This could be because:\n' +
|
|
39
|
+
' 1. The server name or category is misspelled\n' +
|
|
40
|
+
' 2. Your local feeds are outdated\n\n' +
|
|
41
|
+
'Try running "imcp pull" to update your local feeds from remote.', { category, serverName });
|
|
42
42
|
process.exit(1);
|
|
43
43
|
}
|
|
44
44
|
// Parse and validate clients
|
|
@@ -77,7 +77,7 @@ Examples:
|
|
|
77
77
|
}
|
|
78
78
|
Logger.log(`Installing server: ${serverName}`);
|
|
79
79
|
const installOptions = {
|
|
80
|
-
force:
|
|
80
|
+
force: false,
|
|
81
81
|
...(parsedClients?.length && { targetClients: parsedClients }),
|
|
82
82
|
...(Object.keys(parsedEnvs).length > 0 && { env: parsedEnvs })
|
|
83
83
|
};
|
package/dist/cli/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import axios from 'axios';
|
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
12
|
import fs from 'fs';
|
|
13
|
+
import { compareVersions } from '../utils/versionUtils.js';
|
|
13
14
|
// Custom error interface for Commander.js errors
|
|
14
15
|
// ANSI color codes
|
|
15
16
|
const COLORS = {
|
|
@@ -23,7 +24,6 @@ async function main() {
|
|
|
23
24
|
program
|
|
24
25
|
.name('imcp')
|
|
25
26
|
.description('IMCP (Install Model Context Protocol) CLI')
|
|
26
|
-
.version('0.0.1')
|
|
27
27
|
.option('--verbose', 'Show detailed logs for all commands');
|
|
28
28
|
// Parse global options first
|
|
29
29
|
program.parseOptions(process.argv);
|
|
@@ -85,7 +85,8 @@ async function checkForUpdates() {
|
|
|
85
85
|
const npmResponse = await axios.get(`https://registry.npmjs.org/${packageName}`);
|
|
86
86
|
if (npmResponse.data && npmResponse.data['dist-tags'] && npmResponse.data['dist-tags'].latest) {
|
|
87
87
|
const latestVersion = npmResponse.data['dist-tags'].latest;
|
|
88
|
-
|
|
88
|
+
// Compare versions properly to ensure we're only notifying for newer versions
|
|
89
|
+
if (latestVersion && compareVersions(latestVersion, currentVersion) > 0) {
|
|
89
90
|
console.log(`${COLORS.yellow}Update available for ${packageName}: ${currentVersion} → ${latestVersion}${COLORS.reset}`);
|
|
90
91
|
console.log(`${COLORS.yellow}Run \`npm install -g ${packageName}@latest\` to update${COLORS.reset}`);
|
|
91
92
|
}
|
|
@@ -9,9 +9,11 @@ export declare class ConfigurationProvider {
|
|
|
9
9
|
static getInstance(): ConfigurationProvider;
|
|
10
10
|
private withLock;
|
|
11
11
|
initialize(): Promise<void>;
|
|
12
|
+
private loadClientMCPSettings;
|
|
12
13
|
private saveConfiguration;
|
|
13
14
|
getServerCategories(): Promise<MCPServerCategory[]>;
|
|
14
15
|
getServerCategory(categoryName: string): Promise<MCPServerCategory | undefined>;
|
|
16
|
+
getClientMcpSettings(): Promise<Record<string, Record<string, any>> | undefined>;
|
|
15
17
|
getFeedConfiguration(categoryName: string): Promise<FeedConfiguration | undefined>;
|
|
16
18
|
getInstallationStatus(categoryName: string): Promise<InstallationStatus | undefined>;
|
|
17
19
|
getServerStatus(categoryName: string, serverName: string): Promise<MCPServerStatus | undefined>;
|
|
@@ -3,9 +3,10 @@ import path from 'path';
|
|
|
3
3
|
import { exec } from 'child_process';
|
|
4
4
|
import { promisify } from 'util';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
|
-
import { GITHUB_REPO, LOCAL_FEEDS_DIR, SETTINGS_DIR } from './constants.js';
|
|
6
|
+
import { GITHUB_REPO, LOCAL_FEEDS_DIR, SETTINGS_DIR, SUPPORTED_CLIENTS } from './constants.js';
|
|
7
7
|
import { Logger } from '../utils/logger.js';
|
|
8
8
|
import { checkGithubAuth } from '../utils/githubAuth.js';
|
|
9
|
+
import { readJsonFile } from '../utils/clientUtils.js';
|
|
9
10
|
const execAsync = promisify(exec);
|
|
10
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
export class ConfigurationProvider {
|
|
@@ -20,6 +21,7 @@ export class ConfigurationProvider {
|
|
|
20
21
|
this.configuration = {
|
|
21
22
|
localServerCategories: [],
|
|
22
23
|
feeds: {},
|
|
24
|
+
clientMCPSettings: {}
|
|
23
25
|
};
|
|
24
26
|
this.tempDir = path.join(LOCAL_FEEDS_DIR, '../temp');
|
|
25
27
|
}
|
|
@@ -49,6 +51,7 @@ export class ConfigurationProvider {
|
|
|
49
51
|
const config = JSON.parse(await fs.readFile(this.configPath, 'utf8'));
|
|
50
52
|
this.configuration = config;
|
|
51
53
|
await this.loadFeedsIntoConfiguration(); // Load feeds into configuration
|
|
54
|
+
await this.loadClientMCPSettings(); // Load MCP settings for each client
|
|
52
55
|
}
|
|
53
56
|
catch (error) {
|
|
54
57
|
if (error.code !== 'ENOENT') {
|
|
@@ -59,6 +62,45 @@ export class ConfigurationProvider {
|
|
|
59
62
|
}
|
|
60
63
|
});
|
|
61
64
|
}
|
|
65
|
+
async loadClientMCPSettings() {
|
|
66
|
+
try {
|
|
67
|
+
Logger.debug('Starting to load MCP client settings...');
|
|
68
|
+
const settings = {};
|
|
69
|
+
for (const [clientName, clientSettings] of Object.entries(SUPPORTED_CLIENTS)) {
|
|
70
|
+
const settingPath = process.env.CODE_INSIDERS
|
|
71
|
+
? clientSettings.codeInsiderSettingPath
|
|
72
|
+
: clientSettings.codeSettingPath;
|
|
73
|
+
try {
|
|
74
|
+
let content = await readJsonFile(settingPath, true);
|
|
75
|
+
if (clientName === 'GithubCopilot') {
|
|
76
|
+
// Initialize the mcp section if it doesn't exist
|
|
77
|
+
if (!content.mcp) {
|
|
78
|
+
content = {
|
|
79
|
+
servers: {},
|
|
80
|
+
inputs: []
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
content = content.mcp;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
settings[clientName] = content;
|
|
88
|
+
Logger.debug(`Successfully loaded MCP settings for ${clientName}`);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
Logger.debug(`Warning: Could not load MCP settings for client ${clientName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
92
|
+
settings[clientName] = {};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
this.configuration.clientMCPSettings = settings;
|
|
96
|
+
await this.saveConfiguration();
|
|
97
|
+
Logger.debug('Saved MCP client settings to configuration');
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
Logger.error('Error loading client MCP settings:', error);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
62
104
|
async saveConfiguration() {
|
|
63
105
|
const configDir = path.dirname(this.configPath);
|
|
64
106
|
await fs.mkdir(configDir, { recursive: true });
|
|
@@ -74,6 +116,11 @@ export class ConfigurationProvider {
|
|
|
74
116
|
return this.configuration.localServerCategories.find(s => s.name === categoryName);
|
|
75
117
|
});
|
|
76
118
|
}
|
|
119
|
+
async getClientMcpSettings() {
|
|
120
|
+
return await this.withLock(async () => {
|
|
121
|
+
return this.configuration.clientMCPSettings;
|
|
122
|
+
});
|
|
123
|
+
}
|
|
77
124
|
async getFeedConfiguration(categoryName) {
|
|
78
125
|
return await this.withLock(async () => {
|
|
79
126
|
return this.configuration.feeds[categoryName];
|
|
@@ -325,8 +372,7 @@ export class ConfigurationProvider {
|
|
|
325
372
|
type: req.type,
|
|
326
373
|
installed: false,
|
|
327
374
|
version: req.version,
|
|
328
|
-
error: undefined
|
|
329
|
-
updateInfo: null
|
|
375
|
+
error: undefined
|
|
330
376
|
};
|
|
331
377
|
}
|
|
332
378
|
}
|
|
@@ -14,4 +14,12 @@ export declare class InstallationService {
|
|
|
14
14
|
* @returns A result object indicating success or failure.
|
|
15
15
|
*/
|
|
16
16
|
install(categoryName: string, serverName: string, options: ServerInstallOptions): Promise<ServerOperationResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Process requirement updates specified in serverInstallOptions
|
|
19
|
+
* All updates are processed in parallel for maximum efficiency
|
|
20
|
+
* @param categoryName The category name
|
|
21
|
+
* @param serverName The server name
|
|
22
|
+
* @param requirements The requirements to update
|
|
23
|
+
*/
|
|
24
|
+
private processRequirementUpdates;
|
|
17
25
|
}
|
|
@@ -29,6 +29,15 @@ export class InstallationService {
|
|
|
29
29
|
async install(categoryName, serverName, options) {
|
|
30
30
|
const configProvider = ConfigurationProvider.getInstance();
|
|
31
31
|
const clients = options.targetClients || Object.keys(SUPPORTED_CLIENTS);
|
|
32
|
+
// Process updates for requirements if specified in options
|
|
33
|
+
// Fire off requirement updates in the background without awaiting completion
|
|
34
|
+
if (options.requirements && options.requirements.length > 0) {
|
|
35
|
+
// Start the process but don't await it - it will run in the background
|
|
36
|
+
this.processRequirementUpdates(categoryName, serverName, options.requirements)
|
|
37
|
+
.catch(error => {
|
|
38
|
+
console.error(`Error in background requirement updates: ${error instanceof Error ? error.message : String(error)}`);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
32
41
|
// Check if server is already ready
|
|
33
42
|
const isReady = await configProvider.isServerReady(categoryName, serverName, clients);
|
|
34
43
|
if (isReady) {
|
|
@@ -138,6 +147,114 @@ export class InstallationService {
|
|
|
138
147
|
// Each client installer will check requirements before actual installation
|
|
139
148
|
return await clientInstaller.install(options);
|
|
140
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Process requirement updates specified in serverInstallOptions
|
|
152
|
+
* All updates are processed in parallel for maximum efficiency
|
|
153
|
+
* @param categoryName The category name
|
|
154
|
+
* @param serverName The server name
|
|
155
|
+
* @param requirements The requirements to update
|
|
156
|
+
*/
|
|
157
|
+
async processRequirementUpdates(categoryName, serverName, requirements) {
|
|
158
|
+
// Use UpdateCheckTracker to prevent concurrent updates
|
|
159
|
+
const updateCheckTracker = await import('../utils/UpdateCheckTracker.js').then(m => m.updateCheckTracker);
|
|
160
|
+
const operationKey = `requirement-updates-${categoryName}-${serverName}`;
|
|
161
|
+
// Check if there's already an update operation in progress for this server
|
|
162
|
+
const canProceed = await updateCheckTracker.startOperation(operationKey);
|
|
163
|
+
if (!canProceed) {
|
|
164
|
+
console.log(`Requirement updates for ${categoryName}/${serverName} already in progress, skipping`);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const configProvider = ConfigurationProvider.getInstance();
|
|
169
|
+
const feedConfig = await configProvider.getFeedConfiguration(categoryName);
|
|
170
|
+
if (!feedConfig) {
|
|
171
|
+
console.error(`Feed configuration not found for category: ${categoryName}`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// Import the RequirementService
|
|
175
|
+
const { requirementService } = await import('./RequirementService.js');
|
|
176
|
+
// Create an array of promises to update all requirements in parallel
|
|
177
|
+
const updatePromises = requirements.map(async (reqToUpdate) => {
|
|
178
|
+
try {
|
|
179
|
+
// Find the full requirement config
|
|
180
|
+
const reqConfig = feedConfig.requirements?.find((r) => r.name === reqToUpdate.name);
|
|
181
|
+
if (!reqConfig) {
|
|
182
|
+
console.error(`Requirement configuration not found for: ${reqToUpdate.name}`);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
// Get current status
|
|
186
|
+
const currentStatus = await configProvider.getRequirementStatus(categoryName, reqToUpdate.name);
|
|
187
|
+
if (!currentStatus) {
|
|
188
|
+
console.error(`No current status found for requirement: ${reqToUpdate.name}`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
// Update requirement status to indicate update in progress
|
|
192
|
+
await configProvider.updateRequirementStatus(categoryName, reqToUpdate.name, {
|
|
193
|
+
...currentStatus,
|
|
194
|
+
name: reqToUpdate.name, // Ensure name is included
|
|
195
|
+
type: currentStatus.type || 'unknown', // Ensure type is included
|
|
196
|
+
installed: currentStatus.installed || false, // Ensure installed is included
|
|
197
|
+
inProgress: true,
|
|
198
|
+
operationStatus: {
|
|
199
|
+
status: 'in-progress',
|
|
200
|
+
type: 'update',
|
|
201
|
+
target: 'requirement',
|
|
202
|
+
message: `Updating ${reqToUpdate.name} from ${currentStatus.version || 'unknown'} to ${reqToUpdate.version}`
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
// Create updated requirement config with new version
|
|
206
|
+
const updatedReqConfig = {
|
|
207
|
+
...reqConfig,
|
|
208
|
+
version: reqToUpdate.version
|
|
209
|
+
};
|
|
210
|
+
// Update the requirement
|
|
211
|
+
const updatedStatus = await requirementService.updateRequirement(updatedReqConfig, reqToUpdate.version);
|
|
212
|
+
// Update requirement status
|
|
213
|
+
await configProvider.updateRequirementStatus(categoryName, reqToUpdate.name, {
|
|
214
|
+
...updatedStatus,
|
|
215
|
+
name: reqToUpdate.name, // Ensure name is always included
|
|
216
|
+
type: updatedStatus.type || currentStatus.type || 'unknown', // Ensure type is included
|
|
217
|
+
installed: updatedStatus.installed, // This should be defined from updatedStatus
|
|
218
|
+
inProgress: false,
|
|
219
|
+
operationStatus: {
|
|
220
|
+
status: updatedStatus.installed ? 'completed' : 'failed',
|
|
221
|
+
type: 'update',
|
|
222
|
+
target: 'requirement',
|
|
223
|
+
message: updatedStatus.installed
|
|
224
|
+
? `Successfully updated ${reqToUpdate.name} to version ${reqToUpdate.version}`
|
|
225
|
+
: `Failed to update ${reqToUpdate.name} to version ${reqToUpdate.version}`
|
|
226
|
+
},
|
|
227
|
+
// Clear availableUpdate if update was successful
|
|
228
|
+
availableUpdate: updatedStatus.installed ? undefined : currentStatus.availableUpdate
|
|
229
|
+
});
|
|
230
|
+
console.log(`Requirement ${reqToUpdate.name} updated to version ${reqToUpdate.version}`);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
console.error(`Error updating requirement ${reqToUpdate.name}:`, error);
|
|
234
|
+
// Update status to indicate failure
|
|
235
|
+
await configProvider.updateRequirementStatus(categoryName, reqToUpdate.name, {
|
|
236
|
+
name: reqToUpdate.name,
|
|
237
|
+
type: 'unknown',
|
|
238
|
+
installed: false,
|
|
239
|
+
inProgress: false,
|
|
240
|
+
error: error instanceof Error ? error.message : String(error),
|
|
241
|
+
operationStatus: {
|
|
242
|
+
status: 'failed',
|
|
243
|
+
type: 'update',
|
|
244
|
+
target: 'requirement',
|
|
245
|
+
message: `Error updating requirement: ${error instanceof Error ? error.message : String(error)}`
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
// Wait for all updates to complete in parallel
|
|
251
|
+
await Promise.all(updatePromises);
|
|
252
|
+
}
|
|
253
|
+
finally {
|
|
254
|
+
// Always release the lock when done, even if there was an error
|
|
255
|
+
await updateCheckTracker.endOperation(operationKey);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
141
258
|
}
|
|
142
259
|
// Export a singleton instance (optional)
|
|
143
260
|
// export const installationService = new InstallationService();
|
|
@@ -10,6 +10,7 @@ export declare class MCPManager extends EventEmitter {
|
|
|
10
10
|
getFeedConfiguration(categoryName: string): Promise<import("./types.js").FeedConfiguration | undefined>;
|
|
11
11
|
installServer(categoryName: string, serverName: string, requestOptions?: ServerInstallOptions): Promise<ServerOperationResult>;
|
|
12
12
|
uninstallServer(categoryName: string, serverName: string, options?: ServerUninstallOptions): Promise<ServerOperationResult>;
|
|
13
|
+
updateRequirement(categoryName: string, serverName: string, requirementName: string, updateVersion: string): Promise<ServerOperationResult>;
|
|
13
14
|
emit<E extends MCPEvent>(event: E, data: MCPEventData[E]): boolean;
|
|
14
15
|
on<E extends MCPEvent>(event: E, listener: (data: MCPEventData[E]) => void): this;
|
|
15
16
|
off<E extends MCPEvent>(event: E, listener: (data: MCPEventData[E]) => void): this;
|
package/dist/core/MCPManager.js
CHANGED
|
@@ -82,6 +82,48 @@ export class MCPManager extends EventEmitter {
|
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
+
async updateRequirement(categoryName, serverName, requirementName, updateVersion) {
|
|
86
|
+
try {
|
|
87
|
+
const { requirementService } = await import('./RequirementService.js');
|
|
88
|
+
const serverCategory = await this.configProvider.getServerCategory(categoryName);
|
|
89
|
+
if (!serverCategory) {
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
message: `Server category ${categoryName} is not onboarded`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (!serverCategory.feedConfiguration) {
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
message: `Server category ${categoryName} has no feed configuration`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// Find the requirement
|
|
102
|
+
const requirement = serverCategory.feedConfiguration.requirements.find(r => r.name === requirementName);
|
|
103
|
+
if (!requirement) {
|
|
104
|
+
return {
|
|
105
|
+
success: false,
|
|
106
|
+
message: `Requirement ${requirementName} not found in category ${categoryName}`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// Update the requirement using requirementService
|
|
110
|
+
const updatedStatus = await requirementService.updateRequirement(requirement, updateVersion);
|
|
111
|
+
// Update the status in configuration
|
|
112
|
+
await this.configProvider.updateRequirementStatus(categoryName, requirementName, updatedStatus);
|
|
113
|
+
return {
|
|
114
|
+
success: true,
|
|
115
|
+
message: `Successfully updated ${requirementName} to version ${updateVersion}`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error(`Error updating requirement ${requirementName}:`, error);
|
|
120
|
+
return {
|
|
121
|
+
success: false,
|
|
122
|
+
message: `Failed to update ${requirementName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
123
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
85
127
|
// Type-safe event emitter methods
|
|
86
128
|
emit(event, data) {
|
|
87
129
|
return super.emit(event, data);
|
|
@@ -29,6 +29,13 @@ export declare class RequirementService {
|
|
|
29
29
|
* @returns Updated status with available updates information
|
|
30
30
|
*/
|
|
31
31
|
checkRequirementForUpdates(requirement: RequirementConfig): Promise<RequirementStatus>;
|
|
32
|
+
/**
|
|
33
|
+
* Update a requirement to a new version
|
|
34
|
+
* @param requirement The requirement configuration
|
|
35
|
+
* @param updateVersion The version to update to
|
|
36
|
+
* @returns The updated requirement status
|
|
37
|
+
*/
|
|
38
|
+
updateRequirement(requirement: RequirementConfig, updateVersion: string): Promise<RequirementStatus>;
|
|
32
39
|
/**
|
|
33
40
|
* Install multiple requirements
|
|
34
41
|
* @param requirements The requirements to install
|
|
@@ -61,6 +61,23 @@ export class RequirementService {
|
|
|
61
61
|
}
|
|
62
62
|
return status;
|
|
63
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Update a requirement to a new version
|
|
66
|
+
* @param requirement The requirement configuration
|
|
67
|
+
* @param updateVersion The version to update to
|
|
68
|
+
* @returns The updated requirement status
|
|
69
|
+
*/
|
|
70
|
+
async updateRequirement(requirement, updateVersion) {
|
|
71
|
+
// Validate requirement
|
|
72
|
+
this.validateRequirement(requirement);
|
|
73
|
+
// Create an updated requirement with the new version
|
|
74
|
+
const updatedRequirement = {
|
|
75
|
+
...requirement,
|
|
76
|
+
version: updateVersion
|
|
77
|
+
};
|
|
78
|
+
// Install the updated version
|
|
79
|
+
return await this.installRequirement(updatedRequirement);
|
|
80
|
+
}
|
|
64
81
|
/**
|
|
65
82
|
* Install multiple requirements
|
|
66
83
|
* @param requirements The requirements to install
|
package/dist/core/constants.d.ts
CHANGED
|
@@ -27,3 +27,8 @@ export declare const SUPPORTED_CLIENTS: Record<string, any>;
|
|
|
27
27
|
* List of supported client names.
|
|
28
28
|
*/
|
|
29
29
|
export declare const SUPPORTED_CLIENT_NAMES: string[];
|
|
30
|
+
/**
|
|
31
|
+
* Minimum time between requirement update checks (in milliseconds)
|
|
32
|
+
* 10 minutes = 10 * 60 * 1000 = 600000 ms
|
|
33
|
+
*/
|
|
34
|
+
export declare const UPDATE_CHECK_INTERVAL_MS: number;
|
package/dist/core/constants.js
CHANGED
|
@@ -25,8 +25,26 @@ export const SETTINGS_DIR = (() => {
|
|
|
25
25
|
* Local feeds directory path
|
|
26
26
|
*/
|
|
27
27
|
export const LOCAL_FEEDS_DIR = path.join(SETTINGS_DIR, 'feeds');
|
|
28
|
-
const CODE_STRORAGE_DIR =
|
|
29
|
-
|
|
28
|
+
const CODE_STRORAGE_DIR = (() => {
|
|
29
|
+
switch (process.platform) {
|
|
30
|
+
case 'win32':
|
|
31
|
+
return path.join(os.homedir(), 'AppData', 'Roaming', 'Code', 'User');
|
|
32
|
+
case 'darwin': // macOS
|
|
33
|
+
return path.join(os.homedir(), 'Library', 'Application Support', 'Code', 'User');
|
|
34
|
+
default: // linux
|
|
35
|
+
return path.join(os.homedir(), '.config', 'Code', 'User');
|
|
36
|
+
}
|
|
37
|
+
})();
|
|
38
|
+
const CODE_INSIDER_STRORAGE_DIR = (() => {
|
|
39
|
+
switch (process.platform) {
|
|
40
|
+
case 'win32':
|
|
41
|
+
return path.join(os.homedir(), 'AppData', 'Roaming', 'Code - Insiders', 'User');
|
|
42
|
+
case 'darwin': // macOS
|
|
43
|
+
return path.join(os.homedir(), 'Library', 'Application Support', 'Code - Insiders', 'User');
|
|
44
|
+
default: // linux
|
|
45
|
+
return path.join(os.homedir(), '.config', 'Code - Insiders', 'User');
|
|
46
|
+
}
|
|
47
|
+
})();
|
|
30
48
|
/**
|
|
31
49
|
* Supported client configurations.
|
|
32
50
|
* Key: Client name (e.g., 'vscode')
|
|
@@ -34,14 +52,14 @@ const CODE_INSIDER_STRORAGE_DIR = path.join(os.homedir(), 'AppData', 'Roaming',
|
|
|
34
52
|
* TODO: Define actual client settings structure.
|
|
35
53
|
*/
|
|
36
54
|
export const SUPPORTED_CLIENTS = {
|
|
37
|
-
'Cline': {
|
|
38
|
-
codeSettingPath: path.join(CODE_STRORAGE_DIR, 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'),
|
|
39
|
-
codeInsiderSettingPath: path.join(CODE_INSIDER_STRORAGE_DIR, 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'),
|
|
40
|
-
},
|
|
41
55
|
'MSRooCode': {
|
|
42
56
|
codeSettingPath: path.join(CODE_STRORAGE_DIR, 'globalStorage', 'microsoftai.ms-roo-cline', 'settings', 'cline_mcp_settings.json'),
|
|
43
57
|
codeInsiderSettingPath: path.join(CODE_INSIDER_STRORAGE_DIR, 'globalStorage', 'microsoftai.ms-roo-clinev', 'settings', 'cline_mcp_settings.json'),
|
|
44
58
|
},
|
|
59
|
+
'Cline': {
|
|
60
|
+
codeSettingPath: path.join(CODE_STRORAGE_DIR, 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'),
|
|
61
|
+
codeInsiderSettingPath: path.join(CODE_INSIDER_STRORAGE_DIR, 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'),
|
|
62
|
+
},
|
|
45
63
|
'GithubCopilot': {
|
|
46
64
|
codeSettingPath: path.join(CODE_STRORAGE_DIR, 'settings.json'),
|
|
47
65
|
codeInsiderSettingPath: path.join(CODE_INSIDER_STRORAGE_DIR, 'settings.json'),
|
|
@@ -52,4 +70,9 @@ export const SUPPORTED_CLIENTS = {
|
|
|
52
70
|
* List of supported client names.
|
|
53
71
|
*/
|
|
54
72
|
export const SUPPORTED_CLIENT_NAMES = Object.keys(SUPPORTED_CLIENTS);
|
|
73
|
+
/**
|
|
74
|
+
* Minimum time between requirement update checks (in milliseconds)
|
|
75
|
+
* 10 minutes = 10 * 60 * 1000 = 600000 ms
|
|
76
|
+
*/
|
|
77
|
+
export const UPDATE_CHECK_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
|
|
55
78
|
//# sourceMappingURL=constants.js.map
|
|
@@ -2,6 +2,7 @@ import path from 'path';
|
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
3
|
import { SETTINGS_DIR } from '../constants.js';
|
|
4
4
|
import { extractZipFile } from '../../utils/clientUtils.js';
|
|
5
|
+
import { Logger } from '../../utils/logger.js';
|
|
5
6
|
/**
|
|
6
7
|
* Abstract base class with common functionality for all requirement installers
|
|
7
8
|
*/
|
|
@@ -51,12 +52,17 @@ export class BaseInstaller {
|
|
|
51
52
|
...currentStatus,
|
|
52
53
|
availableUpdate: {
|
|
53
54
|
version: latestVersion,
|
|
54
|
-
message: `Update available: ${currentStatus.version} → ${latestVersion}
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
message: `Update available: ${currentStatus.version} → ${latestVersion}`
|
|
56
|
+
},
|
|
57
|
+
lastCheckTime: new Date().toISOString()
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return {
|
|
62
|
+
...currentStatus,
|
|
63
|
+
lastCheckTime: new Date().toISOString()
|
|
57
64
|
};
|
|
58
65
|
}
|
|
59
|
-
return currentStatus;
|
|
60
66
|
}
|
|
61
67
|
catch (error) {
|
|
62
68
|
// Don't update status on error, just log it
|
|
@@ -88,31 +94,42 @@ export class BaseInstaller {
|
|
|
88
94
|
let version = requirement.version;
|
|
89
95
|
let resolvedAssetName = assetName || '';
|
|
90
96
|
let resolvedAssetsName = assetsName || '';
|
|
97
|
+
const { stdout } = await this.execPromise(`gh release view --repo ${repository} --json tagName --jq .tagName`);
|
|
98
|
+
const latestTag = stdout.trim();
|
|
91
99
|
// Handle latest version detection
|
|
92
100
|
if (version.includes('${latest}') || version === 'latest') {
|
|
93
|
-
const { stdout } = await this.execPromise(`gh release view --repo ${repository} --json tagName --jq .tagName`);
|
|
94
|
-
const latestTag = stdout.trim();
|
|
95
101
|
let latestVersion = latestTag;
|
|
96
102
|
if (latestVersion.startsWith('v') && version.startsWith('v')) {
|
|
97
103
|
latestVersion = latestVersion.substring(1); // Remove 'v' prefix if present
|
|
98
104
|
// Replace ${latest} in version and asset names
|
|
99
105
|
version = version.replace('${latest}', latestVersion);
|
|
100
106
|
if (assetsName) {
|
|
101
|
-
resolvedAssetsName = assetsName.replace('${latest}', latestVersion);
|
|
107
|
+
resolvedAssetsName = assetsName.replace('${latest}', latestVersion).replace('${version}', version);
|
|
102
108
|
}
|
|
103
109
|
if (assetName) {
|
|
104
|
-
resolvedAssetName = assetName.replace('${latest}', latestVersion);
|
|
110
|
+
resolvedAssetName = assetName.replace('${latest}', latestVersion).replace('${version}', version);
|
|
105
111
|
}
|
|
106
112
|
}
|
|
107
113
|
}
|
|
114
|
+
else {
|
|
115
|
+
if (assetsName) {
|
|
116
|
+
resolvedAssetsName = assetsName.replace('${latest}', version).replace('${version}', version);
|
|
117
|
+
}
|
|
118
|
+
if (assetName) {
|
|
119
|
+
resolvedAssetName = assetName.replace('${latest}', version).replace('${version}', version);
|
|
120
|
+
}
|
|
121
|
+
Logger.debug(`Downloading ${requirement.name} from GitHub release ${repository} version ${version}`);
|
|
122
|
+
Logger.debug(`ResolvedAssetsName} ${resolvedAssetName}; ResolvedAsetName} ${resolvedAssetName}`);
|
|
123
|
+
}
|
|
108
124
|
const pattern = resolvedAssetsName ? resolvedAssetsName : resolvedAssetName;
|
|
125
|
+
Logger.debug(`Resolved pattern: ${pattern}`);
|
|
109
126
|
if (!pattern) {
|
|
110
127
|
throw new Error('Either assetsName or assetName must be specified for GitHub release downloads');
|
|
111
128
|
}
|
|
112
129
|
// Download the release asset
|
|
113
130
|
const downloadPath = path.join(this.downloadsDir, path.basename(pattern));
|
|
114
131
|
if (!await this.fileExists(downloadPath)) {
|
|
115
|
-
await this.execPromise(`gh release download ${version} --repo ${repository} --pattern "${pattern}" -O "${downloadPath}"`);
|
|
132
|
+
await this.execPromise(`gh release download ${version.startsWith('v') ? version : `v${version}`} --repo ${repository} --pattern "${pattern}" -O "${downloadPath}"`);
|
|
116
133
|
}
|
|
117
134
|
// Handle zip file extraction if the downloaded file is a zip
|
|
118
135
|
if (downloadPath.endsWith('.zip')) {
|
|
@@ -8,6 +8,12 @@ export declare class ClientInstaller {
|
|
|
8
8
|
constructor(categoryName: string, serverName: string, clients: string[]);
|
|
9
9
|
private generateOperationId;
|
|
10
10
|
private getNpmPath;
|
|
11
|
+
/**
|
|
12
|
+
* Check if a command is available on the system
|
|
13
|
+
* @param command The command to check
|
|
14
|
+
* @returns True if the command is available, false otherwise
|
|
15
|
+
*/
|
|
16
|
+
private isCommandAvailable;
|
|
11
17
|
private installClient;
|
|
12
18
|
private processInstallation;
|
|
13
19
|
private installClientConfig;
|