imcp 0.0.14 → 0.0.16
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 +1 -0
- package/dist/core/ConfigurationProvider.js +15 -0
- package/dist/core/InstallationService.js +2 -7
- package/dist/core/MCPManager.d.ts +11 -2
- package/dist/core/MCPManager.js +24 -1
- package/dist/core/RequirementService.js +2 -8
- package/dist/core/installers/clients/BaseClientInstaller.d.ts +51 -0
- package/dist/core/installers/clients/BaseClientInstaller.js +160 -0
- package/dist/core/installers/clients/ClientInstaller.d.ts +16 -9
- package/dist/core/installers/clients/ClientInstaller.js +80 -527
- package/dist/core/installers/clients/ClientInstallerFactory.d.ts +20 -0
- package/dist/core/installers/clients/ClientInstallerFactory.js +37 -0
- package/dist/core/installers/clients/ClineInstaller.d.ts +18 -0
- package/dist/core/installers/clients/ClineInstaller.js +124 -0
- package/dist/core/installers/clients/GithubCopilotInstaller.d.ts +34 -0
- package/dist/core/installers/clients/GithubCopilotInstaller.js +162 -0
- package/dist/core/installers/clients/MSRooCodeInstaller.d.ts +15 -0
- package/dist/core/installers/clients/MSRooCodeInstaller.js +122 -0
- package/dist/core/installers/requirements/BaseInstaller.d.ts +11 -34
- package/dist/core/installers/requirements/BaseInstaller.js +5 -116
- package/dist/core/installers/requirements/CommandInstaller.d.ts +6 -1
- package/dist/core/installers/requirements/CommandInstaller.js +7 -0
- package/dist/core/installers/requirements/GeneralInstaller.d.ts +6 -1
- package/dist/core/installers/requirements/GeneralInstaller.js +9 -4
- package/dist/core/installers/requirements/NpmInstaller.d.ts +46 -7
- package/dist/core/installers/requirements/NpmInstaller.js +150 -58
- package/dist/core/installers/requirements/PipInstaller.d.ts +9 -0
- package/dist/core/installers/requirements/PipInstaller.js +66 -28
- package/dist/core/onboard/FeedOnboardService.d.ts +50 -13
- package/dist/core/onboard/FeedOnboardService.js +263 -88
- package/dist/core/onboard/OnboardProcessor.d.ts +79 -0
- package/dist/core/onboard/OnboardProcessor.js +290 -0
- package/dist/core/onboard/OnboardStatus.d.ts +49 -0
- package/dist/core/onboard/OnboardStatus.js +10 -0
- package/dist/core/onboard/OnboardStatusManager.d.ts +57 -0
- package/dist/core/onboard/OnboardStatusManager.js +176 -0
- package/dist/core/types.d.ts +4 -5
- package/dist/core/validators/FeedValidator.d.ts +8 -1
- package/dist/core/validators/FeedValidator.js +60 -7
- package/dist/core/validators/IServerValidator.d.ts +19 -0
- package/dist/core/validators/IServerValidator.js +2 -0
- package/dist/core/validators/SSEServerValidator.d.ts +15 -0
- package/dist/core/validators/SSEServerValidator.js +39 -0
- package/dist/core/validators/ServerValidatorFactory.d.ts +24 -0
- package/dist/core/validators/ServerValidatorFactory.js +45 -0
- package/dist/core/validators/StdioServerValidator.d.ts +46 -0
- package/dist/core/validators/StdioServerValidator.js +229 -0
- package/dist/services/InstallRequestValidator.d.ts +1 -1
- package/dist/services/ServerService.d.ts +9 -6
- package/dist/services/ServerService.js +18 -7
- package/dist/utils/adoUtils.d.ts +29 -0
- package/dist/utils/adoUtils.js +252 -0
- package/dist/utils/clientUtils.d.ts +0 -7
- package/dist/utils/clientUtils.js +0 -42
- package/dist/utils/githubUtils.d.ts +10 -0
- package/dist/utils/githubUtils.js +22 -0
- package/dist/utils/macroExpressionUtils.d.ts +38 -0
- package/dist/utils/macroExpressionUtils.js +116 -0
- package/dist/utils/osUtils.d.ts +4 -20
- package/dist/utils/osUtils.js +78 -23
- package/dist/web/contract/serverContract.d.ts +3 -1
- package/dist/web/public/css/notifications.css +48 -17
- package/dist/web/public/css/onboard.css +66 -3
- package/dist/web/public/index.html +84 -16
- package/dist/web/public/js/api.js +3 -6
- package/dist/web/public/js/flights/flights.js +127 -0
- package/dist/web/public/js/modal/installation.js +5 -5
- package/dist/web/public/js/modal/modalSetup.js +3 -2
- package/dist/web/public/js/notifications.js +66 -27
- package/dist/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
- package/dist/web/public/js/onboard/formProcessor.js +810 -255
- package/dist/web/public/js/onboard/index.js +328 -85
- package/dist/web/public/js/onboard/publishHandler.js +132 -0
- package/dist/web/public/js/onboard/state.js +61 -17
- package/dist/web/public/js/onboard/templates.js +217 -249
- package/dist/web/public/js/onboard/uiHandlers.js +679 -117
- package/dist/web/public/js/onboard/validationHandlers.js +378 -0
- package/dist/web/public/js/serverCategoryList.js +15 -2
- package/dist/web/public/onboard.html +191 -45
- package/dist/web/public/styles.css +91 -1
- package/dist/web/server.d.ts +0 -10
- package/dist/web/server.js +131 -22
- package/package.json +2 -2
- package/src/core/ConfigurationProvider.ts +15 -0
- package/src/core/InstallationService.ts +2 -7
- package/src/core/MCPManager.ts +26 -1
- package/src/core/RequirementService.ts +2 -9
- package/src/core/installers/clients/BaseClientInstaller.ts +196 -0
- package/src/core/installers/clients/ClientInstaller.ts +97 -608
- package/src/core/installers/clients/ClientInstallerFactory.ts +43 -0
- package/src/core/installers/clients/ClineInstaller.ts +135 -0
- package/src/core/installers/clients/GithubCopilotInstaller.ts +179 -0
- package/src/core/installers/clients/MSRooCodeInstaller.ts +133 -0
- package/src/core/installers/requirements/BaseInstaller.ts +13 -136
- package/src/core/installers/requirements/CommandInstaller.ts +9 -1
- package/src/core/installers/requirements/GeneralInstaller.ts +11 -4
- package/src/core/installers/requirements/NpmInstaller.ts +178 -61
- package/src/core/installers/requirements/PipInstaller.ts +68 -29
- package/src/core/onboard/FeedOnboardService.ts +346 -0
- package/src/core/onboard/OnboardProcessor.ts +305 -0
- package/src/core/onboard/OnboardStatus.ts +55 -0
- package/src/core/onboard/OnboardStatusManager.ts +188 -0
- package/src/core/types.ts +4 -5
- package/src/core/validators/FeedValidator.ts +79 -0
- package/src/core/validators/IServerValidator.ts +21 -0
- package/src/core/validators/SSEServerValidator.ts +43 -0
- package/src/core/validators/ServerValidatorFactory.ts +51 -0
- package/src/core/validators/StdioServerValidator.ts +259 -0
- package/src/services/InstallRequestValidator.ts +1 -1
- package/src/services/ServerService.ts +22 -7
- package/src/utils/adoUtils.ts +291 -0
- package/src/utils/clientUtils.ts +0 -44
- package/src/utils/githubUtils.ts +24 -0
- package/src/utils/macroExpressionUtils.ts +121 -0
- package/src/utils/osUtils.ts +89 -24
- package/src/web/contract/serverContract.ts +74 -0
- package/src/web/public/css/notifications.css +48 -17
- package/src/web/public/css/onboard.css +107 -0
- package/src/web/public/index.html +84 -16
- package/src/web/public/js/api.js +3 -6
- package/src/web/public/js/flights/flights.js +127 -0
- package/src/web/public/js/modal/installation.js +5 -5
- package/src/web/public/js/modal/modalSetup.js +3 -2
- package/src/web/public/js/notifications.js +66 -27
- package/src/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
- package/src/web/public/js/onboard/formProcessor.js +864 -0
- package/src/web/public/js/onboard/index.js +374 -0
- package/src/web/public/js/onboard/publishHandler.js +132 -0
- package/src/web/public/js/onboard/state.js +76 -0
- package/src/web/public/js/onboard/templates.js +343 -0
- package/src/web/public/js/onboard/uiHandlers.js +758 -0
- package/src/web/public/js/onboard/validationHandlers.js +378 -0
- package/src/web/public/js/serverCategoryList.js +15 -2
- package/src/web/public/onboard.html +296 -0
- package/src/web/public/styles.css +91 -1
- package/src/web/server.ts +167 -58
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Onboard MCP Category - IMCP</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
<link href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css" rel="stylesheet">
|
|
9
|
+
<!-- Bootstrap CSS is likely not needed if primarily using Tailwind -->
|
|
10
|
+
<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> -->
|
|
11
|
+
<link rel="stylesheet" href="styles.css">
|
|
12
|
+
<link rel="stylesheet" href="css/onboard.css">
|
|
13
|
+
<!-- Highlight.js for JSON syntax highlighting -->
|
|
14
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
|
|
15
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
|
16
|
+
<link rel="stylesheet" href="css/modal.css">
|
|
17
|
+
<link rel="stylesheet" href="css/notifications.css">
|
|
18
|
+
<script type="module">
|
|
19
|
+
import flights, { getFlightQueryParameters } from './js/flights/flights.js';
|
|
20
|
+
if (!flights.enableOnboard) {
|
|
21
|
+
// Preserve flight params when redirecting
|
|
22
|
+
window.location.href = `index.html${getFlightQueryParameters()}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
26
|
+
const flightQueryString = getFlightQueryParameters();
|
|
27
|
+
|
|
28
|
+
const backToServerLink = document.querySelector('a[href="index.html"]');
|
|
29
|
+
if (backToServerLink) {
|
|
30
|
+
backToServerLink.href = `index.html${flightQueryString}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Update cancel buttons in forms
|
|
34
|
+
const cancelButtons = document.querySelectorAll('button[onclick="window.location.href=\'index.html\'"]');
|
|
35
|
+
cancelButtons.forEach(button => {
|
|
36
|
+
button.onclick = () => { window.location.href = `index.html${flightQueryString}`; };
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const jsonEditorCancelButton = document.querySelector('#jsonEditorActionsContainer button[onclick="window.location.href=\'index.html\'"]');
|
|
40
|
+
if (jsonEditorCancelButton) {
|
|
41
|
+
jsonEditorCancelButton.onclick = () => { window.location.href = `index.html${flightQueryString}`; };
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
</script>
|
|
45
|
+
</head>
|
|
46
|
+
<body class="bg-gray-50 min-h-screen">
|
|
47
|
+
<div class="alert-container"></div>
|
|
48
|
+
<div class="container mx-auto px-4 py-6">
|
|
49
|
+
<div class="flex items-center justify-between mb-8">
|
|
50
|
+
<h1 class="text-3xl font-bold text-gray-900 flex items-center">
|
|
51
|
+
<i class='bx bx-server mr-3 text-blue-600'></i>
|
|
52
|
+
Onboard MCP Category
|
|
53
|
+
</h1>
|
|
54
|
+
<a href="index.html" class="text-gray-600 hover:text-gray-900 flex items-center">
|
|
55
|
+
<i class='bx bx-arrow-back mr-2'></i>
|
|
56
|
+
Back to Servers
|
|
57
|
+
</a>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6 max-w-6xl mx-auto">
|
|
61
|
+
<!-- Tab Navigation -->
|
|
62
|
+
<div class="mb-6 border-b border-gray-200">
|
|
63
|
+
<nav class="flex space-x-8" aria-label="Tabs">
|
|
64
|
+
<button id="tab-create-category" class="py-2 px-4 text-blue-600 border-b-2 border-blue-600 font-semibold focus:outline-none" type="button">
|
|
65
|
+
Create New Server Category
|
|
66
|
+
</button>
|
|
67
|
+
<button id="tab-create-server" class="py-2 px-4 text-gray-600 border-b-2 border-transparent hover:text-blue-600 hover:border-blue-600 font-semibold focus:outline-none" type="button">
|
|
68
|
+
Create New Server in Existing Category
|
|
69
|
+
</button>
|
|
70
|
+
</nav>
|
|
71
|
+
<!-- View Mode Toggle Switch -->
|
|
72
|
+
<div class="flex justify-end mt-2 mb-4">
|
|
73
|
+
<label for="viewModeToggle" class="flex items-center cursor-pointer">
|
|
74
|
+
<span class="mr-3 text-sm font-medium text-gray-700">Form View</span>
|
|
75
|
+
<div class="relative">
|
|
76
|
+
<input type="checkbox" id="viewModeToggle" class="sr-only peer">
|
|
77
|
+
<div class="w-10 h-6 bg-gray-200 rounded-full peer peer-focus:ring-2 peer-focus:ring-blue-300 peer-checked:bg-blue-600 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all"></div>
|
|
78
|
+
</div>
|
|
79
|
+
<span class="ml-3 text-sm font-medium text-gray-700">JSON View</span>
|
|
80
|
+
</label>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
<!-- Tab Panels -->
|
|
84
|
+
<div id="panel-create-category">
|
|
85
|
+
<form id="onboardForm" class="space-y-6">
|
|
86
|
+
<!-- Basic Information -->
|
|
87
|
+
<div class="space-y-4">
|
|
88
|
+
<h2 onclick="toggleSectionContent('basicInformationContent', this.querySelector('.toggle-icon'))" class="text-xl font-semibold text-gray-900 flex items-center justify-between mb-4 cursor-pointer">
|
|
89
|
+
<span class="flex items-center">
|
|
90
|
+
<i class='bx bx-info-circle mr-2 text-blue-600'></i>
|
|
91
|
+
Basic Information
|
|
92
|
+
</span>
|
|
93
|
+
<i class='bx bx-chevron-down toggle-icon text-xl text-gray-500'></i>
|
|
94
|
+
</h2>
|
|
95
|
+
<div id="basicInformationContent" class="grid grid-cols-1 md:grid-cols-2 gap-4 p-6 bg-white rounded-lg border border-gray-200 shadow-sm">
|
|
96
|
+
<div>
|
|
97
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">Category Name*</label>
|
|
98
|
+
<input type="text" name="name" required
|
|
99
|
+
class="w-full px-3 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
100
|
+
placeholder="e.g., ai-coder-tools">
|
|
101
|
+
</div>
|
|
102
|
+
<div>
|
|
103
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">Display Name*</label>
|
|
104
|
+
<input type="text" name="displayName" required
|
|
105
|
+
class="w-full px-3 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
106
|
+
placeholder="e.g., Coder Tools">
|
|
107
|
+
</div>
|
|
108
|
+
<div class="md:col-span-2">
|
|
109
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">Description</label>
|
|
110
|
+
<textarea name="description" rows="3"
|
|
111
|
+
class="w-full px-3 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
112
|
+
placeholder="Describe the purpose and capabilities of this server category"></textarea>
|
|
113
|
+
</div>
|
|
114
|
+
<div class="md:col-span-2">
|
|
115
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">Repository URL</label>
|
|
116
|
+
<input type="url" name="repository"
|
|
117
|
+
class="w-full px-3 py-1.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
118
|
+
placeholder="e.g., https://github.com/organization/repository">
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
<!-- MCP Servers -->
|
|
123
|
+
<div class="space-y-4">
|
|
124
|
+
<div class="flex justify-between items-center mb-4">
|
|
125
|
+
<h2 class="text-xl font-semibold text-gray-900 flex items-center">
|
|
126
|
+
<i class='bx bx-server mr-2 text-blue-600'></i>
|
|
127
|
+
MCP Servers
|
|
128
|
+
</h2>
|
|
129
|
+
<button type="button" onclick="addServer()"
|
|
130
|
+
class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 flex items-center text-sm">
|
|
131
|
+
<i class='bx bx-plus mr-2'></i>
|
|
132
|
+
Add MCP Server
|
|
133
|
+
</button>
|
|
134
|
+
</div>
|
|
135
|
+
<div id="serversList" class="space-y-4">
|
|
136
|
+
<!-- Server configurations will be added here dynamically by serverTemplate -->
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
<div class="flex justify-end space-x-4 pt-6 border-t">
|
|
140
|
+
<button type="button" onclick="window.location.href='index.html'"
|
|
141
|
+
class="px-6 py-2.5 border border-gray-300 rounded-lg text-gray-600 hover:bg-gray-50">
|
|
142
|
+
Cancel
|
|
143
|
+
</button>
|
|
144
|
+
<button type="button" id="validateButtonNewCategory"
|
|
145
|
+
class="px-6 py-2.5 bg-green-500 text-white rounded-lg hover:bg-green-600 flex items-center">
|
|
146
|
+
<i class='bx bx-check-shield mr-2'></i>
|
|
147
|
+
Validate
|
|
148
|
+
</button>
|
|
149
|
+
<button type="submit" id="publishButtonNewCategory"
|
|
150
|
+
class="px-6 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 flex items-center">
|
|
151
|
+
<i class='bx bxs-cloud-upload mr-2'></i>
|
|
152
|
+
Publish
|
|
153
|
+
</button>
|
|
154
|
+
</div>
|
|
155
|
+
<!-- Validation Status Panel -->
|
|
156
|
+
<div id="validationStatusPanelNewCategory" class="hidden p-6 bg-gray-50 rounded-lg border border-gray-200">
|
|
157
|
+
<h3 onclick="toggleSectionContent('validationStatusContentNewCategory', this.querySelector('.toggle-icon'))" class="cursor-pointer text-lg font-semibold text-gray-800 mb-3 flex items-center justify-between">
|
|
158
|
+
<span class="flex items-center">
|
|
159
|
+
<i class='bx bx-analyse mr-2 text-blue-600'></i>Validation Status
|
|
160
|
+
</span>
|
|
161
|
+
<i class='bx bx-chevron-down toggle-icon text-xl text-gray-500'></i>
|
|
162
|
+
</h3>
|
|
163
|
+
<div id="validationStatusContentNewCategory" class="text-sm text-gray-700 space-y-2">
|
|
164
|
+
<!-- Validation messages will be displayed here -->
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
</form>
|
|
168
|
+
</div>
|
|
169
|
+
<div id="panel-create-server" class="hidden">
|
|
170
|
+
<form id="onboardServerForm" class="space-y-6">
|
|
171
|
+
<!-- Select Existing Category -->
|
|
172
|
+
<div class="space-y-4">
|
|
173
|
+
<h2 class="text-xl font-semibold text-gray-900 flex items-center mb-4">
|
|
174
|
+
<i class='bx bx-category mr-2 text-blue-600'></i>
|
|
175
|
+
Select Server Category
|
|
176
|
+
</h2>
|
|
177
|
+
<select id="existingCategorySelect" name="categoryName" required
|
|
178
|
+
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
179
|
+
<option value="">Loading categories...</option>
|
|
180
|
+
</select>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<!-- Basic Information (Read-only after category selection) -->
|
|
184
|
+
<div id="existingCategoryBasicInfoContainer" class="hidden space-y-4">
|
|
185
|
+
<h2 onclick="toggleSectionContent('existingCategoryBasicInformationContent', this.querySelector('.toggle-icon'))" class="text-xl font-semibold text-gray-900 flex items-center justify-between mb-4 cursor-pointer">
|
|
186
|
+
<span class="flex items-center">
|
|
187
|
+
<i class='bx bx-info-circle mr-2 text-blue-600'></i>
|
|
188
|
+
Basic Information (Read-only)
|
|
189
|
+
</span>
|
|
190
|
+
<i class='bx bx-chevron-down toggle-icon text-xl text-gray-500'></i>
|
|
191
|
+
</h2>
|
|
192
|
+
<div id="existingCategoryBasicInformationContent" class="grid grid-cols-1 md:grid-cols-2 gap-4 p-6 bg-white rounded-lg border border-gray-200 shadow-sm">
|
|
193
|
+
<div>
|
|
194
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">Category Name</label>
|
|
195
|
+
<input type="text" name="name" readonly class="w-full px-3 py-1.5 border border-gray-300 rounded-lg bg-gray-100 cursor-not-allowed">
|
|
196
|
+
</div>
|
|
197
|
+
<div>
|
|
198
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">Display Name</label>
|
|
199
|
+
<input type="text" name="displayName" readonly class="w-full px-3 py-1.5 border border-gray-300 rounded-lg bg-gray-100 cursor-not-allowed">
|
|
200
|
+
</div>
|
|
201
|
+
<div class="md:col-span-2">
|
|
202
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">Description</label>
|
|
203
|
+
<textarea name="description" rows="3" readonly class="w-full px-3 py-1.5 border border-gray-300 rounded-lg bg-gray-100 cursor-not-allowed"></textarea>
|
|
204
|
+
</div>
|
|
205
|
+
<div class="md:col-span-2">
|
|
206
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">Repository URL</label>
|
|
207
|
+
<input type="url" name="repository" readonly class="w-full px-3 py-1.5 border border-gray-300 rounded-lg bg-gray-100 cursor-not-allowed">
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<!-- MCP Servers (Existing read-only, new ones editable) -->
|
|
213
|
+
<div id="existingCategoryMcpServersContainer" class="hidden space-y-4">
|
|
214
|
+
<div class="flex justify-between items-center mb-4">
|
|
215
|
+
<h2 class="text-xl font-semibold text-gray-900 flex items-center">
|
|
216
|
+
<i class='bx bx-server mr-2 text-blue-600'></i>
|
|
217
|
+
MCP Servers
|
|
218
|
+
</h2>
|
|
219
|
+
<button type="button" id="addServerToExistingCategoryBtn"
|
|
220
|
+
class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 flex items-center text-sm">
|
|
221
|
+
<i class='bx bx-plus mr-2'></i>
|
|
222
|
+
Add MCP Server
|
|
223
|
+
</button>
|
|
224
|
+
</div>
|
|
225
|
+
<div id="existingCategoryServersList" class="space-y-4">
|
|
226
|
+
<!-- Server configurations will be added here: existing ones read-only, new ones editable -->
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<!-- Action Buttons -->
|
|
231
|
+
<div class="flex justify-end space-x-4 pt-6 border-t">
|
|
232
|
+
<button type="button" onclick="window.location.href='index.html'"
|
|
233
|
+
class="px-6 py-2.5 border border-gray-300 rounded-lg text-gray-600 hover:bg-gray-50">
|
|
234
|
+
Cancel
|
|
235
|
+
</button>
|
|
236
|
+
<button type="button" id="validateButtonExistingCategory"
|
|
237
|
+
class="px-6 py-2.5 bg-green-500 text-white rounded-lg hover:bg-green-600 flex items-center">
|
|
238
|
+
<i class='bx bx-check-shield mr-2'></i>
|
|
239
|
+
Validate
|
|
240
|
+
</button>
|
|
241
|
+
<button type="submit" id="publishButtonExistingCategory"
|
|
242
|
+
class="px-6 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 flex items-center">
|
|
243
|
+
<i class='bx bxs-cloud-upload mr-2'></i>
|
|
244
|
+
Publish
|
|
245
|
+
</button>
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
<!-- Validation Status Panel -->
|
|
249
|
+
<div id="validationStatusPanelExistingCategory" class="hidden p-6 bg-gray-50 rounded-lg border border-gray-200">
|
|
250
|
+
<h3 onclick="toggleSectionContent('validationStatusContentExistingCategoryTab', this.querySelector('.toggle-icon'))" class="cursor-pointer text-lg font-semibold text-gray-800 mb-3 flex items-center justify-between">
|
|
251
|
+
<span class="flex items-center">
|
|
252
|
+
<i class='bx bx-analyse mr-2 text-blue-600'></i>Validation Status
|
|
253
|
+
</span>
|
|
254
|
+
<i class='bx bx-chevron-down toggle-icon text-xl text-gray-500'></i>
|
|
255
|
+
</h3>
|
|
256
|
+
<div id="validationStatusContentExistingCategoryTab" class="text-sm text-gray-700 space-y-2">
|
|
257
|
+
<!-- Validation messages will be displayed here -->
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
</form>
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
<!-- JSON Editor Panel -->
|
|
264
|
+
<div id="panel-json-editor" class="hidden">
|
|
265
|
+
<h2 class="text-xl font-semibold text-gray-900 flex items-center mb-4">
|
|
266
|
+
<i class='bx bx-code-alt mr-2 text-blue-600'></i>
|
|
267
|
+
Edit Configuration as JSON
|
|
268
|
+
</h2>
|
|
269
|
+
<div class="relative">
|
|
270
|
+
<button type="button" onclick="copyJsonToClipboard()" title="Copy JSON to clipboard"
|
|
271
|
+
class="absolute top-2 right-2 z-10 px-3 py-1.5 text-xs bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-md flex items-center">
|
|
272
|
+
<i class='bx bx-copy mr-1'></i> Copy
|
|
273
|
+
</button>
|
|
274
|
+
<textarea id="jsonEditorTextarea" rows="20"
|
|
275
|
+
class="w-full px-3 py-2 font-mono text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
276
|
+
placeholder="JSON configuration will appear here..."></textarea>
|
|
277
|
+
</div>
|
|
278
|
+
<div id="jsonEditorActionsContainer" class="flex justify-end space-x-4 pt-6 border-t mt-6">
|
|
279
|
+
<!-- "Apply JSON to Form" button removed -->
|
|
280
|
+
<button type="button" onclick="window.location.href='index.html'"
|
|
281
|
+
class="px-6 py-2.5 border border-gray-300 rounded-lg text-gray-600 hover:bg-gray-50">
|
|
282
|
+
Cancel
|
|
283
|
+
</button>
|
|
284
|
+
<button type="button" id="saveJsonButton"
|
|
285
|
+
class="px-6 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 flex items-center">
|
|
286
|
+
<i class='bx bxs-cloud-upload mr-2'></i>
|
|
287
|
+
Publish JSON
|
|
288
|
+
</button>
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<script src="js/onboard/index.js" type="module"></script>
|
|
294
|
+
<!-- Highlight.js script can be removed if not using <pre><code> for JSON -->
|
|
295
|
+
</body>
|
|
296
|
+
</html>
|
|
@@ -152,4 +152,94 @@ body {
|
|
|
152
152
|
outline: none;
|
|
153
153
|
border-color: #2563eb;
|
|
154
154
|
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
|
155
|
-
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Onboarding form styles */
|
|
158
|
+
|
|
159
|
+
/* Form field styles */
|
|
160
|
+
input[type="text"],
|
|
161
|
+
input[type="url"],
|
|
162
|
+
select,
|
|
163
|
+
textarea {
|
|
164
|
+
transition: all 0.2s ease;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
input[type="text"]:focus,
|
|
168
|
+
input[type="url"]:focus,
|
|
169
|
+
select:focus,
|
|
170
|
+
textarea:focus {
|
|
171
|
+
outline: none;
|
|
172
|
+
border-color: #2563eb;
|
|
173
|
+
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* Radio button styles */
|
|
177
|
+
input[type="radio"] {
|
|
178
|
+
width: 1rem;
|
|
179
|
+
height: 1rem;
|
|
180
|
+
border: 2px solid #d1d5db;
|
|
181
|
+
border-radius: 50%;
|
|
182
|
+
transition: all 0.2s ease;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
input[type="radio"]:checked {
|
|
186
|
+
border-color: #2563eb;
|
|
187
|
+
background-color: #2563eb;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* Remove button styles */
|
|
191
|
+
.remove-button {
|
|
192
|
+
color: #dc2626;
|
|
193
|
+
transition: all 0.2s ease;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.remove-button:hover {
|
|
197
|
+
color: #b91c1c;
|
|
198
|
+
background-color: #fee2e2;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Add button styles */
|
|
202
|
+
.add-button {
|
|
203
|
+
background-color: #f3f4f6;
|
|
204
|
+
transition: all 0.2s ease;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.add-button:hover {
|
|
208
|
+
background-color: #e5e7eb;
|
|
209
|
+
transform: translateY(-1px);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Form section headings */
|
|
213
|
+
.section-heading {
|
|
214
|
+
color: #1f2937;
|
|
215
|
+
font-size: 1.25rem;
|
|
216
|
+
font-weight: 600;
|
|
217
|
+
margin-bottom: 1rem;
|
|
218
|
+
display: flex;
|
|
219
|
+
align-items: center;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.section-heading i {
|
|
223
|
+
color: #2563eb;
|
|
224
|
+
margin-right: 0.5rem;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* Required field indicator */
|
|
228
|
+
.required-field::after {
|
|
229
|
+
content: "*";
|
|
230
|
+
color: #dc2626;
|
|
231
|
+
margin-left: 0.25rem;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* Form grid layouts */
|
|
235
|
+
.form-grid {
|
|
236
|
+
display: grid;
|
|
237
|
+
gap: 1rem;
|
|
238
|
+
margin-bottom: 1.5rem;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@media (min-width: 768px) {
|
|
242
|
+
.form-grid {
|
|
243
|
+
grid-template-columns: repeat(2, 1fr);
|
|
244
|
+
}
|
|
245
|
+
}
|
package/src/web/server.ts
CHANGED
|
@@ -2,14 +2,32 @@ import express, { Request, Response } from 'express';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { ServerSchema } from '../core/ServerSchemaProvider.js';
|
|
5
|
-
|
|
5
|
+
import {
|
|
6
|
+
RequirementConfig,
|
|
7
|
+
FeedConfiguration,
|
|
8
|
+
MCPServerCategory,
|
|
9
|
+
McpConfig,
|
|
10
|
+
EnvVariableConfig
|
|
11
|
+
} from '../core/types.js';
|
|
12
|
+
import {
|
|
13
|
+
ApiResponse,
|
|
14
|
+
ListQueryParams,
|
|
15
|
+
InstallServersRequestBody,
|
|
16
|
+
UninstallServersRequestBody,
|
|
17
|
+
OnboardRequestBody,
|
|
18
|
+
OnboardServerConfig,
|
|
19
|
+
OnboardRequirementConfig,
|
|
20
|
+
RequirementType
|
|
21
|
+
} from './contract/serverContract.js';
|
|
22
|
+
|
|
23
|
+
import { OperationStatus, OnboardingProcessStatus } from '../core/onboard/OnboardStatus.js';
|
|
6
24
|
import { SUPPORTED_CLIENT_NAMES } from '../core/constants.js';
|
|
7
25
|
import { serverService } from '../services/ServerService.js';
|
|
26
|
+
import { feedOnboardService } from '../core/onboard/FeedOnboardService.js';
|
|
8
27
|
import { openBrowser } from '../utils/osUtils.js';
|
|
9
|
-
import { ServerInstallOptions } from '../core/types.js';
|
|
10
28
|
import { Logger } from '../utils/logger.js';
|
|
11
29
|
import { configProvider } from '../core/ConfigurationProvider.js';
|
|
12
|
-
|
|
30
|
+
import { onboardStatusManager } from '../core/onboard/OnboardStatusManager.js';
|
|
13
31
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
32
|
const app = express();
|
|
15
33
|
|
|
@@ -17,37 +35,6 @@ const app = express();
|
|
|
17
35
|
app.use('/', express.static(path.join(__dirname, '..', '..', 'src', 'web', 'public')));
|
|
18
36
|
app.use(express.json());
|
|
19
37
|
|
|
20
|
-
// API Routes
|
|
21
|
-
interface ListQueryParams {
|
|
22
|
-
local?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface UpdateRequirementsRequestBody {
|
|
26
|
-
requirements: {
|
|
27
|
-
name: string;
|
|
28
|
-
updateVersion: string;
|
|
29
|
-
}[];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface InstallServersRequestBody {
|
|
33
|
-
serverList: Record<string, ServerInstallOptions>;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface UninstallServersRequestBody {
|
|
37
|
-
serverList: Record<string, { removeData?: boolean }>;
|
|
38
|
-
options: {
|
|
39
|
-
targets: string[];
|
|
40
|
-
removeData?: boolean;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
interface ApiResponse<T> {
|
|
45
|
-
success: boolean;
|
|
46
|
-
data?: T;
|
|
47
|
-
error?: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
38
|
// Get available targets
|
|
52
39
|
app.get('/api/targets', async (req: Request, res: Response) => {
|
|
53
40
|
try {
|
|
@@ -92,35 +79,34 @@ app.get('/api/categories', async (req: Request<{}, {}, {}, ListQueryParams>, res
|
|
|
92
79
|
error: message
|
|
93
80
|
});
|
|
94
81
|
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Get server schema
|
|
85
|
+
app.get('/api/categories/:categoryName/servers/:serverName/schema', async (req: Request<{ categoryName: string; serverName: string }>, res: Response) => {
|
|
86
|
+
try {
|
|
87
|
+
const { categoryName, serverName } = req.params;
|
|
88
|
+
const schema = await serverService.getServerSchema(categoryName, serverName);
|
|
95
89
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
const { categoryName, serverName } = req.params;
|
|
100
|
-
const schema = await serverService.getServerSchema(categoryName, serverName);
|
|
101
|
-
|
|
102
|
-
if (!schema) {
|
|
103
|
-
return res.status(404).json({
|
|
104
|
-
success: false,
|
|
105
|
-
error: `Schema not found for server ${serverName} in category ${categoryName}`
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const response: ApiResponse<ServerSchema> = {
|
|
110
|
-
success: true,
|
|
111
|
-
data: schema
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
res.json(response);
|
|
115
|
-
} catch (error) {
|
|
116
|
-
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
117
|
-
res.status(500).json({
|
|
90
|
+
if (!schema) {
|
|
91
|
+
return res.status(404).json({
|
|
118
92
|
success: false,
|
|
119
|
-
error: `
|
|
93
|
+
error: `Schema not found for server ${serverName} in category ${categoryName}`
|
|
120
94
|
});
|
|
121
95
|
}
|
|
122
|
-
});
|
|
123
96
|
|
|
97
|
+
const response: ApiResponse<ServerSchema> = {
|
|
98
|
+
success: true,
|
|
99
|
+
data: schema
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
res.json(response);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
105
|
+
res.status(500).json({
|
|
106
|
+
success: false,
|
|
107
|
+
error: `Failed to get schema for server ${req.params.serverName} in category ${req.params.categoryName}: ${message}`
|
|
108
|
+
});
|
|
109
|
+
}
|
|
124
110
|
});
|
|
125
111
|
|
|
126
112
|
// Get categories data (including feed configuration)
|
|
@@ -150,6 +136,8 @@ app.get('/api/categories/:categoryName', async (req: Request<{ categoryName: str
|
|
|
150
136
|
}
|
|
151
137
|
});
|
|
152
138
|
|
|
139
|
+
// Handle server category onboarding
|
|
140
|
+
|
|
153
141
|
// Install servers for a category
|
|
154
142
|
app.post('/api/categories/:categoryName/install', async (req: Request<{ categoryName: string }, {}, InstallServersRequestBody>, res: Response) => {
|
|
155
143
|
try {
|
|
@@ -182,8 +170,128 @@ app.post('/api/categories/:categoryName/install', async (req: Request<{ category
|
|
|
182
170
|
error: `Failed to install server for ${req.params.categoryName}: ${message}`
|
|
183
171
|
});
|
|
184
172
|
}
|
|
173
|
+
|
|
174
|
+
|
|
185
175
|
});
|
|
186
176
|
|
|
177
|
+
// Handle server category onboarding
|
|
178
|
+
app.post('/api/categories/onboard', async (req: Request<{}, {}, OnboardRequestBody>, res: Response) => {
|
|
179
|
+
try {
|
|
180
|
+
const { categoryData, forExistingCategory } = req.body; // Extract forExistingCategory from request body
|
|
181
|
+
|
|
182
|
+
// Basic validation for categoryData presence and essential fields for this endpoint
|
|
183
|
+
if (!categoryData || !categoryData.name || !categoryData.displayName) {
|
|
184
|
+
return res.status(400).json({
|
|
185
|
+
success: false,
|
|
186
|
+
error: 'Category data, including name and display name, is required.'
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// categoryData is now expected to be a FeedConfiguration object directly from the client
|
|
191
|
+
const feedConfiguration: FeedConfiguration = categoryData as FeedConfiguration;
|
|
192
|
+
|
|
193
|
+
// Structural validation is now primarily handled by FeedOnboardService.validateStaticConfig
|
|
194
|
+
// The service will throw an error if the structure is invalid, which will be caught by the catch block below.
|
|
195
|
+
|
|
196
|
+
const operationResult = await feedOnboardService.onboardFeed(feedConfiguration, forExistingCategory);
|
|
197
|
+
|
|
198
|
+
// The response now directly reflects the OperationStatus returned by the service.
|
|
199
|
+
// The client will use data.onboardingId (which is the categoryName) to poll for status updates.
|
|
200
|
+
const response: ApiResponse<typeof operationResult> = {
|
|
201
|
+
success: operationResult.status !== OnboardingProcessStatus.FAILED, // Or a more nuanced success check
|
|
202
|
+
// Ensure data.onboardingId is the categoryName for polling
|
|
203
|
+
data: { ...operationResult, onboardingId: feedConfiguration.name }
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
res.status(operationResult.status === OnboardingProcessStatus.FAILED ? 500 : 200).json(response);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
209
|
+
res.status(500).json({
|
|
210
|
+
success: false,
|
|
211
|
+
error: `Failed to ${req.body.isUpdate ? 'update' : 'create'} server category: ${message}`
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Validate feed configuration
|
|
217
|
+
app.post('/api/categories/onboard/validate', async (req: Request<{}, {}, OnboardRequestBody>, res: Response) => {
|
|
218
|
+
try {
|
|
219
|
+
const { categoryData, forExistingCategory } = req.body;
|
|
220
|
+
|
|
221
|
+
if (!categoryData || !categoryData.name) {
|
|
222
|
+
return res.status(400).json({
|
|
223
|
+
success: false,
|
|
224
|
+
error: 'Category name is required in categoryData for validation.'
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// categoryData is now expected to be a FeedConfiguration object directly from the client
|
|
229
|
+
const feedConfigurationToValidate: FeedConfiguration = categoryData as FeedConfiguration;
|
|
230
|
+
|
|
231
|
+
// Structural validation is now primarily handled by FeedOnboardService.validateStaticConfig
|
|
232
|
+
// The service will throw an error if the structure is invalid, which will be caught by the catch block below.
|
|
233
|
+
|
|
234
|
+
// Call the service, which now returns OperationStatus & { feedConfiguration? }
|
|
235
|
+
// serverName parameter has been removed from validateFeed
|
|
236
|
+
const validationOperationResult = await feedOnboardService.validateFeed(feedConfigurationToValidate, forExistingCategory);
|
|
237
|
+
|
|
238
|
+
// The response includes the operation status and the feed configuration that was validated.
|
|
239
|
+
// The client will use data.onboardingId (which is the categoryName) to poll for status updates.
|
|
240
|
+
const response: ApiResponse<typeof validationOperationResult> = {
|
|
241
|
+
success: validationOperationResult.status !== OnboardingProcessStatus.FAILED,
|
|
242
|
+
// Ensure data.onboardingId is the categoryName for polling
|
|
243
|
+
data: { ...validationOperationResult, onboardingId: feedConfigurationToValidate.name }
|
|
244
|
+
};
|
|
245
|
+
res.status(validationOperationResult.status === OnboardingProcessStatus.FAILED ? 500 : 200).json(response);
|
|
246
|
+
} catch (error) {
|
|
247
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
248
|
+
res.status(500).json({
|
|
249
|
+
success: false,
|
|
250
|
+
error: `Failed to validate server category: ${message}`
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Get category onboarding/validation operation status
|
|
256
|
+
app.get('/api/categories/:categoryName/onboard/status', async (req: Request<{ categoryName: string }>, res: Response) => {
|
|
257
|
+
try {
|
|
258
|
+
const { categoryName } = req.params;
|
|
259
|
+
// categoryName is now the identifier for the operation status
|
|
260
|
+
const status = await onboardStatusManager.getStatus(categoryName);
|
|
261
|
+
|
|
262
|
+
if (!status) {
|
|
263
|
+
return res.status(404).json({
|
|
264
|
+
success: false,
|
|
265
|
+
error: `No active operation found for category ${categoryName}`
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Construct the response data based on the retrieved OnboardStatus
|
|
270
|
+
const responseData: OperationStatus = {
|
|
271
|
+
onboardingId: status.onboardingId, // This will be the categoryName
|
|
272
|
+
status: status.status,
|
|
273
|
+
message: status.currentStep || status.errorMessage || 'Processing...',
|
|
274
|
+
lastQueried: new Date().toISOString(),
|
|
275
|
+
...(status.validationStatus && { validationStatus: status.validationStatus }),
|
|
276
|
+
...(status.prInfo && { prInfo: status.prInfo }),
|
|
277
|
+
...(status.result && { result: status.result }),
|
|
278
|
+
...(status.errorMessage && { errorMessage: status.errorMessage }),
|
|
279
|
+
...(status.operationType && { operationType: status.operationType }),
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const response: ApiResponse<OperationStatus> = {
|
|
283
|
+
success: true,
|
|
284
|
+
data: responseData
|
|
285
|
+
};
|
|
286
|
+
res.json(response);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
289
|
+
res.status(500).json({
|
|
290
|
+
success: false,
|
|
291
|
+
error: `Failed to get operation status for category ${req.params.categoryName}: ${message}`
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
});
|
|
187
295
|
// Uninstall tools from a server
|
|
188
296
|
app.post('/api/categories/:categoryName/uninstall', async (req: Request<{ categoryName: string }, {}, UninstallServersRequestBody>, res: Response) => {
|
|
189
297
|
try {
|
|
@@ -232,6 +340,7 @@ app.post('/api/categories/:categoryName/uninstall', async (req: Request<{ catego
|
|
|
232
340
|
}
|
|
233
341
|
});
|
|
234
342
|
|
|
343
|
+
|
|
235
344
|
export async function startWebServer(port = 3000): Promise<void> {
|
|
236
345
|
return new Promise((resolve, reject) => {
|
|
237
346
|
const server = app.listen(port, () => {
|