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.
Files changed (136) hide show
  1. package/dist/core/ConfigurationProvider.d.ts +1 -0
  2. package/dist/core/ConfigurationProvider.js +15 -0
  3. package/dist/core/InstallationService.js +2 -7
  4. package/dist/core/MCPManager.d.ts +11 -2
  5. package/dist/core/MCPManager.js +24 -1
  6. package/dist/core/RequirementService.js +2 -8
  7. package/dist/core/installers/clients/BaseClientInstaller.d.ts +51 -0
  8. package/dist/core/installers/clients/BaseClientInstaller.js +160 -0
  9. package/dist/core/installers/clients/ClientInstaller.d.ts +16 -9
  10. package/dist/core/installers/clients/ClientInstaller.js +80 -527
  11. package/dist/core/installers/clients/ClientInstallerFactory.d.ts +20 -0
  12. package/dist/core/installers/clients/ClientInstallerFactory.js +37 -0
  13. package/dist/core/installers/clients/ClineInstaller.d.ts +18 -0
  14. package/dist/core/installers/clients/ClineInstaller.js +124 -0
  15. package/dist/core/installers/clients/GithubCopilotInstaller.d.ts +34 -0
  16. package/dist/core/installers/clients/GithubCopilotInstaller.js +162 -0
  17. package/dist/core/installers/clients/MSRooCodeInstaller.d.ts +15 -0
  18. package/dist/core/installers/clients/MSRooCodeInstaller.js +122 -0
  19. package/dist/core/installers/requirements/BaseInstaller.d.ts +11 -34
  20. package/dist/core/installers/requirements/BaseInstaller.js +5 -116
  21. package/dist/core/installers/requirements/CommandInstaller.d.ts +6 -1
  22. package/dist/core/installers/requirements/CommandInstaller.js +7 -0
  23. package/dist/core/installers/requirements/GeneralInstaller.d.ts +6 -1
  24. package/dist/core/installers/requirements/GeneralInstaller.js +9 -4
  25. package/dist/core/installers/requirements/NpmInstaller.d.ts +46 -7
  26. package/dist/core/installers/requirements/NpmInstaller.js +150 -58
  27. package/dist/core/installers/requirements/PipInstaller.d.ts +9 -0
  28. package/dist/core/installers/requirements/PipInstaller.js +66 -28
  29. package/dist/core/onboard/FeedOnboardService.d.ts +50 -13
  30. package/dist/core/onboard/FeedOnboardService.js +263 -88
  31. package/dist/core/onboard/OnboardProcessor.d.ts +79 -0
  32. package/dist/core/onboard/OnboardProcessor.js +290 -0
  33. package/dist/core/onboard/OnboardStatus.d.ts +49 -0
  34. package/dist/core/onboard/OnboardStatus.js +10 -0
  35. package/dist/core/onboard/OnboardStatusManager.d.ts +57 -0
  36. package/dist/core/onboard/OnboardStatusManager.js +176 -0
  37. package/dist/core/types.d.ts +4 -5
  38. package/dist/core/validators/FeedValidator.d.ts +8 -1
  39. package/dist/core/validators/FeedValidator.js +60 -7
  40. package/dist/core/validators/IServerValidator.d.ts +19 -0
  41. package/dist/core/validators/IServerValidator.js +2 -0
  42. package/dist/core/validators/SSEServerValidator.d.ts +15 -0
  43. package/dist/core/validators/SSEServerValidator.js +39 -0
  44. package/dist/core/validators/ServerValidatorFactory.d.ts +24 -0
  45. package/dist/core/validators/ServerValidatorFactory.js +45 -0
  46. package/dist/core/validators/StdioServerValidator.d.ts +46 -0
  47. package/dist/core/validators/StdioServerValidator.js +229 -0
  48. package/dist/services/InstallRequestValidator.d.ts +1 -1
  49. package/dist/services/ServerService.d.ts +9 -6
  50. package/dist/services/ServerService.js +18 -7
  51. package/dist/utils/adoUtils.d.ts +29 -0
  52. package/dist/utils/adoUtils.js +252 -0
  53. package/dist/utils/clientUtils.d.ts +0 -7
  54. package/dist/utils/clientUtils.js +0 -42
  55. package/dist/utils/githubUtils.d.ts +10 -0
  56. package/dist/utils/githubUtils.js +22 -0
  57. package/dist/utils/macroExpressionUtils.d.ts +38 -0
  58. package/dist/utils/macroExpressionUtils.js +116 -0
  59. package/dist/utils/osUtils.d.ts +4 -20
  60. package/dist/utils/osUtils.js +78 -23
  61. package/dist/web/contract/serverContract.d.ts +3 -1
  62. package/dist/web/public/css/notifications.css +48 -17
  63. package/dist/web/public/css/onboard.css +66 -3
  64. package/dist/web/public/index.html +84 -16
  65. package/dist/web/public/js/api.js +3 -6
  66. package/dist/web/public/js/flights/flights.js +127 -0
  67. package/dist/web/public/js/modal/installation.js +5 -5
  68. package/dist/web/public/js/modal/modalSetup.js +3 -2
  69. package/dist/web/public/js/notifications.js +66 -27
  70. package/dist/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
  71. package/dist/web/public/js/onboard/formProcessor.js +810 -255
  72. package/dist/web/public/js/onboard/index.js +328 -85
  73. package/dist/web/public/js/onboard/publishHandler.js +132 -0
  74. package/dist/web/public/js/onboard/state.js +61 -17
  75. package/dist/web/public/js/onboard/templates.js +217 -249
  76. package/dist/web/public/js/onboard/uiHandlers.js +679 -117
  77. package/dist/web/public/js/onboard/validationHandlers.js +378 -0
  78. package/dist/web/public/js/serverCategoryList.js +15 -2
  79. package/dist/web/public/onboard.html +191 -45
  80. package/dist/web/public/styles.css +91 -1
  81. package/dist/web/server.d.ts +0 -10
  82. package/dist/web/server.js +131 -22
  83. package/package.json +2 -2
  84. package/src/core/ConfigurationProvider.ts +15 -0
  85. package/src/core/InstallationService.ts +2 -7
  86. package/src/core/MCPManager.ts +26 -1
  87. package/src/core/RequirementService.ts +2 -9
  88. package/src/core/installers/clients/BaseClientInstaller.ts +196 -0
  89. package/src/core/installers/clients/ClientInstaller.ts +97 -608
  90. package/src/core/installers/clients/ClientInstallerFactory.ts +43 -0
  91. package/src/core/installers/clients/ClineInstaller.ts +135 -0
  92. package/src/core/installers/clients/GithubCopilotInstaller.ts +179 -0
  93. package/src/core/installers/clients/MSRooCodeInstaller.ts +133 -0
  94. package/src/core/installers/requirements/BaseInstaller.ts +13 -136
  95. package/src/core/installers/requirements/CommandInstaller.ts +9 -1
  96. package/src/core/installers/requirements/GeneralInstaller.ts +11 -4
  97. package/src/core/installers/requirements/NpmInstaller.ts +178 -61
  98. package/src/core/installers/requirements/PipInstaller.ts +68 -29
  99. package/src/core/onboard/FeedOnboardService.ts +346 -0
  100. package/src/core/onboard/OnboardProcessor.ts +305 -0
  101. package/src/core/onboard/OnboardStatus.ts +55 -0
  102. package/src/core/onboard/OnboardStatusManager.ts +188 -0
  103. package/src/core/types.ts +4 -5
  104. package/src/core/validators/FeedValidator.ts +79 -0
  105. package/src/core/validators/IServerValidator.ts +21 -0
  106. package/src/core/validators/SSEServerValidator.ts +43 -0
  107. package/src/core/validators/ServerValidatorFactory.ts +51 -0
  108. package/src/core/validators/StdioServerValidator.ts +259 -0
  109. package/src/services/InstallRequestValidator.ts +1 -1
  110. package/src/services/ServerService.ts +22 -7
  111. package/src/utils/adoUtils.ts +291 -0
  112. package/src/utils/clientUtils.ts +0 -44
  113. package/src/utils/githubUtils.ts +24 -0
  114. package/src/utils/macroExpressionUtils.ts +121 -0
  115. package/src/utils/osUtils.ts +89 -24
  116. package/src/web/contract/serverContract.ts +74 -0
  117. package/src/web/public/css/notifications.css +48 -17
  118. package/src/web/public/css/onboard.css +107 -0
  119. package/src/web/public/index.html +84 -16
  120. package/src/web/public/js/api.js +3 -6
  121. package/src/web/public/js/flights/flights.js +127 -0
  122. package/src/web/public/js/modal/installation.js +5 -5
  123. package/src/web/public/js/modal/modalSetup.js +3 -2
  124. package/src/web/public/js/notifications.js +66 -27
  125. package/src/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +338 -0
  126. package/src/web/public/js/onboard/formProcessor.js +864 -0
  127. package/src/web/public/js/onboard/index.js +374 -0
  128. package/src/web/public/js/onboard/publishHandler.js +132 -0
  129. package/src/web/public/js/onboard/state.js +76 -0
  130. package/src/web/public/js/onboard/templates.js +343 -0
  131. package/src/web/public/js/onboard/uiHandlers.js +758 -0
  132. package/src/web/public/js/onboard/validationHandlers.js +378 -0
  133. package/src/web/public/js/serverCategoryList.js +15 -2
  134. package/src/web/public/onboard.html +296 -0
  135. package/src/web/public/styles.css +91 -1
  136. 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
- // Get server schema
97
- app.get('/api/categories/:categoryName/servers/:serverName/schema', async (req: Request<{ categoryName: string; serverName: string }>, res: Response) => {
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: `Failed to get schema for server ${req.params.serverName} in category ${req.params.categoryName}: ${message}`
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, () => {