imcp 0.1.6 → 0.1.8-dev

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 (314) hide show
  1. package/dist/cli/commands/install.js +1 -106
  2. package/dist/cli/commands/install.js.map +1 -0
  3. package/dist/cli/commands/list.js +1 -90
  4. package/dist/cli/commands/list.js.map +1 -0
  5. package/dist/cli/commands/pull.js +1 -16
  6. package/dist/cli/commands/pull.js.map +1 -0
  7. package/dist/cli/commands/serve.js +1 -33
  8. package/dist/cli/commands/serve.js.map +1 -0
  9. package/dist/cli/commands/uninstall.js +1 -46
  10. package/dist/cli/commands/uninstall.js.map +1 -0
  11. package/dist/cli/index.js +1 -65
  12. package/dist/cli/index.js.map +1 -0
  13. package/dist/core/installers/clients/BaseClientInstaller.js +1 -282
  14. package/dist/core/installers/clients/BaseClientInstaller.js.map +1 -0
  15. package/dist/core/installers/clients/ClientInstaller.js +1 -163
  16. package/dist/core/installers/clients/ClientInstaller.js.map +1 -0
  17. package/dist/core/installers/clients/ClientInstallerFactory.js +1 -36
  18. package/dist/core/installers/clients/ClientInstallerFactory.js.map +1 -0
  19. package/dist/core/installers/clients/ClineInstaller.js +1 -30
  20. package/dist/core/installers/clients/ClineInstaller.js.map +1 -0
  21. package/dist/core/installers/clients/ExtensionInstaller.js +1 -151
  22. package/dist/core/installers/clients/ExtensionInstaller.js.map +1 -0
  23. package/dist/core/installers/clients/GithubCopilotInstaller.js +1 -68
  24. package/dist/core/installers/clients/GithubCopilotInstaller.js.map +1 -0
  25. package/dist/core/installers/clients/MSRooCodeInstaller.js +1 -28
  26. package/dist/core/installers/clients/MSRooCodeInstaller.js.map +1 -0
  27. package/dist/core/installers/index.js +1 -8
  28. package/dist/core/installers/index.js.map +1 -0
  29. package/dist/core/installers/requirements/BaseInstaller.js +1 -56
  30. package/dist/core/installers/requirements/BaseInstaller.js.map +1 -0
  31. package/dist/core/installers/requirements/CommandInstaller.js +1 -213
  32. package/dist/core/installers/requirements/CommandInstaller.js.map +1 -0
  33. package/dist/core/installers/requirements/GeneralInstaller.js +1 -126
  34. package/dist/core/installers/requirements/GeneralInstaller.js.map +1 -0
  35. package/dist/core/installers/requirements/InstallerFactory.js +1 -99
  36. package/dist/core/installers/requirements/InstallerFactory.js.map +1 -0
  37. package/dist/core/installers/requirements/NpmInstaller.js +1 -235
  38. package/dist/core/installers/requirements/NpmInstaller.js.map +1 -0
  39. package/dist/core/installers/requirements/NugetInstaller.js +1 -188
  40. package/dist/core/installers/requirements/NugetInstaller.js.map +1 -0
  41. package/dist/core/installers/requirements/PipInstaller.js +1 -192
  42. package/dist/core/installers/requirements/PipInstaller.js.map +1 -0
  43. package/dist/core/installers/requirements/RequirementInstaller.js +1 -2
  44. package/dist/core/installers/requirements/RequirementInstaller.js.map +1 -0
  45. package/dist/core/loaders/ConfigurationLoader.js +1 -256
  46. package/dist/core/loaders/ConfigurationLoader.js.map +1 -0
  47. package/dist/core/loaders/ConfigurationProvider.js +1 -383
  48. package/dist/core/loaders/ConfigurationProvider.js.map +1 -0
  49. package/dist/core/loaders/InstallOperationManager.js +1 -310
  50. package/dist/core/loaders/InstallOperationManager.js.map +1 -0
  51. package/dist/core/loaders/ServerSchemaLoader.js +1 -108
  52. package/dist/core/loaders/ServerSchemaLoader.js.map +1 -0
  53. package/dist/core/loaders/ServerSchemaProvider.js +1 -89
  54. package/dist/core/loaders/ServerSchemaProvider.js.map +1 -0
  55. package/dist/core/loaders/SystemSettingsManager.js +1 -256
  56. package/dist/core/loaders/SystemSettingsManager.js.map +1 -0
  57. package/dist/core/metadatas/constants.js +1 -100
  58. package/dist/core/metadatas/constants.js.map +1 -0
  59. package/dist/core/metadatas/recordingConstants.js +1 -46
  60. package/dist/core/metadatas/recordingConstants.js.map +1 -0
  61. package/dist/core/metadatas/types.js +1 -15
  62. package/dist/core/metadatas/types.js.map +1 -0
  63. package/dist/core/onboard/FeedOnboardService.js +1 -422
  64. package/dist/core/onboard/FeedOnboardService.js.map +1 -0
  65. package/dist/core/onboard/OnboardProcessor.js +1 -333
  66. package/dist/core/onboard/OnboardProcessor.js.map +1 -0
  67. package/dist/core/onboard/OnboardStatus.js +1 -9
  68. package/dist/core/onboard/OnboardStatus.js.map +1 -0
  69. package/dist/core/onboard/OnboardStatusManager.js +1 -360
  70. package/dist/core/onboard/OnboardStatusManager.js.map +1 -0
  71. package/dist/core/validators/FeedValidator.js +1 -133
  72. package/dist/core/validators/FeedValidator.js.map +1 -0
  73. package/dist/core/validators/IServerValidator.js +1 -1
  74. package/dist/core/validators/IServerValidator.js.map +1 -0
  75. package/dist/core/validators/SSEServerValidator.js +1 -38
  76. package/dist/core/validators/SSEServerValidator.js.map +1 -0
  77. package/dist/core/validators/ServerValidatorFactory.js +1 -44
  78. package/dist/core/validators/ServerValidatorFactory.js.map +1 -0
  79. package/dist/core/validators/StdioServerValidator.js +1 -281
  80. package/dist/core/validators/StdioServerValidator.js.map +1 -0
  81. package/dist/index.js +1 -18
  82. package/dist/index.js.map +1 -0
  83. package/dist/services/InstallationService.js +1 -81
  84. package/dist/services/InstallationService.js.map +1 -0
  85. package/dist/services/MCPManager.js +1 -197
  86. package/dist/services/MCPManager.js.map +1 -0
  87. package/dist/services/RequirementService.js +1 -548
  88. package/dist/services/RequirementService.js.map +1 -0
  89. package/dist/services/ServerService.js +1 -127
  90. package/dist/services/ServerService.js.map +1 -0
  91. package/dist/services/TelemetryService.js +1 -53
  92. package/dist/services/TelemetryService.js.map +1 -0
  93. package/dist/utils/UpdateCheckTracker.js +1 -79
  94. package/dist/utils/UpdateCheckTracker.js.map +1 -0
  95. package/dist/utils/adoUtils.js +1 -254
  96. package/dist/utils/adoUtils.js.map +1 -0
  97. package/dist/utils/clientUtils.js +1 -65
  98. package/dist/utils/clientUtils.js.map +1 -0
  99. package/dist/utils/feedUtils.js +1 -28
  100. package/dist/utils/feedUtils.js.map +1 -0
  101. package/dist/utils/githubAuth.js +1 -177
  102. package/dist/utils/githubAuth.js.map +1 -0
  103. package/dist/utils/githubUtils.js +1 -125
  104. package/dist/utils/githubUtils.js.map +1 -0
  105. package/dist/utils/logger.js +1 -176
  106. package/dist/utils/logger.js.map +1 -0
  107. package/dist/utils/macroExpressionUtils.js +1 -93
  108. package/dist/utils/macroExpressionUtils.js.map +1 -0
  109. package/dist/utils/osUtils.js +1 -664
  110. package/dist/utils/osUtils.js.map +1 -0
  111. package/dist/utils/versionUtils.js +1 -101
  112. package/dist/utils/versionUtils.js.map +1 -0
  113. package/dist/web/contract/serverContract.js +1 -1
  114. package/dist/web/contract/serverContract.js.map +1 -0
  115. package/dist/web/public/js/api.js +2 -132
  116. package/dist/web/public/js/api.js.map +1 -0
  117. package/dist/web/public/js/detailsWidget.js +2 -264
  118. package/dist/web/public/js/detailsWidget.js.map +1 -0
  119. package/dist/web/public/js/flights/flights.js +2 -127
  120. package/dist/web/public/js/flights/flights.js.map +1 -0
  121. package/dist/web/public/js/modal/index.js +2 -52
  122. package/dist/web/public/js/modal/index.js.map +1 -0
  123. package/dist/web/public/js/modal/installModal.js +2 -162
  124. package/dist/web/public/js/modal/installModal.js.map +1 -0
  125. package/dist/web/public/js/modal/installation.js +2 -266
  126. package/dist/web/public/js/modal/installation.js.map +1 -0
  127. package/dist/web/public/js/modal/loadingModal.js +2 -182
  128. package/dist/web/public/js/modal/loadingModal.js.map +1 -0
  129. package/dist/web/public/js/modal/modalSetup.js +2 -595
  130. package/dist/web/public/js/modal/modalSetup.js.map +1 -0
  131. package/dist/web/public/js/modal/modalUtils.js +2 -37
  132. package/dist/web/public/js/modal/modalUtils.js.map +1 -0
  133. package/dist/web/public/js/modal/versionUtils.js +2 -20
  134. package/dist/web/public/js/modal/versionUtils.js.map +1 -0
  135. package/dist/web/public/js/modal.js +2 -42
  136. package/dist/web/public/js/modal.js.map +1 -0
  137. package/dist/web/public/js/notifications.js +2 -137
  138. package/dist/web/public/js/notifications.js.map +1 -0
  139. package/dist/web/public/js/onboard/formProcessor.js +2 -1037
  140. package/dist/web/public/js/onboard/formProcessor.js.map +1 -0
  141. package/dist/web/public/js/onboard/index.js +2 -374
  142. package/dist/web/public/js/onboard/index.js.map +1 -0
  143. package/dist/web/public/js/onboard/publishHandler.js +2 -172
  144. package/dist/web/public/js/onboard/publishHandler.js.map +1 -0
  145. package/dist/web/public/js/onboard/state.js +2 -76
  146. package/dist/web/public/js/onboard/state.js.map +1 -0
  147. package/dist/web/public/js/onboard/templates.js +2 -342
  148. package/dist/web/public/js/onboard/templates.js.map +1 -0
  149. package/dist/web/public/js/onboard/uiHandlers.js +2 -1076
  150. package/dist/web/public/js/onboard/uiHandlers.js.map +1 -0
  151. package/dist/web/public/js/onboard/validationHandlers.js +2 -493
  152. package/dist/web/public/js/onboard/validationHandlers.js.map +1 -0
  153. package/dist/web/public/js/serverCategoryDetails.js +2 -364
  154. package/dist/web/public/js/serverCategoryDetails.js.map +1 -0
  155. package/dist/web/public/js/serverCategoryList.js +2 -241
  156. package/dist/web/public/js/serverCategoryList.js.map +1 -0
  157. package/dist/web/public/js/settings.js +2 -314
  158. package/dist/web/public/js/settings.js.map +1 -0
  159. package/dist/web/server.js +1 -404
  160. package/dist/web/server.js.map +1 -0
  161. package/package.json +8 -2
  162. package/.github/ISSUE_TEMPLATE/JitAccess.yml +0 -28
  163. package/.github/acl/access.yml +0 -20
  164. package/.github/compliance/inventory.yml +0 -5
  165. package/.github/policies/jit.yml +0 -19
  166. package/.github/workflows/build.yml +0 -28
  167. package/.roo/rules-code/rules.md +0 -88
  168. package/dist/cli/commands/start.d.ts +0 -2
  169. package/dist/cli/commands/start.js +0 -32
  170. package/dist/cli/commands/sync.d.ts +0 -2
  171. package/dist/cli/commands/sync.js +0 -17
  172. package/dist/core/ConfigurationLoader.d.ts +0 -32
  173. package/dist/core/ConfigurationLoader.js +0 -236
  174. package/dist/core/ConfigurationProvider.d.ts +0 -35
  175. package/dist/core/ConfigurationProvider.js +0 -375
  176. package/dist/core/InstallationService.d.ts +0 -50
  177. package/dist/core/InstallationService.js +0 -350
  178. package/dist/core/MCPManager.d.ts +0 -28
  179. package/dist/core/MCPManager.js +0 -188
  180. package/dist/core/RequirementService.d.ts +0 -40
  181. package/dist/core/RequirementService.js +0 -110
  182. package/dist/core/ServerSchemaLoader.d.ts +0 -11
  183. package/dist/core/ServerSchemaLoader.js +0 -43
  184. package/dist/core/ServerSchemaProvider.d.ts +0 -17
  185. package/dist/core/ServerSchemaProvider.js +0 -120
  186. package/dist/core/constants.d.ts +0 -47
  187. package/dist/core/constants.js +0 -94
  188. package/dist/core/installers/BaseInstaller.d.ts +0 -74
  189. package/dist/core/installers/BaseInstaller.js +0 -253
  190. package/dist/core/installers/ClientInstaller.d.ts +0 -23
  191. package/dist/core/installers/ClientInstaller.js +0 -564
  192. package/dist/core/installers/CommandInstaller.d.ts +0 -37
  193. package/dist/core/installers/CommandInstaller.js +0 -173
  194. package/dist/core/installers/GeneralInstaller.d.ts +0 -33
  195. package/dist/core/installers/GeneralInstaller.js +0 -85
  196. package/dist/core/installers/InstallerFactory.d.ts +0 -54
  197. package/dist/core/installers/InstallerFactory.js +0 -97
  198. package/dist/core/installers/NpmInstaller.d.ts +0 -26
  199. package/dist/core/installers/NpmInstaller.js +0 -127
  200. package/dist/core/installers/PipInstaller.d.ts +0 -28
  201. package/dist/core/installers/PipInstaller.js +0 -127
  202. package/dist/core/installers/RequirementInstaller.d.ts +0 -33
  203. package/dist/core/installers/RequirementInstaller.js +0 -3
  204. package/dist/core/types.d.ts +0 -166
  205. package/dist/core/types.js +0 -16
  206. package/dist/services/InstallRequestValidator.d.ts +0 -21
  207. package/dist/services/InstallRequestValidator.js +0 -99
  208. package/dist/web/public/js/modal/installHandler.js +0 -227
  209. package/dist/web/public/js/modal/loadingUI.js +0 -74
  210. package/dist/web/public/js/modal/messageQueue.js +0 -112
  211. package/dist/web/public/js/modal/modalUI.js +0 -214
  212. package/dist/web/public/js/modal/version.js +0 -20
  213. package/dist/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +0 -370
  214. package/docs/ONBOARDING_PAGE_DESIGN.md +0 -260
  215. package/docs/Telemetry.md +0 -136
  216. package/memory-bank/activeContext.md +0 -26
  217. package/memory-bank/decisionLog.md +0 -91
  218. package/memory-bank/productContext.md +0 -41
  219. package/memory-bank/progress.md +0 -35
  220. package/memory-bank/systemPatterns.md +0 -10
  221. package/src/cli/commands/install.ts +0 -139
  222. package/src/cli/commands/list.ts +0 -113
  223. package/src/cli/commands/pull.ts +0 -16
  224. package/src/cli/commands/serve.ts +0 -39
  225. package/src/cli/commands/uninstall.ts +0 -64
  226. package/src/cli/index.ts +0 -82
  227. package/src/core/installers/clients/BaseClientInstaller.ts +0 -341
  228. package/src/core/installers/clients/ClientInstaller.ts +0 -222
  229. package/src/core/installers/clients/ClientInstallerFactory.ts +0 -43
  230. package/src/core/installers/clients/ClineInstaller.ts +0 -35
  231. package/src/core/installers/clients/ExtensionInstaller.ts +0 -165
  232. package/src/core/installers/clients/GithubCopilotInstaller.ts +0 -79
  233. package/src/core/installers/clients/MSRooCodeInstaller.ts +0 -32
  234. package/src/core/installers/index.ts +0 -11
  235. package/src/core/installers/requirements/BaseInstaller.ts +0 -85
  236. package/src/core/installers/requirements/CommandInstaller.ts +0 -231
  237. package/src/core/installers/requirements/GeneralInstaller.ts +0 -133
  238. package/src/core/installers/requirements/InstallerFactory.ts +0 -114
  239. package/src/core/installers/requirements/NpmInstaller.ts +0 -271
  240. package/src/core/installers/requirements/NugetInstaller.ts +0 -203
  241. package/src/core/installers/requirements/PipInstaller.ts +0 -207
  242. package/src/core/installers/requirements/RequirementInstaller.ts +0 -42
  243. package/src/core/loaders/ConfigurationLoader.ts +0 -298
  244. package/src/core/loaders/ConfigurationProvider.ts +0 -462
  245. package/src/core/loaders/InstallOperationManager.ts +0 -367
  246. package/src/core/loaders/ServerSchemaLoader.ts +0 -117
  247. package/src/core/loaders/ServerSchemaProvider.ts +0 -99
  248. package/src/core/loaders/SystemSettingsManager.ts +0 -278
  249. package/src/core/metadatas/constants.ts +0 -122
  250. package/src/core/metadatas/recordingConstants.ts +0 -65
  251. package/src/core/metadatas/types.ts +0 -202
  252. package/src/core/onboard/FeedOnboardService.ts +0 -501
  253. package/src/core/onboard/OnboardProcessor.ts +0 -356
  254. package/src/core/onboard/OnboardStatus.ts +0 -60
  255. package/src/core/onboard/OnboardStatusManager.ts +0 -416
  256. package/src/core/validators/FeedValidator.ts +0 -135
  257. package/src/core/validators/IServerValidator.ts +0 -21
  258. package/src/core/validators/SSEServerValidator.ts +0 -43
  259. package/src/core/validators/ServerValidatorFactory.ts +0 -51
  260. package/src/core/validators/StdioServerValidator.ts +0 -313
  261. package/src/index.ts +0 -44
  262. package/src/services/InstallationService.ts +0 -102
  263. package/src/services/MCPManager.ts +0 -249
  264. package/src/services/RequirementService.ts +0 -627
  265. package/src/services/ServerService.ts +0 -161
  266. package/src/services/TelemetryService.ts +0 -59
  267. package/src/utils/UpdateCheckTracker.ts +0 -86
  268. package/src/utils/adoUtils.ts +0 -293
  269. package/src/utils/clientUtils.ts +0 -72
  270. package/src/utils/feedUtils.ts +0 -31
  271. package/src/utils/githubAuth.ts +0 -212
  272. package/src/utils/githubUtils.ts +0 -164
  273. package/src/utils/logger.ts +0 -195
  274. package/src/utils/macroExpressionUtils.ts +0 -104
  275. package/src/utils/osUtils.ts +0 -700
  276. package/src/utils/versionUtils.ts +0 -114
  277. package/src/web/contract/serverContract.ts +0 -74
  278. package/src/web/public/css/detailsWidget.css +0 -235
  279. package/src/web/public/css/modal.css +0 -757
  280. package/src/web/public/css/notifications.css +0 -101
  281. package/src/web/public/css/onboard.css +0 -107
  282. package/src/web/public/css/serverCategoryList.css +0 -120
  283. package/src/web/public/css/serverDetails.css +0 -139
  284. package/src/web/public/index.html +0 -359
  285. package/src/web/public/js/api.js +0 -132
  286. package/src/web/public/js/detailsWidget.js +0 -264
  287. package/src/web/public/js/flights/flights.js +0 -127
  288. package/src/web/public/js/modal/index.js +0 -52
  289. package/src/web/public/js/modal/installModal.js +0 -162
  290. package/src/web/public/js/modal/installation.js +0 -266
  291. package/src/web/public/js/modal/loadingModal.js +0 -182
  292. package/src/web/public/js/modal/modalSetup.js +0 -595
  293. package/src/web/public/js/modal/modalUtils.js +0 -37
  294. package/src/web/public/js/modal/versionUtils.js +0 -20
  295. package/src/web/public/js/modal.js +0 -42
  296. package/src/web/public/js/notifications.js +0 -137
  297. package/src/web/public/js/onboard/formProcessor.js +0 -1037
  298. package/src/web/public/js/onboard/index.js +0 -374
  299. package/src/web/public/js/onboard/publishHandler.js +0 -172
  300. package/src/web/public/js/onboard/state.js +0 -76
  301. package/src/web/public/js/onboard/templates.js +0 -342
  302. package/src/web/public/js/onboard/uiHandlers.js +0 -1076
  303. package/src/web/public/js/onboard/validationHandlers.js +0 -493
  304. package/src/web/public/js/serverCategoryDetails.js +0 -364
  305. package/src/web/public/js/serverCategoryList.js +0 -241
  306. package/src/web/public/js/settings.js +0 -314
  307. package/src/web/public/modal.html +0 -84
  308. package/src/web/public/onboard.html +0 -296
  309. package/src/web/public/settings.html +0 -135
  310. package/src/web/public/styles.css +0 -277
  311. package/src/web/server.ts +0 -478
  312. package/tsconfig.json +0 -18
  313. package/wiki/Installation.md +0 -3
  314. package/wiki/Publish.md +0 -3
