imcp 0.0.12 → 0.0.14
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/dist/core/ConfigurationProvider.d.ts +2 -1
- package/dist/core/ConfigurationProvider.js +20 -24
- package/dist/core/InstallationService.d.ts +17 -0
- package/dist/core/InstallationService.js +127 -61
- package/dist/core/MCPManager.d.ts +1 -0
- package/dist/core/MCPManager.js +3 -0
- package/dist/core/RequirementService.d.ts +4 -4
- package/dist/core/RequirementService.js +11 -7
- package/dist/core/ServerSchemaProvider.d.ts +1 -1
- package/dist/core/ServerSchemaProvider.js +15 -10
- package/dist/core/constants.d.ts +3 -0
- package/dist/core/constants.js +4 -1
- package/dist/core/installers/clients/ClientInstaller.js +58 -40
- package/dist/core/installers/requirements/PipInstaller.js +10 -5
- package/dist/core/onboard/FeedOnboardService.d.ts +35 -0
- package/dist/core/onboard/FeedOnboardService.js +137 -0
- package/dist/core/types.d.ts +6 -1
- package/dist/core/validators/FeedValidator.d.ts +13 -0
- package/dist/core/validators/FeedValidator.js +27 -0
- package/dist/services/ServerService.d.ts +5 -0
- package/dist/services/ServerService.js +15 -0
- package/dist/utils/githubAuth.js +0 -10
- package/dist/utils/githubUtils.d.ts +16 -0
- package/dist/utils/githubUtils.js +55 -39
- package/dist/web/contract/serverContract.d.ts +64 -0
- package/dist/web/contract/serverContract.js +2 -0
- package/dist/web/public/css/detailsWidget.css +157 -32
- package/dist/web/public/css/onboard.css +44 -0
- package/dist/web/public/css/serverDetails.css +35 -19
- package/dist/web/public/index.html +16 -10
- package/dist/web/public/js/detailsWidget.js +43 -40
- package/dist/web/public/js/modal/index.js +58 -0
- package/dist/web/public/js/modal/installHandler.js +227 -0
- package/dist/web/public/js/modal/installModal.js +163 -0
- package/dist/web/public/js/modal/installation.js +281 -0
- package/dist/web/public/js/modal/loadingModal.js +52 -0
- package/dist/web/public/js/modal/loadingUI.js +74 -0
- package/dist/web/public/js/modal/messageQueue.js +112 -0
- package/dist/web/public/js/modal/modalSetup.js +512 -0
- package/dist/web/public/js/modal/modalUI.js +214 -0
- package/dist/web/public/js/modal/modalUtils.js +49 -0
- package/dist/web/public/js/modal/version.js +20 -0
- package/dist/web/public/js/modal/versionUtils.js +20 -0
- package/dist/web/public/js/modal.js +25 -1041
- package/dist/web/public/js/onboard/formProcessor.js +309 -0
- package/dist/web/public/js/onboard/index.js +131 -0
- package/dist/web/public/js/onboard/state.js +32 -0
- package/dist/web/public/js/onboard/templates.js +375 -0
- package/dist/web/public/js/onboard/uiHandlers.js +196 -0
- package/dist/web/public/js/serverCategoryDetails.js +211 -123
- package/dist/web/public/onboard.html +150 -0
- package/dist/web/server.js +25 -0
- package/package.json +3 -4
- package/src/core/ConfigurationProvider.ts +37 -29
- package/src/core/InstallationService.ts +176 -62
- package/src/core/MCPManager.ts +4 -0
- package/src/core/RequirementService.ts +12 -8
- package/src/core/ServerSchemaLoader.ts +48 -0
- package/src/core/ServerSchemaProvider.ts +137 -0
- package/src/core/constants.ts +4 -1
- package/src/core/installers/clients/ClientInstaller.ts +66 -49
- package/src/core/installers/requirements/PipInstaller.ts +10 -5
- package/src/core/types.ts +6 -1
- package/src/services/ServerService.ts +15 -0
- package/src/utils/githubAuth.ts +14 -27
- package/src/utils/githubUtils.ts +84 -47
- package/src/web/public/css/detailsWidget.css +235 -0
- package/src/web/public/css/serverDetails.css +126 -0
- package/src/web/public/index.html +16 -10
- package/src/web/public/js/detailsWidget.js +264 -0
- package/src/web/public/js/modal/index.js +58 -0
- package/src/web/public/js/modal/installModal.js +163 -0
- package/src/web/public/js/modal/installation.js +281 -0
- package/src/web/public/js/modal/loadingModal.js +52 -0
- package/src/web/public/js/modal/messageQueue.js +112 -0
- package/src/web/public/js/modal/modalSetup.js +512 -0
- package/src/web/public/js/modal/modalUtils.js +49 -0
- package/src/web/public/js/modal/versionUtils.js +20 -0
- package/src/web/public/js/modal.js +25 -1041
- package/src/web/public/js/serverCategoryDetails.js +211 -123
- package/src/web/server.ts +31 -0
|
@@ -276,6 +276,7 @@ export class ClientInstaller {
|
|
|
276
276
|
// --- Start of new logic ---
|
|
277
277
|
// Clone the base installation configuration to avoid modifying the original serverConfig
|
|
278
278
|
const installConfig = JSON.parse(JSON.stringify(serverConfig.installation));
|
|
279
|
+
installConfig.mode = serverConfig.mode;
|
|
279
280
|
const pythonEnv = options.settings?.pythonEnv;
|
|
280
281
|
let pythonDir = null;
|
|
281
282
|
// 1. Determine which args to use and resolve npm paths
|
|
@@ -499,39 +500,48 @@ export class ClientInstaller {
|
|
|
499
500
|
if (!settings.mcpServers) {
|
|
500
501
|
settings.mcpServers = {};
|
|
501
502
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
serverConfigForClient.env
|
|
503
|
+
if (installConfig.mode === 'stdio') {
|
|
504
|
+
// Special handling for Windows when command is npx for Cline and MSROO clients
|
|
505
|
+
// Use a copy to avoid modifying the passed installConfig directly if needed elsewhere
|
|
506
|
+
const serverConfigForClient = { ...installConfig };
|
|
507
|
+
if (process.platform === 'win32' &&
|
|
508
|
+
serverConfigForClient.command === 'npx' &&
|
|
509
|
+
(clientName === 'Cline' || clientName === 'MSRooCode' || clientName === 'MSROO')) {
|
|
510
|
+
// Update command to cmd
|
|
511
|
+
serverConfigForClient.command = 'cmd';
|
|
512
|
+
// Add /c and npx at the beginning of args
|
|
513
|
+
serverConfigForClient.args = ['/c', 'npx', ...serverConfigForClient.args];
|
|
514
|
+
// Add APPDATA environment variable pointing to npm directory
|
|
515
|
+
if (!serverConfigForClient.env) {
|
|
516
|
+
serverConfigForClient.env = {};
|
|
517
|
+
}
|
|
518
|
+
// Dynamically get npm path and set APPDATA to it
|
|
519
|
+
const npmPath = await this.getNpmPath();
|
|
520
|
+
serverConfigForClient.env['APPDATA'] = npmPath;
|
|
521
|
+
Logger.debug(`Windows npx fix: command=${serverConfigForClient.command}, args=${serverConfigForClient.args.join(' ')}, env=${JSON.stringify(serverConfigForClient.env)}`);
|
|
522
|
+
}
|
|
523
|
+
// Convert backslashes to forward slashes in args paths
|
|
524
|
+
if (serverConfigForClient.args) {
|
|
525
|
+
serverConfigForClient.args = serverConfigForClient.args.map((arg) => typeof arg === 'string' ? arg.replace(/\\/g, '/') : arg);
|
|
515
526
|
}
|
|
516
|
-
//
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
527
|
+
// Add or update the server configuration
|
|
528
|
+
settings.mcpServers[serverName] = {
|
|
529
|
+
command: serverConfigForClient.command,
|
|
530
|
+
args: serverConfigForClient.args,
|
|
531
|
+
env: serverConfigForClient.env,
|
|
532
|
+
autoApprove: [],
|
|
533
|
+
disabled: false,
|
|
534
|
+
alwaysAllow: []
|
|
535
|
+
};
|
|
536
|
+
Logger.debug(`Updating ${settingPath} for ${serverName}: ${JSON.stringify(settings.mcpServers[serverName])}`);
|
|
520
537
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
538
|
+
else if (installConfig.mode === 'sse') {
|
|
539
|
+
// Handle SSE mode for Cline and MSRoo clients
|
|
540
|
+
settings.mcpServers[serverName] = {
|
|
541
|
+
type: 'sse',
|
|
542
|
+
url: installConfig.url
|
|
543
|
+
};
|
|
524
544
|
}
|
|
525
|
-
// Add or update the server configuration
|
|
526
|
-
settings.mcpServers[serverName] = {
|
|
527
|
-
command: serverConfigForClient.command,
|
|
528
|
-
args: serverConfigForClient.args,
|
|
529
|
-
env: serverConfigForClient.env,
|
|
530
|
-
autoApprove: [],
|
|
531
|
-
disabled: false,
|
|
532
|
-
alwaysAllow: []
|
|
533
|
-
};
|
|
534
|
-
Logger.debug(`Updating ${settingPath} for ${serverName}: ${JSON.stringify(settings.mcpServers[serverName])}`);
|
|
535
545
|
// Write the updated settings back to the file
|
|
536
546
|
await writeJsonFile(settingPath, settings);
|
|
537
547
|
}
|
|
@@ -551,17 +561,25 @@ export class ClientInstaller {
|
|
|
551
561
|
}
|
|
552
562
|
// Use a copy to avoid modifying the passed installConfig directly
|
|
553
563
|
const serverConfigForClient = { ...installConfig };
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
564
|
+
if (installConfig.mode === 'stdio') {
|
|
565
|
+
if (serverConfigForClient.args) {
|
|
566
|
+
serverConfigForClient.args = serverConfigForClient.args.map((arg) => typeof arg === 'string' ? arg.replace(/\\/g, '/') : arg);
|
|
567
|
+
}
|
|
568
|
+
// Add or update the server configuration
|
|
569
|
+
settings.mcp.servers[serverName] = {
|
|
570
|
+
command: serverConfigForClient.command,
|
|
571
|
+
args: serverConfigForClient.args,
|
|
572
|
+
env: serverConfigForClient.env
|
|
573
|
+
};
|
|
574
|
+
Logger.debug(`Updating ${settingPath} for ${serverName}: ${JSON.stringify(settings.mcp.servers[serverName])}`);
|
|
575
|
+
}
|
|
576
|
+
else if (installConfig.mode === 'sse') {
|
|
577
|
+
// Handle SSE mode for Github Copilot
|
|
578
|
+
settings.mcp.servers[serverName] = {
|
|
579
|
+
type: 'sse',
|
|
580
|
+
url: installConfig.url
|
|
581
|
+
};
|
|
557
582
|
}
|
|
558
|
-
// Add or update the server configuration
|
|
559
|
-
settings.mcp.servers[serverName] = {
|
|
560
|
-
command: serverConfigForClient.command,
|
|
561
|
-
args: serverConfigForClient.args,
|
|
562
|
-
env: serverConfigForClient.env
|
|
563
|
-
};
|
|
564
|
-
Logger.debug(`Updating ${settingPath} for ${serverName}: ${JSON.stringify(settings.mcp.servers[serverName])}`);
|
|
565
583
|
// Write the updated settings back to the file
|
|
566
584
|
await writeJsonFile(settingPath, settings);
|
|
567
585
|
}
|
|
@@ -21,7 +21,7 @@ export class PipInstaller extends BaseInstaller {
|
|
|
21
21
|
}
|
|
22
22
|
supportCheckUpdates() {
|
|
23
23
|
/// temporarily disabling update check for pip as not able to get which pip of python is being used
|
|
24
|
-
return
|
|
24
|
+
return true;
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
27
|
* Check if the Python package is already installed
|
|
@@ -41,7 +41,8 @@ export class PipInstaller extends BaseInstaller {
|
|
|
41
41
|
type: 'pip',
|
|
42
42
|
installed,
|
|
43
43
|
version: installedVersion,
|
|
44
|
-
inProgress: false
|
|
44
|
+
inProgress: false,
|
|
45
|
+
pythonEnv: this.getPythonCommand(options) // Preserve pythonEnv in status check too
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
48
|
catch (error) {
|
|
@@ -50,7 +51,8 @@ export class PipInstaller extends BaseInstaller {
|
|
|
50
51
|
type: 'pip',
|
|
51
52
|
installed: false,
|
|
52
53
|
error: error instanceof Error ? error.message : String(error),
|
|
53
|
-
inProgress: false
|
|
54
|
+
inProgress: false,
|
|
55
|
+
pythonEnv: this.getPythonCommand(options) // Preserve pythonEnv even in error case
|
|
54
56
|
};
|
|
55
57
|
}
|
|
56
58
|
}
|
|
@@ -106,12 +108,14 @@ export class PipInstaller extends BaseInstaller {
|
|
|
106
108
|
throw new Error('Invalid registry configuration');
|
|
107
109
|
}
|
|
108
110
|
}
|
|
111
|
+
// Store the pythonEnv in the status for future use
|
|
109
112
|
return {
|
|
110
113
|
name: requirement.name,
|
|
111
114
|
type: 'pip',
|
|
112
115
|
installed: true,
|
|
113
116
|
version: requirement.version,
|
|
114
|
-
inProgress: false
|
|
117
|
+
inProgress: false,
|
|
118
|
+
pythonEnv: this.getPythonCommand(options) // Store the python env
|
|
115
119
|
};
|
|
116
120
|
}
|
|
117
121
|
catch (error) {
|
|
@@ -120,7 +124,8 @@ export class PipInstaller extends BaseInstaller {
|
|
|
120
124
|
type: 'pip',
|
|
121
125
|
installed: false,
|
|
122
126
|
error: error instanceof Error ? error.message : String(error),
|
|
123
|
-
inProgress: false
|
|
127
|
+
inProgress: false,
|
|
128
|
+
pythonEnv: this.getPythonCommand(options) // Store the python env
|
|
124
129
|
};
|
|
125
130
|
}
|
|
126
131
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { FeedConfiguration } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Service for handling feed onboarding operations
|
|
4
|
+
*/
|
|
5
|
+
export declare class FeedOnboardService {
|
|
6
|
+
private tempDir;
|
|
7
|
+
private repoDir;
|
|
8
|
+
constructor();
|
|
9
|
+
/**
|
|
10
|
+
* Onboard a new feed configuration
|
|
11
|
+
* @param config Feed configuration to onboard
|
|
12
|
+
*/
|
|
13
|
+
onboardFeed(config: FeedConfiguration): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Fork the repository using gh CLI
|
|
16
|
+
*/
|
|
17
|
+
private forkRepo;
|
|
18
|
+
/**
|
|
19
|
+
* Clone the forked repository
|
|
20
|
+
*/
|
|
21
|
+
private cloneRepo;
|
|
22
|
+
/**
|
|
23
|
+
* Get GitHub username using gh CLI
|
|
24
|
+
*/
|
|
25
|
+
private getGitHubUsername;
|
|
26
|
+
/**
|
|
27
|
+
* Save feed configuration to file
|
|
28
|
+
*/
|
|
29
|
+
private saveFeedConfig;
|
|
30
|
+
/**
|
|
31
|
+
* Create pull request using gh CLI
|
|
32
|
+
*/
|
|
33
|
+
private createPullRequest;
|
|
34
|
+
}
|
|
35
|
+
export declare const feedOnboardService: FeedOnboardService;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { GITHUB_REPO, SETTINGS_DIR } from '../constants.js';
|
|
6
|
+
import { feedValidator } from '../validators/FeedValidator.js';
|
|
7
|
+
import { Logger } from '../../utils/logger.js';
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
/**
|
|
10
|
+
* Service for handling feed onboarding operations
|
|
11
|
+
*/
|
|
12
|
+
export class FeedOnboardService {
|
|
13
|
+
tempDir;
|
|
14
|
+
repoDir;
|
|
15
|
+
constructor() {
|
|
16
|
+
this.tempDir = path.join(SETTINGS_DIR, 'temp');
|
|
17
|
+
this.repoDir = path.join(this.tempDir, 'imcp-feed');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Onboard a new feed configuration
|
|
21
|
+
* @param config Feed configuration to onboard
|
|
22
|
+
*/
|
|
23
|
+
async onboardFeed(config) {
|
|
24
|
+
try {
|
|
25
|
+
// Validate feed configuration
|
|
26
|
+
feedValidator.validate(config);
|
|
27
|
+
// Create temp directory
|
|
28
|
+
await fs.mkdir(this.tempDir, { recursive: true });
|
|
29
|
+
// Fork the repo
|
|
30
|
+
await this.forkRepo();
|
|
31
|
+
// Clone the forked repo
|
|
32
|
+
await this.cloneRepo();
|
|
33
|
+
// Save feed configuration
|
|
34
|
+
await this.saveFeedConfig(config);
|
|
35
|
+
// Create branch, push changes and create PR
|
|
36
|
+
await this.createPullRequest(config);
|
|
37
|
+
Logger.log(`Successfully onboarded feed: ${config.name}`);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
Logger.error('Feed onboarding failed:', error);
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
// Cleanup temp directory
|
|
45
|
+
await fs.rm(this.tempDir, { recursive: true, force: true });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Fork the repository using gh CLI
|
|
50
|
+
*/
|
|
51
|
+
async forkRepo() {
|
|
52
|
+
try {
|
|
53
|
+
await execAsync(`gh repo fork ${GITHUB_REPO.repoName} --clone=false`);
|
|
54
|
+
Logger.debug('Successfully forked repository');
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw new Error(`Failed to fork repository: ${error instanceof Error ? error.message : String(error)}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Clone the forked repository
|
|
62
|
+
*/
|
|
63
|
+
async cloneRepo() {
|
|
64
|
+
try {
|
|
65
|
+
// Clean up existing repo directory
|
|
66
|
+
await fs.rm(this.repoDir, { recursive: true, force: true });
|
|
67
|
+
const username = await this.getGitHubUsername();
|
|
68
|
+
const repoUrl = `https://github.com/${username}/${GITHUB_REPO.repoName.split('/')[1]}.git`;
|
|
69
|
+
// Ensure temp dir exists
|
|
70
|
+
await fs.mkdir(this.tempDir, { recursive: true });
|
|
71
|
+
// Clone the repo into imcp-feed subdirectory
|
|
72
|
+
await execAsync(`git clone ${repoUrl} ${this.repoDir}`);
|
|
73
|
+
Logger.debug('Successfully cloned repository');
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
throw new Error(`Failed to clone repository: ${error instanceof Error ? error.message : String(error)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get GitHub username using gh CLI
|
|
81
|
+
*/
|
|
82
|
+
async getGitHubUsername() {
|
|
83
|
+
try {
|
|
84
|
+
const { stdout } = await execAsync('gh api user -q .login');
|
|
85
|
+
return stdout.trim();
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
throw new Error(`Failed to get GitHub username: ${error instanceof Error ? error.message : String(error)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Save feed configuration to file
|
|
93
|
+
*/
|
|
94
|
+
async saveFeedConfig(config) {
|
|
95
|
+
try {
|
|
96
|
+
const feedsDir = path.join(this.repoDir, GITHUB_REPO.feedsPath);
|
|
97
|
+
await fs.mkdir(feedsDir, { recursive: true });
|
|
98
|
+
const configPath = path.join(feedsDir, `${config.name}.json`);
|
|
99
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
100
|
+
Logger.debug(`Saved feed configuration to ${configPath}`);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
throw new Error(`Failed to save feed configuration: ${error instanceof Error ? error.message : String(error)}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Create pull request using gh CLI
|
|
108
|
+
*/
|
|
109
|
+
async createPullRequest(config) {
|
|
110
|
+
try {
|
|
111
|
+
process.chdir(this.repoDir);
|
|
112
|
+
// Ensure we're up to date with upstream main
|
|
113
|
+
await execAsync(`git remote add upstream ${GITHUB_REPO.url}`);
|
|
114
|
+
await execAsync('git fetch upstream');
|
|
115
|
+
await execAsync('git reset --hard upstream/main');
|
|
116
|
+
// Create and checkout new branch
|
|
117
|
+
const branchName = `feed/${config.name}`;
|
|
118
|
+
await execAsync(`git checkout -b ${branchName}`);
|
|
119
|
+
// Stage and commit changes
|
|
120
|
+
await execAsync('git add .');
|
|
121
|
+
await execAsync(`git commit -m "Add feed configuration for ${config.name}"`);
|
|
122
|
+
// Push to origin
|
|
123
|
+
await execAsync(`git push -f origin ${branchName}`);
|
|
124
|
+
const title = `Add feed configuration for ${config.name}`;
|
|
125
|
+
const body = `Add new feed configuration:\n\n- Name: ${config.name}\n- Display Name: ${config.displayName}\n- Description: ${config.description}`;
|
|
126
|
+
const username = await this.getGitHubUsername();
|
|
127
|
+
await execAsync(`gh pr create --title "${title}" --body "${body}" --repo ${GITHUB_REPO.repoName} --base main --head ${username}:${branchName}`);
|
|
128
|
+
Logger.debug('Successfully created pull request');
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
throw new Error(`Failed to create pull request: ${error instanceof Error ? error.message : String(error)}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Export singleton instance
|
|
136
|
+
export const feedOnboardService = new FeedOnboardService();
|
|
137
|
+
//# sourceMappingURL=FeedOnboardService.js.map
|
package/dist/core/types.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface RequirementStatus {
|
|
|
16
16
|
};
|
|
17
17
|
lastCheckTime?: string;
|
|
18
18
|
operationStatus?: OperationStatus;
|
|
19
|
+
pythonEnv?: string;
|
|
19
20
|
}
|
|
20
21
|
export interface MCPServerStatus {
|
|
21
22
|
installedStatus: Record<string, OperationStatus>;
|
|
@@ -84,6 +85,7 @@ export interface InstallationConfig {
|
|
|
84
85
|
command: string;
|
|
85
86
|
args: string[];
|
|
86
87
|
env?: Record<string, EnvVariableConfig>;
|
|
88
|
+
url?: string;
|
|
87
89
|
}
|
|
88
90
|
export interface DependencyConfig {
|
|
89
91
|
requirements?: Array<{
|
|
@@ -98,8 +100,10 @@ export interface DependencyConfig {
|
|
|
98
100
|
export interface McpConfig {
|
|
99
101
|
name: string;
|
|
100
102
|
description: string;
|
|
101
|
-
mode: 'stdio' | '
|
|
103
|
+
mode: 'stdio' | 'sse';
|
|
102
104
|
dependencies?: DependencyConfig;
|
|
105
|
+
schemas?: string;
|
|
106
|
+
repository?: string;
|
|
103
107
|
installation: InstallationConfig;
|
|
104
108
|
}
|
|
105
109
|
export interface RegistryConfig {
|
|
@@ -128,6 +132,7 @@ export interface FeedConfiguration {
|
|
|
128
132
|
name: string;
|
|
129
133
|
displayName: string;
|
|
130
134
|
description: string;
|
|
135
|
+
repository?: string;
|
|
131
136
|
requirements: RequirementConfig[];
|
|
132
137
|
mcpServers: McpConfig[];
|
|
133
138
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FeedConfiguration } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Validates feed configurations to ensure they meet required criteria
|
|
4
|
+
*/
|
|
5
|
+
export declare class FeedValidator {
|
|
6
|
+
/**
|
|
7
|
+
* Validates a feed configuration
|
|
8
|
+
* @param config The feed configuration to validate
|
|
9
|
+
* @returns true if valid, throws error if invalid
|
|
10
|
+
*/
|
|
11
|
+
validate(config: FeedConfiguration): boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare const feedValidator: FeedValidator;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates feed configurations to ensure they meet required criteria
|
|
3
|
+
*/
|
|
4
|
+
export class FeedValidator {
|
|
5
|
+
/**
|
|
6
|
+
* Validates a feed configuration
|
|
7
|
+
* @param config The feed configuration to validate
|
|
8
|
+
* @returns true if valid, throws error if invalid
|
|
9
|
+
*/
|
|
10
|
+
validate(config) {
|
|
11
|
+
try {
|
|
12
|
+
// For now return true as requested
|
|
13
|
+
return true;
|
|
14
|
+
// TODO: Implement full validation logic:
|
|
15
|
+
// - Required fields (name, displayName, etc)
|
|
16
|
+
// - Server configuration validation
|
|
17
|
+
// - Requirements validation
|
|
18
|
+
// - Repository validation
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
throw new Error(`Feed validation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Export singleton instance
|
|
26
|
+
export const feedValidator = new FeedValidator();
|
|
27
|
+
//# sourceMappingURL=FeedValidator.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ServerSchema } from '../core/ServerSchemaProvider.js';
|
|
1
2
|
import { MCPServerCategory, ServerInstallOptions, ServerCategoryListOptions, ServerOperationResult, ServerUninstallOptions } from '../core/types.js';
|
|
2
3
|
/**
|
|
3
4
|
* ServerService provides a unified interface for server management operations.
|
|
@@ -18,6 +19,10 @@ export declare class ServerService {
|
|
|
18
19
|
* @private
|
|
19
20
|
*/
|
|
20
21
|
private checkRequirementsForUpdate;
|
|
22
|
+
/**
|
|
23
|
+
* Gets the schema for a specific server in a category
|
|
24
|
+
*/
|
|
25
|
+
getServerSchema(categoryName: string, serverName: string): Promise<ServerSchema | undefined>;
|
|
21
26
|
/**
|
|
22
27
|
* Installs a specific mcp tool for a server.
|
|
23
28
|
* TODO: This might require enhancing MCPManager to handle category-specific installs.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import { Logger } from '../utils/logger.js';
|
|
4
|
+
import { getServerSchemaProvider } from '../core/ServerSchemaProvider.js';
|
|
4
5
|
import { mcpManager } from '../core/MCPManager.js';
|
|
5
6
|
import { UPDATE_CHECK_INTERVAL_MS } from '../core/constants.js';
|
|
6
7
|
import { updateCheckTracker } from '../utils/UpdateCheckTracker.js';
|
|
@@ -96,6 +97,20 @@ export class ServerService {
|
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Gets the schema for a specific server in a category
|
|
102
|
+
*/
|
|
103
|
+
async getServerSchema(categoryName, serverName) {
|
|
104
|
+
try {
|
|
105
|
+
const provider = await getServerSchemaProvider();
|
|
106
|
+
const serverMcpConfig = await mcpManager.getServerMcpConfig(categoryName, serverName);
|
|
107
|
+
return await provider.getSchema(categoryName, serverMcpConfig?.schemas || `${serverName}.json`);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
Logger.error(`Failed to get schema for server ${serverName} in category ${categoryName}:`, error);
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
99
114
|
/**
|
|
100
115
|
* Installs a specific mcp tool for a server.
|
|
101
116
|
* TODO: This might require enhancing MCPManager to handle category-specific installs.
|
package/dist/utils/githubAuth.js
CHANGED
|
@@ -29,16 +29,6 @@ class GithubAuthError extends Error {
|
|
|
29
29
|
export async function checkGithubAuth() {
|
|
30
30
|
Logger.debug('Starting GitHub authentication check');
|
|
31
31
|
try {
|
|
32
|
-
// Check if git is installed
|
|
33
|
-
if (!await isToolInstalled('git')) {
|
|
34
|
-
Logger.log('Installing required Git...');
|
|
35
|
-
await installCLI('git');
|
|
36
|
-
// Verify git was installed correctly, with retry mechanism
|
|
37
|
-
if (!await isToolInstalled('git')) {
|
|
38
|
-
throw new Error('Failed to install Git. Please install it manually and try again.');
|
|
39
|
-
}
|
|
40
|
-
Logger.debug('Git installed successfully and verified');
|
|
41
|
-
}
|
|
42
32
|
// Check if gh CLI is installed
|
|
43
33
|
if (!await isToolInstalled('gh')) {
|
|
44
34
|
Logger.log('Installing required GitHub CLI...');
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import { RegistryConfig, RequirementConfig } from '../core/types.js';
|
|
2
|
+
interface DownloadGithubReleaseResult {
|
|
3
|
+
version: string;
|
|
4
|
+
downloadPath: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Downloads a GitHub release asset
|
|
8
|
+
* @param repo GitHub repository in format owner/repo
|
|
9
|
+
* @param version Version to download, can be "latest"
|
|
10
|
+
* @param assetsName Assets name pattern (optional, but either assetsName or assetName must be provided)
|
|
11
|
+
* @param assetName Asset name pattern (optional, but either assetsName or assetName must be provided)
|
|
12
|
+
* @param isFolder Whether to treat the downloaded asset as a folder (default: false)
|
|
13
|
+
* @param targetDirectory Target directory for downloads (default: SETTINGS_DIR/downloads)
|
|
14
|
+
* @returns Object containing version and download path
|
|
15
|
+
*/
|
|
16
|
+
export declare function downloadGithubRelease(repo: string, version: string, assetsName?: string, assetName?: string, isFolder?: boolean, targetDirectory?: string): Promise<DownloadGithubReleaseResult>;
|
|
2
17
|
/**
|
|
3
18
|
* Helper to handle GitHub release downloads
|
|
4
19
|
* @param requirement The requirement configuration
|
|
@@ -9,3 +24,4 @@ export declare function handleGitHubRelease(requirement: RequirementConfig, regi
|
|
|
9
24
|
resolvedVersion: string;
|
|
10
25
|
resolvedPath: string;
|
|
11
26
|
}>;
|
|
27
|
+
export {};
|
|
@@ -3,78 +3,94 @@ import util from 'util';
|
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { extractZipFile } from './clientUtils.js';
|
|
6
|
-
import { Logger } from './logger.js';
|
|
7
6
|
import { SETTINGS_DIR } from '../core/constants.js';
|
|
8
7
|
const execAsync = util.promisify(exec);
|
|
9
8
|
/**
|
|
10
|
-
*
|
|
11
|
-
* @param
|
|
12
|
-
* @param
|
|
13
|
-
* @
|
|
9
|
+
* Downloads a GitHub release asset
|
|
10
|
+
* @param repo GitHub repository in format owner/repo
|
|
11
|
+
* @param version Version to download, can be "latest"
|
|
12
|
+
* @param assetsName Assets name pattern (optional, but either assetsName or assetName must be provided)
|
|
13
|
+
* @param assetName Asset name pattern (optional, but either assetsName or assetName must be provided)
|
|
14
|
+
* @param isFolder Whether to treat the downloaded asset as a folder (default: false)
|
|
15
|
+
* @param targetDirectory Target directory for downloads (default: SETTINGS_DIR/downloads)
|
|
16
|
+
* @returns Object containing version and download path
|
|
14
17
|
*/
|
|
15
|
-
export async function
|
|
16
|
-
if (!
|
|
17
|
-
throw new Error('GitHub
|
|
18
|
+
export async function downloadGithubRelease(repo, version, assetsName, assetName, isFolder = false, targetDirectory) {
|
|
19
|
+
if (!repo) {
|
|
20
|
+
throw new Error('GitHub repository is required');
|
|
18
21
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const { repository, assetsName, assetName } = registry;
|
|
22
|
-
if (!repository) {
|
|
23
|
-
throw new Error('GitHub repository is required for GitHub release downloads');
|
|
22
|
+
if (!assetsName && !assetName) {
|
|
23
|
+
throw new Error('Either assetsName or assetName must be specified');
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const { stdout } = await execAsync(`gh release view --repo ${
|
|
25
|
+
const downloadsDir = targetDirectory || path.join(SETTINGS_DIR, 'downloads');
|
|
26
|
+
await fs.mkdir(downloadsDir, { recursive: true });
|
|
27
|
+
// Get latest version if needed
|
|
28
|
+
const { stdout } = await execAsync(`gh release view --repo ${repo} --json tagName --jq .tagName`);
|
|
29
29
|
const latestTag = stdout.trim();
|
|
30
30
|
let latestVersion = latestTag;
|
|
31
31
|
const tagWithVPrefix = latestVersion.startsWith('v');
|
|
32
32
|
if (tagWithVPrefix)
|
|
33
33
|
latestVersion = latestVersion.substring(1); // Remove 'v' prefix if present
|
|
34
|
-
|
|
34
|
+
const resolvedVersion = version.includes("latest") ? latestVersion : version;
|
|
35
|
+
// Resolve asset names
|
|
36
|
+
let resolvedAssetsName = '';
|
|
37
|
+
let resolvedAssetName = '';
|
|
35
38
|
if (assetsName) {
|
|
36
|
-
resolvedAssetsName = assetsName.replace('${latest}',
|
|
39
|
+
resolvedAssetsName = assetsName.replace('${latest}', resolvedVersion).replace('${version}', resolvedVersion);
|
|
37
40
|
}
|
|
38
41
|
if (assetName) {
|
|
39
|
-
resolvedAssetName = assetName.replace('${latest}',
|
|
42
|
+
resolvedAssetName = assetName.replace('${latest}', resolvedVersion).replace('${version}', resolvedVersion);
|
|
40
43
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (!pattern) {
|
|
46
|
-
throw new Error('Either assetsName or assetName must be specified for GitHub release downloads');
|
|
44
|
+
// Validate zip requirement for isFolder
|
|
45
|
+
const pattern = resolvedAssetsName || resolvedAssetName;
|
|
46
|
+
if (isFolder && (!resolvedAssetsName || !resolvedAssetsName.endsWith('.zip'))) {
|
|
47
|
+
throw new Error('When isFolder is true, assetsName must be provided and end with .zip');
|
|
47
48
|
}
|
|
48
49
|
// Download the release asset
|
|
49
50
|
const downloadPath = path.join(downloadsDir, path.basename(pattern));
|
|
50
51
|
if (!await fileExists(downloadPath)) {
|
|
51
|
-
await execAsync(`gh release download ${tagWithVPrefix ? `v${
|
|
52
|
+
await execAsync(`gh release download ${tagWithVPrefix ? `v${resolvedVersion}` : resolvedVersion} --repo ${repo} --pattern "${pattern}" -O "${downloadPath}"`);
|
|
52
53
|
}
|
|
53
|
-
// Handle zip
|
|
54
|
-
if (downloadPath.endsWith('.zip')) {
|
|
54
|
+
// Handle zip extraction if needed
|
|
55
|
+
if (isFolder && downloadPath.endsWith('.zip')) {
|
|
55
56
|
const extractDir = path.join(downloadsDir, path.basename(pattern, '.zip'));
|
|
56
57
|
await fs.mkdir(extractDir, { recursive: true });
|
|
57
|
-
// Extract the zip file
|
|
58
58
|
await extractZipFile(downloadPath, { dir: extractDir });
|
|
59
|
-
let assetPath = '';
|
|
60
59
|
// If resolvedAssetName is specified, look for it in the extracted directory
|
|
61
60
|
if (resolvedAssetName) {
|
|
62
|
-
assetPath = path.join(extractDir, resolvedAssetName);
|
|
61
|
+
const assetPath = path.join(extractDir, resolvedAssetName);
|
|
63
62
|
try {
|
|
64
63
|
await fs.access(assetPath);
|
|
65
|
-
return {
|
|
64
|
+
return { version: resolvedVersion, downloadPath: assetPath };
|
|
66
65
|
}
|
|
67
66
|
catch (error) {
|
|
68
67
|
throw new Error(`Asset ${resolvedAssetName} not found in extracted directory ${extractDir}`);
|
|
69
68
|
}
|
|
70
69
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
return { version: resolvedVersion, downloadPath: extractDir };
|
|
71
|
+
}
|
|
72
|
+
return { version: resolvedVersion, downloadPath };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Helper to handle GitHub release downloads
|
|
76
|
+
* @param requirement The requirement configuration
|
|
77
|
+
* @param registry The GitHub release registry configuration
|
|
78
|
+
* @returns The path to the downloaded file
|
|
79
|
+
*/
|
|
80
|
+
export async function handleGitHubRelease(requirement, registry) {
|
|
81
|
+
if (!registry) {
|
|
82
|
+
throw new Error('GitHub release registry configuration is required');
|
|
83
|
+
}
|
|
84
|
+
const { repository, assetsName, assetName } = registry;
|
|
85
|
+
if (!repository) {
|
|
86
|
+
throw new Error('GitHub repository is required for GitHub release downloads');
|
|
76
87
|
}
|
|
77
|
-
|
|
88
|
+
const isZipAsset = assetsName?.endsWith('.zip') || false;
|
|
89
|
+
const result = await downloadGithubRelease(repository, requirement.version, assetsName, assetName, isZipAsset);
|
|
90
|
+
return {
|
|
91
|
+
resolvedVersion: result.version,
|
|
92
|
+
resolvedPath: result.downloadPath
|
|
93
|
+
};
|
|
78
94
|
}
|
|
79
95
|
async function fileExists(filePath) {
|
|
80
96
|
try {
|