@@ -1,1076 +0,0 @@
1
- import {
2
- serverTemplate,
3
- envVariableTemplate,
4
- serverRequirementTemplate
5
- } from './templates.js';
6
-
7
- import {
8
- state,
9
- setServerCounter,
10
- getServerCounter, // Added
11
- setEnvCounter,
12
- getEnvCounter,
13
- deleteEnvCounter,
14
- clearEnvCountersForTab, // Added
15
- setServerRequirementCounter,
16
- getServerRequirementCounter,
17
- deleteServerRequirementCounter,
18
- clearServerRequirementCountersForTab // Added
19
- } from './state.js';
20
- import { getFormData, populateForm, resetOnboardFormDynamicContent } from './formProcessor.js';
21
- import { showToast } from '../notifications.js';
22
-
23
- function reindexElements(container, oldIndex, newIndex, selectors) {
24
- selectors.forEach(({ selector, idBase, namePattern }) => {
25
- const element = container.querySelector(selector);
26
- if (!element) return;
27
-
28
- if (idBase) {
29
- element.id = `${idBase}${newIndex}`;
30
- }
31
-
32
- if (namePattern && element.getAttribute('name')) {
33
- const oldName = element.getAttribute('name');
34
- const newName = oldName.replace(
35
- new RegExp(namePattern.replace('INDEX', oldIndex)),
36
- `$1${newIndex}$2`
37
- );
38
- element.setAttribute('name', newName);
39
- }
40
- });
41
- }
42
-
43
- function reindexServers(serversListId = 'serversList') {
44
- const serversList = document.getElementById(serversListId);
45
- if (!serversList) return;
46
-
47
- const serverItems = serversList.querySelectorAll('.server-item');
48
- const newEnvCounters = new Map();
49
- const newServerRequirementCounters = new Map();
50
-
51
- serverItems.forEach((serverItem, newServerIndex) => {
52
- const oldServerIndex = parseInt(serverItem.dataset.index, 10);
53
- serverItem.dataset.index = newServerIndex;
54
-
55
- const titleElement = serverItem.querySelector('h3');
56
- if (titleElement) {
57
- const originalTextContent = titleElement.textContent || '';
58
- const baseTitle = `MCP Server #${newServerIndex + 1}`;
59
- let suffix = '';
60
- if (originalTextContent.includes('(Adhoc - Editable)')) {
61
- suffix = ' <span class="text-sm text-blue-600 ml-1">(Adhoc - Editable)</span>';
62
- } else if (originalTextContent.includes('(Read-only)')) {
63
- suffix = ' (Read-only)';
64
- }
65
- // Use innerHTML to preserve the span if it's an adhoc server
66
- titleElement.innerHTML = `${baseTitle}${suffix}`;
67
- }
68
-
69
- // Update onclick handlers
70
- // Ensure the server header toggle onclick is correctly re-indexed for its ID parameters
71
- const headerToggle = serverItem.querySelector(`#${serversListId}-server-header-${oldServerIndex}`);
72
- if (headerToggle) {
73
- headerToggle.id = `${serversListId}-server-header-${newServerIndex}`;
74
- const oldContentId = `${serversListId}-server-content-${oldServerIndex}`;
75
- const newContentId = `${serversListId}-server-content-${newServerIndex}`;
76
- let onclickAttr = headerToggle.getAttribute('onclick');
77
- if (onclickAttr) {
78
- onclickAttr = onclickAttr.replace(new RegExp(oldContentId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), newContentId);
79
- headerToggle.setAttribute('onclick', onclickAttr);
80
- }
81
- let onkeydownAttr = headerToggle.getAttribute('onkeydown');
82
- if (onkeydownAttr) {
83
- onkeydownAttr = onkeydownAttr.replace(new RegExp(oldContentId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), newContentId);
84
- headerToggle.setAttribute('onkeydown', onkeydownAttr);
85
- }
86
- headerToggle.setAttribute('aria-controls', newContentId);
87
- }
88
- // Update title ID for aria-labelledby
89
- const titleForAria = serverItem.querySelector(`#${serversListId}-server-title-${oldServerIndex}`);
90
- if (titleForAria) {
91
- titleForAria.id = `${serversListId}-server-title-${newServerIndex}`;
92
- }
93
- const contentRegion = serverItem.querySelector(`#${serversListId}-server-content-${oldServerIndex}`);
94
- if (contentRegion) {
95
- contentRegion.setAttribute('aria-labelledby', `${serversListId}-server-title-${newServerIndex}`);
96
- }
97
-
98
-
99
- serverItem.querySelectorAll('[onclick]').forEach(element => {
100
- const onclickAttr = element.getAttribute('onclick');
101
- if (!onclickAttr) return;
102
-
103
- // Skip the header toggle as it's handled above to be more precise with ID replacement
104
- if (element.id === `${serversListId}-server-header-${newServerIndex}`) return;
105
-
106
- const updatedOnclick = onclickAttr
107
- .replace(new RegExp(`\\((\\s*)${oldServerIndex}(\\s*[,\\)])`, 'g'), `($1${newServerIndex}$2`)
108
- // More specific replacement for content IDs to avoid accidental replacements
109
- .replace(new RegExp(`${serversListId}-server-deps-content-${oldServerIndex}`.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), `${serversListId}-server-deps-content-${newServerIndex}`)
110
- .replace(new RegExp(`${serversListId}-installation-content-${oldServerIndex}`.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), `${serversListId}-installation-content-${newServerIndex}`)
111
- .replace(new RegExp(`${serversListId}-env-vars-content-${oldServerIndex}`.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), `${serversListId}-env-vars-content-${newServerIndex}`);
112
-
113
- element.setAttribute('onclick', updatedOnclick);
114
- });
115
-
116
- // Update IDs and names
117
- reindexElements(serverItem, oldServerIndex, newServerIndex, [
118
- { selector: `#${serversListId}-server-content-${oldServerIndex}`, idBase: `${serversListId}-server-content-` },
119
- { selector: `#${serversListId}-server-deps-content-${oldServerIndex}`, idBase: `${serversListId}-server-deps-content-` },
120
- { selector: `#${serversListId}-installation-content-${oldServerIndex}`, idBase: `${serversListId}-installation-content-` },
121
- { selector: `#${serversListId}-env-vars-content-${oldServerIndex}`, idBase: `${serversListId}-env-vars-content-` },
122
- { selector: `#schema-path-${oldServerIndex}`, idBase: 'schema-path-' },
123
- { selector: `#envVarsContainer_${oldServerIndex}`, idBase: 'envVarsContainer_' }
124
- ]);
125
-
126
- // Reindex env variables
127
- const envVarsContainer = serverItem.querySelector(`#envVarsContainer_${newServerIndex}`);
128
- let envCounter = 0;
129
- if (envVarsContainer) {
130
- envVarsContainer.querySelectorAll('.env-var-item').forEach((envItem, newEnvIndex) => {
131
- const oldEnvIndex = parseInt(envItem.dataset.envIndex, 10);
132
- envItem.dataset.envIndex = newEnvIndex;
133
- reindexElements(envItem, oldEnvIndex, newEnvIndex, [{
134
- selector: '[name]',
135
- namePattern: `(servers\\[${newServerIndex}\\]\\.installation\\.env\\[)\\d+(\\]\\..+)`
136
- }]);
137
- envCounter++;
138
- });
139
- }
140
- newEnvCounters.set(newServerIndex, envCounter);
141
-
142
- // Reindex server requirements
143
- const reqsContainer = serverItem.querySelector(`#server-requirements-list-${newServerIndex}`);
144
- let reqCounter = 0;
145
- if (reqsContainer) {
146
- reqsContainer.querySelectorAll('.server-requirement-item').forEach((reqItem, newReqIndex) => {
147
- const oldReqIndex = parseInt(reqItem.dataset.reqIndex, 10);
148
- reqItem.dataset.reqIndex = newReqIndex;
149
- reindexElements(reqItem, oldReqIndex, newReqIndex, [{
150
- selector: '[name]',
151
- namePattern: `(servers\\[${newServerIndex}\\]\\.requirements\\[)\\d+(\\]\\..+)`
152
- }]);
153
- reqCounter++;
154
- });
155
- }
156
- newServerRequirementCounters.set(newServerIndex, reqCounter);
157
- });
158
-
159
- // Update state for the specific tab
160
- clearEnvCountersForTab(serversListId);
161
- newEnvCounters.forEach((count, serverIdx) => setEnvCounter(serversListId, serverIdx, count));
162
-
163
- clearServerRequirementCountersForTab(serversListId);
164
- newServerRequirementCounters.forEach((count, serverIdx) => setServerRequirementCounter(serversListId, serverIdx, count));
165
-
166
- setServerCounter(serversListId, serverItems.length);
167
- }
168
-
169
- export function addServer(serversListId = 'serversList', isContextGenerallyReadOnly = false, serverData = null) {
170
- const container = document.getElementById(serversListId);
171
- if (!container) return -1;
172
-
173
- // Initialize counters for this tab if it's the first server being added to this specific list
174
- if (container.children.length === 0) {
175
- setServerCounter(serversListId, 0);
176
- clearEnvCountersForTab(serversListId);
177
- clearServerRequirementCountersForTab(serversListId);
178
- }
179
-
180
- const newServerIndex = getServerCounter(serversListId);
181
- let actualReadOnlyForThisServer;
182
- const isServerAdhoc = serverData?.systemTags?.adhoc === 'true';
183
-
184
- if (isServerAdhoc) {
185
- // Adhoc servers are always editable, regardless of the general context.
186
- actualReadOnlyForThisServer = false;
187
- } else if (!isContextGenerallyReadOnly) {
188
- // Context is not generally read-only (e.g., creating a brand new category,
189
- // or user clicked "Add Server" button when no specific read-only category context applies).
190
- actualReadOnlyForThisServer = false;
191
- } else {
192
- // Context IS generally read-only AND the server is NOT adhoc.
193
- // Determine read-only status based on whether it's an original server or a newly added one.
194
- if (state.originalServerNamesForFormPopulation) {
195
- // We are in the process of toggling from JSON view back to Form view for an existing category.
196
- // state.originalServerNamesForFormPopulation contains names of servers that were part of the category *before* any UI/JSON edits.
197
- if (serverData && serverData.name && state.originalServerNamesForFormPopulation.has(serverData.name)) {
198
- // This serverData (from JSON) corresponds to one of the original servers. It should be read-only.
199
- actualReadOnlyForThisServer = true;
200
- } else {
201
- // This serverData (from JSON) is new (wasn't in originalServerNamesForFormPopulation). It should be editable.
202
- actualReadOnlyForThisServer = false;
203
- }
204
- } else {
205
- // We are NOT toggling from JSON view. This is either:
206
- // 1. Initial population of an existing category's servers (serverData will be provided).
207
- // 2. User clicked the "Add Server" button while an existing category context is active (serverData will be null).
208
- if (serverData) {
209
- // Case 1: Initial population of an existing server from category data. Should be read-only (unless it was adhoc, handled above).
210
- actualReadOnlyForThisServer = true;
211
- } else {
212
- // Case 2: User clicked "Add Server" button. The new server item should be editable.
213
- actualReadOnlyForThisServer = false;
214
- }
215
- }
216
- }
217
- // `isContextGenerallyReadOnly`: Influences initial template state for some UI elements (e.g., button visibility).
218
- // `actualReadOnlyForThisServer`: Determines if fields should be disabled and is passed to setupReadOnlyState.
219
- // `serverData`: Used by the template for initial adhoc title span, and passed to setupReadOnlyState for more checks.
220
- container.insertAdjacentHTML('beforeend', serverTemplate(newServerIndex, isContextGenerallyReadOnly, serverData, serversListId));
221
-
222
- setEnvCounter(serversListId, newServerIndex, 0);
223
- setServerRequirementCounter(serversListId, newServerIndex, 0);
224
- setServerCounter(serversListId, newServerIndex + 1);
225
-
226
- const serverItem = container.querySelector(`.server-item[data-index="${newServerIndex}"]`);
227
- if (serverItem) {
228
- // Handle systemTags dataset
229
- if (serverData && serverData.systemTags) {
230
- serverItem.dataset.systemTags = JSON.stringify(serverData.systemTags);
231
- } else if (serverData && !serverData.systemTags) {
232
- delete serverItem.dataset.systemTags;
233
- } else if (!serverData) {
234
- delete serverItem.dataset.systemTags;
235
- }
236
-
237
- // Handle originalName dataset
238
- if (serverData && serverData.name) {
239
- serverItem.dataset.originalName = serverData.name;
240
- } else {
241
- // If it's a new server (serverData is null) or serverData has no name,
242
- // ensure no stale originalName if the DOM element was somehow reused.
243
- delete serverItem.dataset.originalName;
244
- }
245
-
246
- setupServerMode(serverItem, newServerIndex, serversListId, actualReadOnlyForThisServer, serverData);
247
- // Pass serverData to setupReadOnlyState so it can accurately determine adhoc status for title.
248
- setupReadOnlyState(serverItem, actualReadOnlyForThisServer, serverData, serversListId, newServerIndex);
249
-
250
- // Default expand Package Dependencies, Startup Configuration, and Environment Variables sections
251
- const sections = [
252
- { contentId: `${serversListId}-server-deps-content-${newServerIndex}`, iconSelector: `#${serversListId}-deps-header-${newServerIndex} i` },
253
- { contentId: `${serversListId}-installation-content-${newServerIndex}`, iconSelector: `#${serversListId}-startup-header-${newServerIndex} i` },
254
- { contentId: `${serversListId}-env-vars-content-${newServerIndex}`, iconSelector: `#${serversListId}-envars-header-${newServerIndex} i` }
255
- ];
256
-
257
- sections.forEach(({ contentId, iconSelector }) => {
258
- const contentElement = document.getElementById(contentId);
259
- const iconElement = serverItem.querySelector(iconSelector);
260
- if (contentElement && iconElement) {
261
- contentElement.classList.remove('hidden');
262
- iconElement.classList.remove('bxs-chevron-down');
263
- iconElement.classList.add('bxs-chevron-up');
264
- }
265
- });
266
-
267
- // New logic: Focus on the newly added server and collapse others,
268
- // only if it's not an existing read-only server being populated.
269
- if (!actualReadOnlyForThisServer) {
270
- const allServerItems = container.querySelectorAll('.server-item');
271
- allServerItems.forEach(item => {
272
- const currentIndex = parseInt(item.dataset.index, 10);
273
- // Ensure item is a direct child of the container to avoid issues if querySelectorAll picks up nested items.
274
- if (item.parentElement !== container) return;
275
-
276
- const contentId = `${serversListId}-server-content-${currentIndex}`;
277
- const iconElement = item.querySelector('.server-header-toggle i.toggle-icon');
278
- const contentElement = document.getElementById(contentId);
279
-
280
- if (contentElement && iconElement) {
281
- if (currentIndex === newServerIndex) {
282
- // Expand the new server if it's collapsed
283
- if (contentElement.classList.contains('hidden')) {
284
- toggleSectionContent(contentId, iconElement);
285
- }
286
- // Scroll to the new server
287
- // Use a slight delay to ensure rendering is complete for smooth scroll
288
- setTimeout(() => {
289
- item.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
290
- }, 0);
291
- } else {
292
- // Collapse other servers if they are expanded
293
- if (!contentElement.classList.contains('hidden')) {
294
- toggleSectionContent(contentId, iconElement);
295
- }
296
- }
297
- }
298
- });
299
- }
300
- }
301
-
302
- return newServerIndex;
303
- }
304
-
305
- function setupServerMode(serverItem, serverIndex, serversListId, isReadOnly, serverData) {
306
- const modeSelect = serverItem.querySelector(`select[name="servers[${serverIndex}].mode"]`);
307
- if (!modeSelect) return;
308
-
309
- if (isReadOnly && serverData) {
310
- modeSelect.value = serverData.mode || 'stdio';
311
- }
312
-
313
- modeSelect.addEventListener('change', () => renderInstallationConfig(serverIndex, serversListId));
314
- renderInstallationConfig(serverIndex, serversListId, modeSelect.value, isReadOnly, serverData?.installation);
315
- }
316
-
317
- // `isEffectivelyReadOnly` determines if the fields within this server item should be disabled.
318
- // `serverDataFromPopulation` is the original server data object passed during population, used to check initial adhoc status.
319
- function setupReadOnlyState(serverItem, isEffectivelyReadOnly, serverDataFromPopulation, serversListId, serverIndex) {
320
- const titleElement = serverItem.querySelector(`#${serversListId}-server-title-${serverIndex}`);
321
- const baseTitle = `MCP Server #${serverIndex + 1}`;
322
- let isAdhocForTitle = serverDataFromPopulation?.systemTags?.adhoc === 'true';
323
-
324
- // Double-check adhoc status from dataset if serverDataFromPopulation doesn't indicate it
325
- // (e.g. if it became adhoc after JSON edit and re-population)
326
- if (!isAdhocForTitle && serverItem.dataset.systemTags) {
327
- try {
328
- const tags = JSON.parse(serverItem.dataset.systemTags);
329
- if (tags.adhoc === "true") {
330
- isAdhocForTitle = true;
331
- }
332
- } catch (e) { /* ignore parsing error for title determination */ }
333
- }
334
-
335
- if (isEffectivelyReadOnly) {
336
- if (titleElement) {
337
- if (!isAdhocForTitle) {
338
- titleElement.innerHTML = `${baseTitle} (Read-only)`;
339
- } else {
340
- // For adhoc servers, the template is responsible for the "(Adhoc - Editable)" span.
341
- // If it's somehow missing and it's adhoc, ensure it's present.
342
- // This situation should be rare if template logic is correct.
343
- if (!titleElement.querySelector('span.text-blue-600')) {
344
- titleElement.innerHTML = `${baseTitle} <span class="text-sm text-blue-600 ml-1">(Adhoc - Editable)</span>`;
345
- } else {
346
- // Ensure base title is correct if adhoc span is already there
347
- const adhocSpanHTML = titleElement.querySelector('span.text-blue-600').outerHTML;
348
- titleElement.innerHTML = `${baseTitle} ${adhocSpanHTML}`;
349
- }
350
- }
351
- }
352
- // Disable all input fields within this specific server item
353
- serverItem.querySelectorAll('input, select, textarea').forEach(el => {
354
- // Check if the element is part of a sub-item (like env var or requirement)
355
- // These sub-items have their own read-only logic handled by their add functions.
356
- // We only want to disable the main server fields here.
357
- if (!el.closest('.env-var-item') && !el.closest('.server-requirement-item')) {
358
- el.disabled = true;
359
- el.classList.add('bg-gray-100', 'cursor-not-allowed', 'opacity-70');
360
- }
361
- });
362
-
363
- // Hide action buttons (Add Dependency, Add Env Var, Remove Server) for this server if it's read-only,
364
- // but ensure the "Duplicate Server" button remains visible.
365
- serverItem.querySelectorAll('.action-button-in-server').forEach(btn => {
366
- if (!btn.classList.contains('duplicate-mcp-server-button')) {
367
- // This is NOT the duplicate button, so hide it if the server is read-only.
368
- btn.style.display = 'none';
369
- btn.classList.add('hidden');
370
- } else {
371
- // This IS the duplicate button. Ensure it's visible.
372
- // (It should be visible by default as it doesn't have conditional hide classes from the template)
373
- btn.style.display = 'flex'; // Or its appropriate display style
374
- btn.classList.remove('hidden');
375
- btn.disabled = false; // Ensure it's enabled
376
- }
377
- });
378
-
379
- // Expand server content and all key sections
380
- const sections = [
381
- { contentId: `${serversListId}-server-content-${serverIndex}`, iconSelector: '.server-header-toggle i.toggle-icon' },
382
- { contentId: `${serversListId}-server-deps-content-${serverIndex}`, iconSelector: `#${serversListId}-deps-header-${serverIndex} i` },
383
- { contentId: `${serversListId}-installation-content-${serverIndex}`, iconSelector: `#${serversListId}-startup-header-${serverIndex} i` },
384
- { contentId: `${serversListId}-env-vars-content-${serverIndex}`, iconSelector: `#${serversListId}-envars-header-${serverIndex} i` }
385
- ];
386
-
387
- sections.forEach(({ contentId, iconSelector }) => {
388
- const contentElement = document.getElementById(contentId);
389
- const iconElement = serverItem.querySelector(iconSelector);
390
- if (contentElement && iconElement) {
391
- contentElement.classList.remove('hidden');
392
- iconElement.classList.remove('bxs-chevron-down');
393
- iconElement.classList.add('bxs-chevron-up');
394
- }
395
- });
396
- } else { // Server is effectively editable
397
- if (titleElement) {
398
- // Server is editable. Template handles the adhoc span.
399
- // We just ensure the base title is correct.
400
- // If adhoc, the template adds the span. If not adhoc, it's just the base title.
401
- if (isAdhocForTitle) {
402
- // Check if the adhoc span is already there from the template.
403
- // If not (e.g. server became adhoc after initial render and this is a re-evaluation), add it.
404
- if (!titleElement.querySelector('span.text-blue-600')) {
405
- titleElement.innerHTML = `${baseTitle} <span class="text-sm text-blue-600 ml-1">(Adhoc - Editable)</span>`;
406
- } else {
407
- // Ensure base title is correct if adhoc span is already there
408
- const adhocSpanHTML = titleElement.querySelector('span.text-blue-600').outerHTML;
409
- titleElement.innerHTML = `${baseTitle} ${adhocSpanHTML}`;
410
- }
411
- } else {
412
- // Editable and not adhoc, just the base title.
413
- titleElement.innerHTML = baseTitle;
414
- }
415
- }
416
- setTimeout(() => {
417
- // Enable main server fields
418
- serverItem.querySelectorAll('input, select, textarea').forEach(el => {
419
- if (!el.closest('.env-var-item') && !el.closest('.server-requirement-item')) {
420
- el.disabled = false;
421
- el.removeAttribute('readonly'); // Ensure readonly is also removed
422
- el.classList.remove('bg-gray-100', 'cursor-not-allowed', 'opacity-70');
423
- }
424
- });
425
-
426
- // Show ALL action buttons for this server (Add Dependency, Add Env Var, Remove Server)
427
- // as the server is determined to be effectively editable.
428
- serverItem.querySelectorAll('.action-button-in-server').forEach(btn => {
429
- btn.style.display = 'flex'; // Or 'inline-flex' or whatever its default visible display is
430
- btn.disabled = false;
431
- btn.classList.remove('hidden', 'opacity-50', 'cursor-not-allowed');
432
- });
433
- }, 0);
434
- }
435
- }
436
-
437
- export function renderInstallationConfig(serverIndex, serversListId = 'serversList', initialModeValue = null, isReadOnly = false, installationData = null) {
438
- const serverItem = document.querySelector(`#${serversListId} .server-item[data-index="${serverIndex}"]`);
439
- if (!serverItem) return;
440
-
441
- const modeSelect = serverItem.querySelector(`select[name="servers[${serverIndex}].mode"]`);
442
- const mode = initialModeValue || (modeSelect ? modeSelect.value : 'stdio');
443
- const container = serverItem.querySelector(`#installation-config-${serverIndex}`);
444
- const envVarsBlock = serverItem.querySelector(`#env-vars-block-${serverIndex}`);
445
-
446
- if (!container) return;
447
-
448
- const disabledAttr = isReadOnly ? 'disabled' : '';
449
- const readOnlyClasses = isReadOnly ? 'bg-gray-100 cursor-not-allowed opacity-70' : '';
450
-
451
- container.innerHTML = mode === 'sse' ?
452
- generateSSETemplate(serverIndex, disabledAttr, readOnlyClasses, installationData) :
453
- generateStdioTemplate(serverIndex, disabledAttr, readOnlyClasses, installationData);
454
-
455
- if (envVarsBlock) {
456
- envVarsBlock.style.display = mode === 'sse' ? 'none' : '';
457
- }
458
- }
459
-
460
- function generateSSETemplate(serverIndex, disabledAttr, readOnlyClasses, installationData) {
461
- return `
462
- <div>
463
- <label class="block text-sm font-medium text-gray-700 mb-1">Server URL*</label>
464
- <input type="text" name="servers[${serverIndex}].installation.url" required ${disabledAttr}
465
- class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${readOnlyClasses}"
466
- placeholder="e.g., https://your-server.com/api/mcp"
467
- value="${installationData?.url || ''}">
468
- </div>
469
- `;
470
- }
471
-
472
- function generateStdioTemplate(serverIndex, disabledAttr, readOnlyClasses, installationData) {
473
- return `
474
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
475
- <div>
476
- <label class="block text-sm font-medium text-gray-700 mb-1">Command*</label>
477
- <input type="text" name="servers[${serverIndex}].installation.command" required ${disabledAttr}
478
- class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${readOnlyClasses}"
479
- placeholder="e.g., node, python3"
480
- value="${installationData?.command || ''}">
481
- </div>
482
- <div>
483
- <label class="block text-sm font-medium text-gray-700 mb-1">Arguments (comma-separated)</label>
484
- <input type="text" name="servers[${serverIndex}].installation.args" ${disabledAttr}
485
- class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${readOnlyClasses}"
486
- placeholder="arg1, path/to/script.js, --flag"
487
- value="${installationData?.args ? installationData.args.join(', ') : ''}">
488
- </div>
489
- </div>
490
- `;
491
- }
492
-
493
- export function removeServer(serverIndexToRemove, serversListId = 'serversList') {
494
- const serverListContainer = document.getElementById(serversListId);
495
- if (!serverListContainer) return;
496
-
497
- const item = serverListContainer.querySelector(`.server-item[data-index="${serverIndexToRemove}"]`);
498
- if (item) {
499
- item.remove();
500
- deleteEnvCounter(serversListId, serverIndexToRemove);
501
- deleteServerRequirementCounter(serversListId, serverIndexToRemove);
502
- reindexServers(serversListId);
503
- }
504
- }
505
-
506
- // `isServerEffectivelyReadOnly` is passed to determine if the new env var item itself should be read-only.
507
- // This would be true if the parent server is non-adhoc and in a read-only category.
508
- export function addEnvVariable(serverIndex, serversListId = 'serversList', isServerEffectivelyReadOnly = false) {
509
- const container = document.querySelector(`#${serversListId} .server-item[data-index="${serverIndex}"] #envVarsContainer_${serverIndex}`);
510
- if (!container) return -1;
511
-
512
- const envIndex = getEnvCounter(serversListId, serverIndex);
513
- // Pass `isServerEffectivelyReadOnly` to the template for the new env var item.
514
- container.insertAdjacentHTML('beforeend', envVariableTemplate(serverIndex, envIndex, isServerEffectivelyReadOnly, serversListId));
515
- setEnvCounter(serversListId, serverIndex, envIndex + 1);
516
-
517
- // The template itself handles disabling fields if isServerEffectivelyReadOnly is true.
518
- // No need for additional logic here to disable fields of the newly added item.
519
-
520
- return envIndex;
521
- }
522
-
523
- export function removeEnvVariable(serverIndex, envIndex, serversListId = 'serversList') {
524
- const item = document.querySelector(`#${serversListId} .server-item[data-index="${serverIndex}"] .env-var-item[data-env-index="${envIndex}"]`);
525
- if (item) item.remove();
526
- }
527
-
528
- // `isServerEffectivelyReadOnly` is passed to determine if the new requirement item itself should be read-only.
529
- export function addServerRequirement(serverIndex, serversListId = 'serversList', isServerEffectivelyReadOnly = false) {
530
- console.log(`[addServerRequirement] ServerIndex: ${serverIndex}, isServerEffectivelyReadOnly received: ${isServerEffectivelyReadOnly}`); // DEBUG
531
- const container = document.querySelector(`#${serversListId} .server-item[data-index="${serverIndex}"] #server-requirements-list-${serverIndex}`);
532
- if (!container) return -1;
533
-
534
- const reqIndex = getServerRequirementCounter(serversListId, serverIndex);
535
- // Pass `isServerEffectivelyReadOnly` to the template for the new requirement item.
536
- container.insertAdjacentHTML('beforeend', serverRequirementTemplate(serverIndex, reqIndex, isServerEffectivelyReadOnly, serversListId));
537
- setServerRequirementCounter(serversListId, serverIndex, reqIndex + 1);
538
-
539
- // The template itself handles disabling fields if isServerEffectivelyReadOnly is true.
540
-
541
- toggleServerAliasField(serverIndex, reqIndex, serversListId);
542
- toggleServerRegistryConfig(serverIndex, reqIndex, serversListId);
543
- return reqIndex;
544
- }
545
-
546
- export function removeServerRequirement(serverIndex, reqIndex, serversListId = 'serversList') {
547
- const item = document.querySelector(`#${serversListId} .server-item[data-index="${serverIndex}"] .server-requirement-item[data-req-index="${reqIndex}"]`);
548
- if (item) item.remove();
549
- }
550
-
551
- export function toggleServerAliasField(serverIndex, reqIndex, serversListId = 'serversList') {
552
- const requirementItem = document.querySelector(`#${serversListId} .server-item[data-index="${serverIndex}"] .server-requirement-item[data-req-index="${reqIndex}"]`);
553
- if (!requirementItem) return;
554
-
555
- const typeSelect = requirementItem.querySelector(`select[name="servers[${serverIndex}].requirements[${reqIndex}].type"]`);
556
- const aliasField = requirementItem.querySelector(`#server-alias-field-${serverIndex}-${reqIndex}`);
557
-
558
- if (typeSelect && aliasField) {
559
- aliasField.classList.toggle('hidden', typeSelect.value !== 'command');
560
- }
561
- }
562
-
563
- export function toggleServerRegistryConfig(serverIndex, reqIndex, serversListId = 'serversList') {
564
- const requirementItem = document.querySelector(`#${serversListId} .server-item[data-index="${serverIndex}"] .server-requirement-item[data-req-index="${reqIndex}"]`);
565
- if (!requirementItem) return;
566
-
567
- const select = requirementItem.querySelector(`select[name="servers[${serverIndex}].requirements[${reqIndex}].registryType"]`);
568
- if (!select) return;
569
-
570
- ['github', 'artifacts'].forEach(type => {
571
- const config = requirementItem.querySelector(`#server-${type}-config-${serverIndex}-${reqIndex}`);
572
- if (config) {
573
- config.classList.toggle('hidden', select.value !== type);
574
- }
575
- });
576
- }
577
-
578
- export function toggleSectionContent(contentId, iconElement, toggleElement = null) {
579
- const contentElement = document.getElementById(contentId);
580
- if (!contentElement) return;
581
-
582
- toggleElement = toggleElement || (iconElement?.parentElement || null);
583
- const isHiddenAfterToggle = contentElement.classList.toggle('hidden');
584
-
585
- if (iconElement) {
586
- iconElement.classList.toggle('bxs-chevron-up', !isHiddenAfterToggle);
587
- iconElement.classList.toggle('bxs-chevron-down', isHiddenAfterToggle);
588
- }
589
-
590
- if (toggleElement) {
591
- toggleElement.setAttribute('aria-expanded', (!isHiddenAfterToggle).toString());
592
- }
593
- }
594
-
595
- export function toggleViewMode(isJsonView, currentFormId, currentServersListId, baseCategoryData = null, isExistingCategoryContext = false) {
596
- const elements = {
597
- createCategory: document.getElementById('panel-create-category'),
598
- createServer: document.getElementById('panel-create-server'),
599
- jsonEditor: document.getElementById('panel-json-editor'),
600
- textarea: document.getElementById('jsonEditorTextarea'),
601
- activeForm: document.getElementById(currentFormId),
602
- viewModeToggle: document.getElementById('viewModeToggle'),
603
- actionButtons: {
604
- json: document.getElementById('jsonEditorActionsContainer'),
605
- main: document.querySelector(`#${currentFormId} ~ .flex.justify-end.space-x-4.pt-6.border-t`)
606
- }
607
- };
608
-
609
- if (isViewAlreadyActive(isJsonView, elements, currentFormId)) return;
610
-
611
- // Clear any previous temporary state
612
- if (state.originalServerNamesForFormPopulation) {
613
- state.originalServerNamesForFormPopulation = null;
614
- }
615
-
616
- try {
617
- if (isJsonView) {
618
- handleJsonView(elements, currentFormId, currentServersListId, baseCategoryData, isExistingCategoryContext);
619
- } else { // Switching to Form View
620
- // If switching to form view for 'create-server' tab in an existing category context,
621
- // prepare the set of original server names.
622
- if (isExistingCategoryContext && baseCategoryData && baseCategoryData.mcpServers && currentFormId === 'onboardServerForm') {
623
- state.originalServerNamesForFormPopulation = new Set(
624
- baseCategoryData.mcpServers.map(s => s.name).filter(name => name) // Ensure name exists
625
- );
626
- }
627
- handleFormView(elements, currentFormId, currentServersListId, baseCategoryData, isExistingCategoryContext);
628
- // Clean up the temporary state after populateForm has finished using it.
629
- if (state.originalServerNamesForFormPopulation) {
630
- state.originalServerNamesForFormPopulation = null;
631
- }
632
- }
633
- } catch (error) {
634
- console.error('Error in view mode toggle:', error);
635
- showToast(`Error: ${error.message}`, 'error');
636
- if (elements.viewModeToggle) elements.viewModeToggle.checked = isJsonView; // Revert toggle on error
637
- // Ensure cleanup on error too
638
- if (state.originalServerNamesForFormPopulation) {
639
- state.originalServerNamesForFormPopulation = null;
640
- }
641
- }
642
- }
643
-
644
- function isViewAlreadyActive(isJsonView, elements, currentFormId) {
645
- if (isJsonView && elements.jsonEditor && !elements.jsonEditor.classList.contains('hidden')) {
646
- return true;
647
- }
648
-
649
- if (!isJsonView && elements.jsonEditor?.classList.contains('hidden')) {
650
- if (currentFormId === 'onboardForm' && elements.createCategory && !elements.createCategory.classList.contains('hidden')) {
651
- return true;
652
- }
653
- if (currentFormId === 'onboardServerForm' && elements.createServer && !elements.createServer.classList.contains('hidden')) {
654
- return true;
655
- }
656
- }
657
-
658
- return false;
659
- }
660
-
661
- function handleJsonView(elements, currentFormId, currentServersListId, baseCategoryData, isExistingCategoryContext) {
662
- const feedConfig = getFeedConfiguration(elements.activeForm, baseCategoryData, isExistingCategoryContext);
663
-
664
- elements.textarea.value = JSON.stringify(feedConfig, null, 2);
665
- elements.textarea.readOnly = isExistingCategoryContext && baseCategoryData?.mcpServers?.length > 0;
666
-
667
- if (elements.textarea.readOnly) {
668
- elements.textarea.classList.add('bg-gray-100', 'cursor-not-allowed');
669
- showToast('JSON view is read-only for existing servers in this category.', 'info');
670
- } else {
671
- elements.textarea.classList.remove('bg-gray-100', 'cursor-not-allowed');
672
- }
673
-
674
- togglePanels(elements, true);
675
- toggleButtons(elements.actionButtons, true);
676
- }
677
-
678
- function handleFormView(elements, currentFormId, currentServersListId, baseCategoryData, isExistingCategoryContext) {
679
- const jsonData = JSON.parse(elements.textarea.value);
680
- // Explicitly clear the server list container before repopulating to prevent UI duplication
681
- const serverListContainer = document.getElementById(currentServersListId);
682
- if (serverListContainer) {
683
- serverListContainer.innerHTML = ''; // Clear existing UI server items
684
- }
685
- // Reset counters and other dynamic state
686
- resetOnboardFormDynamicContent(currentFormId, currentServersListId);
687
- populateForm(jsonData, currentFormId, isExistingCategoryContext && baseCategoryData?.mcpServers?.length > 0, currentServersListId);
688
-
689
- togglePanels(elements, false, currentFormId);
690
- toggleButtons(elements.actionButtons, false);
691
- }
692
-
693
- function getFeedConfiguration(activeForm, baseCategoryData, isExistingCategoryContext) {
694
- if (!isExistingCategoryContext || !baseCategoryData) {
695
- // If not in an existing category context, or no base data, just get current form data.
696
- return activeForm ? getFormData(activeForm) : {};
697
- }
698
-
699
- // In an existing category context, get the data from the current form.
700
- // `true` for forExistingCategoryTab ensures getFormData processes only server-related fields
701
- // and correctly handles adhoc/new servers.
702
- const newData = getFormData(activeForm, true, baseCategoryData);
703
-
704
- // Start with a deep clone of the original category data (for name, description, etc.)
705
- const merged = JSON.parse(JSON.stringify(baseCategoryData));
706
-
707
- // Replace mcpServers with the current state from the form (newData).
708
- // This ensures deletions and modifications in the form are accurately reflected.
709
- // newData.mcpServers will include original read-only servers (if any),
710
- // modified adhoc servers, and newly added servers, all with their current state.
711
- merged.mcpServers = newData.mcpServers || [];
712
-
713
- // Requirements should also be derived from the current state of servers in the form.
714
- // formDataToFeedConfiguration (which produces newData) calculates global requirements
715
- // based on the servers it finds. So, newData.requirements should be the correct set.
716
- merged.requirements = newData.requirements || [];
717
-
718
- return merged;
719
- }
720
-
721
- function togglePanels(elements, isJsonView, currentFormId = null) {
722
- // First ensure all panels are hidden
723
- elements.createCategory?.classList.add('hidden');
724
- elements.createServer?.classList.add('hidden');
725
- elements.jsonEditor?.classList.add('hidden');
726
-
727
- // Then show only the appropriate panel
728
- if (isJsonView) {
729
- elements.jsonEditor?.classList.remove('hidden');
730
- } else if (currentFormId) {
731
- // Show only the panel corresponding to the current form
732
- if (currentFormId === 'onboardForm') {
733
- elements.createCategory?.classList.remove('hidden');
734
- } else if (currentFormId === 'onboardServerForm') {
735
- elements.createServer?.classList.remove('hidden');
736
- }
737
- }
738
- }
739
-
740
- function toggleButtons(buttons, isJsonView) {
741
- buttons.main?.classList.toggle('hidden', isJsonView);
742
- buttons.json?.classList.toggle('hidden', isJsonView);
743
- }
744
-
745
- export async function saveJsonData(activeTab, currentSelectedCategoryData = null) {
746
- const textarea = document.getElementById('jsonEditorTextarea');
747
- if (textarea.readOnly) {
748
- showToast('Cannot save, JSON editor is in read-only mode for existing servers.', 'warning');
749
- return;
750
- }
751
-
752
- try {
753
- const editorData = JSON.parse(textarea.value);
754
- const config = prepareConfiguration(activeTab, editorData, currentSelectedCategoryData);
755
- if (!config) return;
756
-
757
- await saveConfiguration(config);
758
- } catch (error) {
759
- console.error('Error saving JSON data:', error);
760
- showToast(`Error saving JSON data: ${error.message}`, 'error');
761
- }
762
- }
763
-
764
- function prepareConfiguration(activeTab, editorData, currentSelectedCategoryData) {
765
- if (activeTab === 'create-category') {
766
- if (!editorData.name) {
767
- showToast('Category Name is required in JSON.', 'error');
768
- return null;
769
- }
770
- return {
771
- data: editorData,
772
- isUpdate: isUpdateOperation(editorData.name)
773
- };
774
- }
775
-
776
- if (activeTab === 'create-server') {
777
- if (!currentSelectedCategoryData?.name) {
778
- showToast('No existing category context for saving JSON.', 'error');
779
- return null;
780
- }
781
- return {
782
- data: mergeConfigurations(currentSelectedCategoryData, editorData),
783
- isUpdate: true
784
- };
785
- }
786
-
787
- showToast('Invalid tab context for saving JSON.', 'error');
788
- return null;
789
- }
790
-
791
- function isUpdateOperation(categoryName) {
792
- const urlParams = new URLSearchParams(window.location.search);
793
- return urlParams.get('action') === 'edit' && urlParams.get('category') === categoryName;
794
- }
795
-
796
- function mergeConfigurations(base, editor) {
797
- const merged = JSON.parse(JSON.stringify(base));
798
- const existingServerNames = new Set(merged.mcpServers?.map(s => s.name) || []);
799
-
800
- const newServers = (editor.mcpServers || []).filter(s => !existingServerNames.has(s.name));
801
- merged.mcpServers = (merged.mcpServers || []).concat(newServers);
802
-
803
- const existingReqs = new Set(merged.requirements?.map(r => `${r.type}|${r.name}|${r.version}`) || []);
804
- (editor.requirements || []).forEach(req => {
805
- const key = `${req.type}|${req.name}|${req.version}`;
806
- const isNewServerReq = newServers.some(s =>
807
- s.dependencies?.requirements?.some(r =>
808
- r.name === req.name && r.version === req.version
809
- )
810
- );
811
-
812
- if (!existingReqs.has(key) && isNewServerReq) {
813
- merged.requirements = merged.requirements || [];
814
- merged.requirements.push(req);
815
- }
816
- });
817
-
818
- return merged;
819
- }
820
-
821
- async function saveConfiguration({ data, isUpdate }) {
822
- const response = await fetch('/api/categories/onboard', {
823
- method: 'POST',
824
- headers: { 'Content-Type': 'application/json' },
825
- body: JSON.stringify({ categoryData: data, isUpdate })
826
- });
827
-
828
- if (!response.ok) {
829
- const error = await response.json();
830
- throw new Error(error.error || `HTTP error! status: ${response.status}`);
831
- }
832
-
833
- showToast('JSON data submitted successfully!', 'success');
834
- }
835
-
836
- export async function copyJsonToClipboard() {
837
- const textarea = document.getElementById('jsonEditorTextarea');
838
- if (!textarea) {
839
- showToast('JSON content not found.', 'error');
840
- return;
841
- }
842
-
843
- try {
844
- await navigator.clipboard.writeText(textarea.value);
845
- showToast('JSON copied to clipboard!', 'success');
846
- } catch (err) {
847
- console.error('Failed to copy JSON:', err);
848
- showToast('Failed to copy JSON. See console for details.', 'error');
849
- }
850
- }
851
-
852
- /**
853
- * Duplicates an MCP server item in the form.
854
- * @param {number} serverIndexToDuplicate - The index of the server to duplicate.
855
- * @param {string} serversListId - The ID of the servers list container (e.g., 'serversList', 'existingCategoryServersList').
856
- */
857
- export function duplicateServer(serverIndexToDuplicate, serversListId) {
858
- const formId = serversListId === 'serversList' ? 'onboardForm' : 'onboardServerForm';
859
- const formElement = document.getElementById(formId);
860
- if (!formElement) {
861
- showToast(`Form with ID ${formId} not found. Cannot duplicate server.`, 'error');
862
- return;
863
- }
864
-
865
- // Determine if we are in the "Create Server in Existing Category" tab context
866
- const isExistingCategoryContext = serversListId === 'existingCategoryServersList';
867
- let baseCategoryDataForFormExtraction = null;
868
- if (isExistingCategoryContext) {
869
- // If in existing category context, getFormData might need the base category data
870
- // This depends on how getFormData is structured to handle this tab.
871
- // Assuming window.currentSelectedCategoryData holds the loaded category.
872
- baseCategoryDataForFormExtraction = window.currentSelectedCategoryData || null;
873
- }
874
-
875
- const serverItemToDuplicate = formElement.querySelector(`.server-item[data-index="${serverIndexToDuplicate}"]`);
876
- if (!serverItemToDuplicate) {
877
- showToast('Could not find the server item to duplicate in the DOM.', 'error');
878
- return;
879
- }
880
-
881
- // Track and temporarily enable ALL disabled fields in the form to ensure we get complete data
882
- const disabledFields = [];
883
- formElement.querySelectorAll('input, select, textarea').forEach(el => {
884
- if (el.disabled) {
885
- disabledFields.push({element: el, wasDisabled: true});
886
- el.disabled = false;
887
- }
888
- });
889
-
890
- // Get all current server data from the form
891
- const currentFullFeedConfig = getFormData(formElement, isExistingCategoryContext, baseCategoryDataForFormExtraction);
892
-
893
- // Restore original disabled state for all fields
894
- disabledFields.forEach(({element, wasDisabled}) => {
895
- element.disabled = wasDisabled;
896
- });
897
-
898
- if (!currentFullFeedConfig || !currentFullFeedConfig.mcpServers ||
899
- currentFullFeedConfig.mcpServers.length <= serverIndexToDuplicate) {
900
- showToast('Could not retrieve data for the server to duplicate after attempting to enable fields.', 'error');
901
- return;
902
- }
903
-
904
- // 2. Get the specific server data to duplicate (deep copy)
905
- let serverToDuplicateData = JSON.parse(JSON.stringify(currentFullFeedConfig.mcpServers[serverIndexToDuplicate]));
906
-
907
- // 3. IMPORTANT: Remove systemTags from the duplicated data.
908
- // The duplicated server should be treated as a new, fully editable server.
909
- if (serverToDuplicateData.systemTags) {
910
- delete serverToDuplicateData.systemTags;
911
- }
912
- // Also, ensure its name is distinct if needed, or clear it to force user input.
913
- // For now, we'll copy the name but it will be editable.
914
- // Consider adding a suffix like "-copy" to the name if automatic distinct names are desired.
915
- // serverToDuplicateData.name = serverToDuplicateData.name ? `${serverToDuplicateData.name}-copy` : 'duplicated-server';
916
-
917
-
918
- // 4. Determine if the general context for adding a new server is read-only
919
- // This is primarily for the 'Edit Existing Category' tab.
920
- // 'Create New Category' tab is never read-only in this sense.
921
- let isContextGenerallyReadOnlyForAddServer = false;
922
- if (isExistingCategoryContext) {
923
- isContextGenerallyReadOnlyForAddServer = window.isExistingCategoryReadOnly || false;
924
- }
925
-
926
- // 5. Add a new server item. Pass `null` for serverData to ensure it's treated as a new item.
927
- // The `isContextGenerallyReadOnlyForAddServer` helps `addServer` decide initial template state.
928
- const newServerIndex = addServer(serversListId, isContextGenerallyReadOnlyForAddServer, null);
929
-
930
- if (newServerIndex === -1) {
931
- showToast('Failed to add a new server item for duplication.', 'error');
932
- return;
933
- }
934
-
935
- // 6. Populate the new server item with the copied data.
936
- // The new server should be fully editable.
937
- // We need to call populateForm or a similar function for just this new server.
938
- // populateForm expects a full FeedConfiguration. We'll construct a minimal one.
939
- const tempFeedConfigForPopulation = {
940
- mcpServers: [serverToDuplicateData] // Contains only the server data to populate
941
- };
942
-
943
- // Call populateForm, but ensure it targets the *new* server index and makes it editable.
944
- // The populateForm function needs to be aware it's populating a *specific, new* server.
945
- // The last `true` tells populateForm to make this specific server editable, overriding general read-only context.
946
- // This might require adjustments in populateForm or a dedicated populateSingleServerForm function.
947
- // For now, assuming populateForm can handle this if we pass a single server and target the new index.
948
- // We pass `false` for `renderServersAsReadOnly` to ensure the duplicated server is editable.
949
- // And we pass `true` for a hypothetical `forceEditableForSingleServer` if populateForm supported it.
950
- // Let's simplify: populateForm will populate based on the data. addServer already set it up as editable.
951
- // We need to ensure that `populateForm` correctly populates the server at `newServerIndex`.
952
- // The `populateForm` function in formProcessor.js needs to be able to populate a *specific* server
953
- // if we pass only one server in `mcpServers`.
954
-
955
- // Get the newly added server item
956
- const serversList = document.getElementById(serversListId);
957
- const newServerItem = serversList.querySelector(`.server-item[data-index="${newServerIndex}"]`);
958
-
959
- if (newServerItem) {
960
- // Directly populate fields for the new server.
961
- // This is a simplified version of what populateForm does for a single server.
962
- // This avoids needing to modify populateForm extensively for this specific use case.
963
- populateServerManually(newServerItem, newServerIndex, serverToDuplicateData, serversListId);
964
- showToast(`Server #${serverIndexToDuplicate + 1} duplicated to Server #${newServerIndex + 1}.`, 'success');
965
- } else {
966
- showToast('Failed to find the new server item after duplication.', 'error');
967
- }
968
- }
969
- window.duplicateServer = duplicateServer;
970
-
971
- /**
972
- * Manually populates a single server item's form fields with provided data.
973
- * This is a helper for the duplicateServer functionality.
974
- * @param {HTMLElement} serverItemElement - The HTML element of the server item.
975
- * @param {number} serverIndex - The index of the server item.
976
- * @param {object} serverData - The data object for the server.
977
- * @param {string} serversListId - The ID of the servers list.
978
- */
979
- function populateServerManually(serverItemElement, serverIndex, serverData, serversListId) {
980
- // Populate basic fields
981
- serverItemElement.querySelector(`input[name="servers[${serverIndex}].name"]`).value = serverData.name || '';
982
- serverItemElement.querySelector(`textarea[name="servers[${serverIndex}].description"]`).value = serverData.description || '';
983
- serverItemElement.querySelector(`select[name="servers[${serverIndex}].mode"]`).value = serverData.mode || 'stdio';
984
- serverItemElement.querySelector(`input[name="servers[${serverIndex}].schemas"]`).value = serverData.schemas || '';
985
- serverItemElement.querySelector(`input[name="servers[${serverIndex}].repository"]`).value = serverData.repository || '';
986
-
987
- // Trigger change on mode to render correct installation config
988
- const modeSelect = serverItemElement.querySelector(`select[name="servers[${serverIndex}].mode"]`);
989
- if (modeSelect) {
990
- modeSelect.dispatchEvent(new Event('change')); // This will call renderInstallationConfig
991
- }
992
-
993
- // Populate installation config (command/args for stdio, url for sse)
994
- // renderInstallationConfig should have been called by the modeSelect change event.
995
- // Now, set the values based on serverData.installation
996
- if (serverData.mode === 'stdio' && serverData.installation) {
997
- const commandInput = serverItemElement.querySelector(`input[name="servers[${serverIndex}].installation.command"]`);
998
- if (commandInput) commandInput.value = serverData.installation.command || '';
999
- const argsInput = serverItemElement.querySelector(`input[name="servers[${serverIndex}].installation.args"]`);
1000
- if (argsInput) argsInput.value = serverData.installation.args ? serverData.installation.args.join(', ') : '';
1001
- } else if (serverData.mode === 'sse' && serverData.installation) {
1002
- const urlInput = serverItemElement.querySelector(`input[name="servers[${serverIndex}].installation.url"]`);
1003
- if (urlInput) urlInput.value = serverData.installation.url || '';
1004
- }
1005
-
1006
- // Populate environment variables
1007
- const envVarsContainer = serverItemElement.querySelector(`#envVarsContainer_${serverIndex}`);
1008
- if (envVarsContainer && serverData.installation && serverData.installation.env) {
1009
- Object.entries(serverData.installation.env).forEach(([name, envConfig]) => {
1010
- const envVarIndex = addEnvVariable(serverIndex, serversListId, false); // Add as editable
1011
- const envVarItem = envVarsContainer.querySelector(`.env-var-item[data-env-index="${envVarIndex}"]`);
1012
- if (envVarItem) {
1013
- envVarItem.querySelector(`input[name="servers[${serverIndex}].installation.env[${envVarIndex}].name"]`).value = name;
1014
- envVarItem.querySelector(`input[name="servers[${serverIndex}].installation.env[${envVarIndex}].default"]`).value = envConfig.Default || '';
1015
- envVarItem.querySelector(`input[name="servers[${serverIndex}].installation.env[${envVarIndex}].required"]`).checked = envConfig.Required || false;
1016
- envVarItem.querySelector(`textarea[name="servers[${serverIndex}].installation.env[${envVarIndex}].description"]`).value = envConfig.Description || '';
1017
- }
1018
- });
1019
- }
1020
-
1021
- // Populate server requirements (dependencies)
1022
- const serverReqsContainer = serverItemElement.querySelector(`#server-requirements-list-${serverIndex}`);
1023
- if (serverReqsContainer && serverData.dependencies && serverData.dependencies.requirements) {
1024
- serverData.dependencies.requirements.forEach(req => {
1025
- const reqIndex = addServerRequirement(serverIndex, serversListId, false); // Add as editable
1026
- const reqItem = serverReqsContainer.querySelector(`.server-requirement-item[data-req-index="${reqIndex}"]`);
1027
- if (reqItem) {
1028
- reqItem.querySelector(`input[name="servers[${serverIndex}].requirements[${reqIndex}].name"]`).value = req.name || '';
1029
- reqItem.querySelector(`select[name="servers[${serverIndex}].requirements[${reqIndex}].type"]`).value = req.type || '';
1030
- reqItem.querySelector(`input[name="servers[${serverIndex}].requirements[${reqIndex}].version"]`).value = req.version || '';
1031
- reqItem.querySelector(`input[name="servers[${serverIndex}].requirements[${reqIndex}].order"]`).value = req.order || '';
1032
- reqItem.querySelector(`input[name="servers[${serverIndex}].requirements[${reqIndex}].alias"]`).value = req.alias || '';
1033
-
1034
- const typeSelect = reqItem.querySelector(`select[name="servers[${serverIndex}].requirements[${reqIndex}].type"]`);
1035
- if (typeSelect) typeSelect.dispatchEvent(new Event('change')); // To show/hide alias
1036
-
1037
- const registryTypeSelect = reqItem.querySelector(`select[name="servers[${serverIndex}].requirements[${reqIndex}].registryType"]`);
1038
- if (registryTypeSelect) {
1039
- registryTypeSelect.value = req.registryType || 'public';
1040
- registryTypeSelect.dispatchEvent(new Event('change')); // To show/hide specific registry configs
1041
-
1042
- if (req.registry) {
1043
- if (req.registryType === 'github' && req.registry.githubRelease) {
1044
- reqItem.querySelector(`input[name="servers[${serverIndex}].requirements[${reqIndex}].registry.githubRelease.repository"]`).value = req.registry.githubRelease.repository || '';
1045
- reqItem.querySelector(`input[name="servers[${serverIndex}].requirements[${reqIndex}].registry.githubRelease.assetsName"]`).value = req.registry.githubRelease.assetsName || '';
1046
- reqItem.querySelector(`input[name="servers[${serverIndex}].requirements[${reqIndex}].registry.githubRelease.assetName"]`).value = req.registry.githubRelease.assetName || '';
1047
- } else if (req.registryType === 'artifacts' && req.registry.artifacts) {
1048
- reqItem.querySelector(`input[name="servers[${serverIndex}].requirements[${reqIndex}].registry.artifacts.registryName"]`).value = req.registry.artifacts.registryName || '';
1049
- reqItem.querySelector(`input[name="servers[${serverIndex}].requirements[${reqIndex}].registry.artifacts.registryUrl"]`).value = req.registry.artifacts.registryUrl || '';
1050
- } else if (req.registryType === 'local' && req.registry.local) {
1051
- reqItem.querySelector(`input[name="servers[${serverIndex}].requirements[${reqIndex}].registry.local.localPath"]`).value = req.registry.local.localPath || '';
1052
- reqItem.querySelector(`input[name="servers[${serverIndex}].requirements[${reqIndex}].registry.local.assetName"]`).value = req.registry.local.assetName || '';
1053
- }
1054
- }
1055
- }
1056
- }
1057
- });
1058
- }
1059
- // Ensure the duplicated server is fully editable by calling setupReadOnlyState with false
1060
- setupReadOnlyState(serverItemElement, false, null, serversListId, serverIndex);
1061
- }
1062
- Object.entries({
1063
- addServer,
1064
- removeServer,
1065
- addEnvVariable,
1066
- removeEnvVariable,
1067
- addServerRequirement,
1068
- removeServerRequirement,
1069
- toggleServerAliasField,
1070
- toggleServerRegistryConfig,
1071
- renderInstallationConfig,
1072
- toggleSectionContent,
1073
- copyJsonToClipboard
1074
- }).forEach(([name, fn]) => {
1075
- window[name] = fn;
1076
- });