imcp 0.1.7 → 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.
- package/dist/cli/commands/install.js +1 -106
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/list.js +1 -90
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/pull.js +1 -16
- package/dist/cli/commands/pull.js.map +1 -0
- package/dist/cli/commands/serve.js +1 -33
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/uninstall.js +1 -46
- package/dist/cli/commands/uninstall.js.map +1 -0
- package/dist/cli/index.js +1 -65
- package/dist/cli/index.js.map +1 -0
- package/dist/core/installers/clients/BaseClientInstaller.js +1 -282
- package/dist/core/installers/clients/BaseClientInstaller.js.map +1 -0
- package/dist/core/installers/clients/ClientInstaller.js +1 -163
- package/dist/core/installers/clients/ClientInstaller.js.map +1 -0
- package/dist/core/installers/clients/ClientInstallerFactory.js +1 -36
- package/dist/core/installers/clients/ClientInstallerFactory.js.map +1 -0
- package/dist/core/installers/clients/ClineInstaller.js +1 -30
- package/dist/core/installers/clients/ClineInstaller.js.map +1 -0
- package/dist/core/installers/clients/ExtensionInstaller.js +1 -151
- package/dist/core/installers/clients/ExtensionInstaller.js.map +1 -0
- package/dist/core/installers/clients/GithubCopilotInstaller.js +1 -68
- package/dist/core/installers/clients/GithubCopilotInstaller.js.map +1 -0
- package/dist/core/installers/clients/MSRooCodeInstaller.js +1 -28
- package/dist/core/installers/clients/MSRooCodeInstaller.js.map +1 -0
- package/dist/core/installers/index.js +1 -8
- package/dist/core/installers/index.js.map +1 -0
- package/dist/core/installers/requirements/BaseInstaller.js +1 -56
- package/dist/core/installers/requirements/BaseInstaller.js.map +1 -0
- package/dist/core/installers/requirements/CommandInstaller.js +1 -213
- package/dist/core/installers/requirements/CommandInstaller.js.map +1 -0
- package/dist/core/installers/requirements/GeneralInstaller.js +1 -126
- package/dist/core/installers/requirements/GeneralInstaller.js.map +1 -0
- package/dist/core/installers/requirements/InstallerFactory.js +1 -99
- package/dist/core/installers/requirements/InstallerFactory.js.map +1 -0
- package/dist/core/installers/requirements/NpmInstaller.js +1 -235
- package/dist/core/installers/requirements/NpmInstaller.js.map +1 -0
- package/dist/core/installers/requirements/NugetInstaller.js +1 -188
- package/dist/core/installers/requirements/NugetInstaller.js.map +1 -0
- package/dist/core/installers/requirements/PipInstaller.js +1 -192
- package/dist/core/installers/requirements/PipInstaller.js.map +1 -0
- package/dist/core/installers/requirements/RequirementInstaller.js +1 -2
- package/dist/core/installers/requirements/RequirementInstaller.js.map +1 -0
- package/dist/core/loaders/ConfigurationLoader.js +1 -256
- package/dist/core/loaders/ConfigurationLoader.js.map +1 -0
- package/dist/core/loaders/ConfigurationProvider.js +1 -383
- package/dist/core/loaders/ConfigurationProvider.js.map +1 -0
- package/dist/core/loaders/InstallOperationManager.js +1 -310
- package/dist/core/loaders/InstallOperationManager.js.map +1 -0
- package/dist/core/loaders/ServerSchemaLoader.js +1 -108
- package/dist/core/loaders/ServerSchemaLoader.js.map +1 -0
- package/dist/core/loaders/ServerSchemaProvider.js +1 -89
- package/dist/core/loaders/ServerSchemaProvider.js.map +1 -0
- package/dist/core/loaders/SystemSettingsManager.js +1 -256
- package/dist/core/loaders/SystemSettingsManager.js.map +1 -0
- package/dist/core/metadatas/constants.js +1 -100
- package/dist/core/metadatas/constants.js.map +1 -0
- package/dist/core/metadatas/recordingConstants.js +1 -46
- package/dist/core/metadatas/recordingConstants.js.map +1 -0
- package/dist/core/metadatas/types.js +1 -15
- package/dist/core/metadatas/types.js.map +1 -0
- package/dist/core/onboard/FeedOnboardService.js +1 -422
- package/dist/core/onboard/FeedOnboardService.js.map +1 -0
- package/dist/core/onboard/OnboardProcessor.js +1 -333
- package/dist/core/onboard/OnboardProcessor.js.map +1 -0
- package/dist/core/onboard/OnboardStatus.js +1 -9
- package/dist/core/onboard/OnboardStatus.js.map +1 -0
- package/dist/core/onboard/OnboardStatusManager.js +1 -360
- package/dist/core/onboard/OnboardStatusManager.js.map +1 -0
- package/dist/core/validators/FeedValidator.js +1 -133
- package/dist/core/validators/FeedValidator.js.map +1 -0
- package/dist/core/validators/IServerValidator.js +1 -1
- package/dist/core/validators/IServerValidator.js.map +1 -0
- package/dist/core/validators/SSEServerValidator.js +1 -38
- package/dist/core/validators/SSEServerValidator.js.map +1 -0
- package/dist/core/validators/ServerValidatorFactory.js +1 -44
- package/dist/core/validators/ServerValidatorFactory.js.map +1 -0
- package/dist/core/validators/StdioServerValidator.js +1 -281
- package/dist/core/validators/StdioServerValidator.js.map +1 -0
- package/dist/index.js +1 -18
- package/dist/index.js.map +1 -0
- package/dist/services/InstallationService.js +1 -81
- package/dist/services/InstallationService.js.map +1 -0
- package/dist/services/MCPManager.js +1 -197
- package/dist/services/MCPManager.js.map +1 -0
- package/dist/services/RequirementService.js +1 -548
- package/dist/services/RequirementService.js.map +1 -0
- package/dist/services/ServerService.js +1 -127
- package/dist/services/ServerService.js.map +1 -0
- package/dist/services/TelemetryService.js +1 -53
- package/dist/services/TelemetryService.js.map +1 -0
- package/dist/utils/UpdateCheckTracker.js +1 -79
- package/dist/utils/UpdateCheckTracker.js.map +1 -0
- package/dist/utils/adoUtils.js +1 -254
- package/dist/utils/adoUtils.js.map +1 -0
- package/dist/utils/clientUtils.js +1 -65
- package/dist/utils/clientUtils.js.map +1 -0
- package/dist/utils/feedUtils.js +1 -28
- package/dist/utils/feedUtils.js.map +1 -0
- package/dist/utils/githubAuth.js +1 -177
- package/dist/utils/githubAuth.js.map +1 -0
- package/dist/utils/githubUtils.js +1 -125
- package/dist/utils/githubUtils.js.map +1 -0
- package/dist/utils/logger.js +1 -176
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/macroExpressionUtils.js +1 -93
- package/dist/utils/macroExpressionUtils.js.map +1 -0
- package/dist/utils/osUtils.js +1 -664
- package/dist/utils/osUtils.js.map +1 -0
- package/dist/utils/versionUtils.js +1 -101
- package/dist/utils/versionUtils.js.map +1 -0
- package/dist/web/contract/serverContract.js +1 -1
- package/dist/web/contract/serverContract.js.map +1 -0
- package/dist/web/public/js/api.js +2 -132
- package/dist/web/public/js/api.js.map +1 -0
- package/dist/web/public/js/detailsWidget.js +2 -264
- package/dist/web/public/js/detailsWidget.js.map +1 -0
- package/dist/web/public/js/flights/flights.js +2 -127
- package/dist/web/public/js/flights/flights.js.map +1 -0
- package/dist/web/public/js/modal/index.js +2 -52
- package/dist/web/public/js/modal/index.js.map +1 -0
- package/dist/web/public/js/modal/installModal.js +2 -162
- package/dist/web/public/js/modal/installModal.js.map +1 -0
- package/dist/web/public/js/modal/installation.js +2 -266
- package/dist/web/public/js/modal/installation.js.map +1 -0
- package/dist/web/public/js/modal/loadingModal.js +2 -182
- package/dist/web/public/js/modal/loadingModal.js.map +1 -0
- package/dist/web/public/js/modal/modalSetup.js +2 -595
- package/dist/web/public/js/modal/modalSetup.js.map +1 -0
- package/dist/web/public/js/modal/modalUtils.js +2 -37
- package/dist/web/public/js/modal/modalUtils.js.map +1 -0
- package/dist/web/public/js/modal/versionUtils.js +2 -20
- package/dist/web/public/js/modal/versionUtils.js.map +1 -0
- package/dist/web/public/js/modal.js +2 -42
- package/dist/web/public/js/modal.js.map +1 -0
- package/dist/web/public/js/notifications.js +2 -137
- package/dist/web/public/js/notifications.js.map +1 -0
- package/dist/web/public/js/onboard/formProcessor.js +2 -1037
- package/dist/web/public/js/onboard/formProcessor.js.map +1 -0
- package/dist/web/public/js/onboard/index.js +2 -374
- package/dist/web/public/js/onboard/index.js.map +1 -0
- package/dist/web/public/js/onboard/publishHandler.js +2 -172
- package/dist/web/public/js/onboard/publishHandler.js.map +1 -0
- package/dist/web/public/js/onboard/state.js +2 -76
- package/dist/web/public/js/onboard/state.js.map +1 -0
- package/dist/web/public/js/onboard/templates.js +2 -342
- package/dist/web/public/js/onboard/templates.js.map +1 -0
- package/dist/web/public/js/onboard/uiHandlers.js +2 -1076
- package/dist/web/public/js/onboard/uiHandlers.js.map +1 -0
- package/dist/web/public/js/onboard/validationHandlers.js +2 -493
- package/dist/web/public/js/onboard/validationHandlers.js.map +1 -0
- package/dist/web/public/js/serverCategoryDetails.js +2 -364
- package/dist/web/public/js/serverCategoryDetails.js.map +1 -0
- package/dist/web/public/js/serverCategoryList.js +2 -241
- package/dist/web/public/js/serverCategoryList.js.map +1 -0
- package/dist/web/public/js/settings.js +2 -314
- package/dist/web/public/js/settings.js.map +1 -0
- package/dist/web/server.js +1 -404
- package/dist/web/server.js.map +1 -0
- package/package.json +8 -2
- package/.github/ISSUE_TEMPLATE/JitAccess.yml +0 -28
- package/.github/acl/access.yml +0 -20
- package/.github/compliance/inventory.yml +0 -5
- package/.github/policies/jit.yml +0 -19
- package/.github/workflows/build.yml +0 -28
- package/.roo/rules-code/rules.md +0 -88
- package/dist/cli/commands/start.d.ts +0 -2
- package/dist/cli/commands/start.js +0 -32
- package/dist/cli/commands/sync.d.ts +0 -2
- package/dist/cli/commands/sync.js +0 -17
- package/dist/core/ConfigurationLoader.d.ts +0 -32
- package/dist/core/ConfigurationLoader.js +0 -236
- package/dist/core/ConfigurationProvider.d.ts +0 -35
- package/dist/core/ConfigurationProvider.js +0 -375
- package/dist/core/InstallationService.d.ts +0 -50
- package/dist/core/InstallationService.js +0 -350
- package/dist/core/MCPManager.d.ts +0 -28
- package/dist/core/MCPManager.js +0 -188
- package/dist/core/RequirementService.d.ts +0 -40
- package/dist/core/RequirementService.js +0 -110
- package/dist/core/ServerSchemaLoader.d.ts +0 -11
- package/dist/core/ServerSchemaLoader.js +0 -43
- package/dist/core/ServerSchemaProvider.d.ts +0 -17
- package/dist/core/ServerSchemaProvider.js +0 -120
- package/dist/core/constants.d.ts +0 -47
- package/dist/core/constants.js +0 -94
- package/dist/core/installers/BaseInstaller.d.ts +0 -74
- package/dist/core/installers/BaseInstaller.js +0 -253
- package/dist/core/installers/ClientInstaller.d.ts +0 -23
- package/dist/core/installers/ClientInstaller.js +0 -564
- package/dist/core/installers/CommandInstaller.d.ts +0 -37
- package/dist/core/installers/CommandInstaller.js +0 -173
- package/dist/core/installers/GeneralInstaller.d.ts +0 -33
- package/dist/core/installers/GeneralInstaller.js +0 -85
- package/dist/core/installers/InstallerFactory.d.ts +0 -54
- package/dist/core/installers/InstallerFactory.js +0 -97
- package/dist/core/installers/NpmInstaller.d.ts +0 -26
- package/dist/core/installers/NpmInstaller.js +0 -127
- package/dist/core/installers/PipInstaller.d.ts +0 -28
- package/dist/core/installers/PipInstaller.js +0 -127
- package/dist/core/installers/RequirementInstaller.d.ts +0 -33
- package/dist/core/installers/RequirementInstaller.js +0 -3
- package/dist/core/types.d.ts +0 -166
- package/dist/core/types.js +0 -16
- package/dist/services/InstallRequestValidator.d.ts +0 -21
- package/dist/services/InstallRequestValidator.js +0 -99
- package/dist/web/public/js/modal/installHandler.js +0 -227
- package/dist/web/public/js/modal/loadingUI.js +0 -74
- package/dist/web/public/js/modal/messageQueue.js +0 -112
- package/dist/web/public/js/modal/modalUI.js +0 -214
- package/dist/web/public/js/modal/version.js +0 -20
- package/dist/web/public/js/onboard/ONBOARDING_PAGE_DESIGN.md +0 -370
- package/docs/ONBOARDING_PAGE_DESIGN.md +0 -260
- package/docs/Telemetry.md +0 -136
- package/memory-bank/activeContext.md +0 -26
- package/memory-bank/decisionLog.md +0 -91
- package/memory-bank/productContext.md +0 -41
- package/memory-bank/progress.md +0 -35
- package/memory-bank/systemPatterns.md +0 -10
- package/src/cli/commands/install.ts +0 -139
- package/src/cli/commands/list.ts +0 -113
- package/src/cli/commands/pull.ts +0 -16
- package/src/cli/commands/serve.ts +0 -39
- package/src/cli/commands/uninstall.ts +0 -64
- package/src/cli/index.ts +0 -82
- package/src/core/installers/clients/BaseClientInstaller.ts +0 -341
- package/src/core/installers/clients/ClientInstaller.ts +0 -222
- package/src/core/installers/clients/ClientInstallerFactory.ts +0 -43
- package/src/core/installers/clients/ClineInstaller.ts +0 -35
- package/src/core/installers/clients/ExtensionInstaller.ts +0 -165
- package/src/core/installers/clients/GithubCopilotInstaller.ts +0 -79
- package/src/core/installers/clients/MSRooCodeInstaller.ts +0 -32
- package/src/core/installers/index.ts +0 -11
- package/src/core/installers/requirements/BaseInstaller.ts +0 -85
- package/src/core/installers/requirements/CommandInstaller.ts +0 -231
- package/src/core/installers/requirements/GeneralInstaller.ts +0 -133
- package/src/core/installers/requirements/InstallerFactory.ts +0 -114
- package/src/core/installers/requirements/NpmInstaller.ts +0 -271
- package/src/core/installers/requirements/NugetInstaller.ts +0 -203
- package/src/core/installers/requirements/PipInstaller.ts +0 -207
- package/src/core/installers/requirements/RequirementInstaller.ts +0 -42
- package/src/core/loaders/ConfigurationLoader.ts +0 -298
- package/src/core/loaders/ConfigurationProvider.ts +0 -462
- package/src/core/loaders/InstallOperationManager.ts +0 -367
- package/src/core/loaders/ServerSchemaLoader.ts +0 -117
- package/src/core/loaders/ServerSchemaProvider.ts +0 -99
- package/src/core/loaders/SystemSettingsManager.ts +0 -278
- package/src/core/metadatas/constants.ts +0 -122
- package/src/core/metadatas/recordingConstants.ts +0 -65
- package/src/core/metadatas/types.ts +0 -202
- package/src/core/onboard/FeedOnboardService.ts +0 -501
- package/src/core/onboard/OnboardProcessor.ts +0 -356
- package/src/core/onboard/OnboardStatus.ts +0 -60
- package/src/core/onboard/OnboardStatusManager.ts +0 -416
- package/src/core/validators/FeedValidator.ts +0 -135
- package/src/core/validators/IServerValidator.ts +0 -21
- package/src/core/validators/SSEServerValidator.ts +0 -43
- package/src/core/validators/ServerValidatorFactory.ts +0 -51
- package/src/core/validators/StdioServerValidator.ts +0 -313
- package/src/index.ts +0 -44
- package/src/services/InstallationService.ts +0 -102
- package/src/services/MCPManager.ts +0 -249
- package/src/services/RequirementService.ts +0 -627
- package/src/services/ServerService.ts +0 -161
- package/src/services/TelemetryService.ts +0 -59
- package/src/utils/UpdateCheckTracker.ts +0 -86
- package/src/utils/adoUtils.ts +0 -293
- package/src/utils/clientUtils.ts +0 -72
- package/src/utils/feedUtils.ts +0 -31
- package/src/utils/githubAuth.ts +0 -212
- package/src/utils/githubUtils.ts +0 -164
- package/src/utils/logger.ts +0 -195
- package/src/utils/macroExpressionUtils.ts +0 -104
- package/src/utils/osUtils.ts +0 -700
- package/src/utils/versionUtils.ts +0 -114
- package/src/web/contract/serverContract.ts +0 -74
- package/src/web/public/css/detailsWidget.css +0 -235
- package/src/web/public/css/modal.css +0 -757
- package/src/web/public/css/notifications.css +0 -101
- package/src/web/public/css/onboard.css +0 -107
- package/src/web/public/css/serverCategoryList.css +0 -120
- package/src/web/public/css/serverDetails.css +0 -139
- package/src/web/public/index.html +0 -359
- package/src/web/public/js/api.js +0 -132
- package/src/web/public/js/detailsWidget.js +0 -264
- package/src/web/public/js/flights/flights.js +0 -127
- package/src/web/public/js/modal/index.js +0 -52
- package/src/web/public/js/modal/installModal.js +0 -162
- package/src/web/public/js/modal/installation.js +0 -266
- package/src/web/public/js/modal/loadingModal.js +0 -182
- package/src/web/public/js/modal/modalSetup.js +0 -595
- package/src/web/public/js/modal/modalUtils.js +0 -37
- package/src/web/public/js/modal/versionUtils.js +0 -20
- package/src/web/public/js/modal.js +0 -42
- package/src/web/public/js/notifications.js +0 -137
- package/src/web/public/js/onboard/formProcessor.js +0 -1037
- package/src/web/public/js/onboard/index.js +0 -374
- package/src/web/public/js/onboard/publishHandler.js +0 -172
- package/src/web/public/js/onboard/state.js +0 -76
- package/src/web/public/js/onboard/templates.js +0 -342
- package/src/web/public/js/onboard/uiHandlers.js +0 -1076
- package/src/web/public/js/onboard/validationHandlers.js +0 -493
- package/src/web/public/js/serverCategoryDetails.js +0 -364
- package/src/web/public/js/serverCategoryList.js +0 -241
- package/src/web/public/js/settings.js +0 -314
- package/src/web/public/modal.html +0 -84
- package/src/web/public/onboard.html +0 -296
- package/src/web/public/settings.html +0 -135
- package/src/web/public/styles.css +0 -277
- package/src/web/server.ts +0 -478
- package/tsconfig.json +0 -18
- package/wiki/Installation.md +0 -3
- package/wiki/Publish.md +0 -3
|
@@ -1,1037 +0,0 @@
|
|
|
1
|
-
// Form data processing and API calls
|
|
2
|
-
import { showToast } from '../notifications.js';
|
|
3
|
-
import { state, setServerCounter, getServerCounter, clearEnvCountersForTab, clearServerRequirementCountersForTab } from './state.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Adds real-time validation to form inputs.
|
|
7
|
-
* @param {string} formId - The ID of the form to add validation to.
|
|
8
|
-
*/
|
|
9
|
-
function setupRealTimeValidation(formId) {
|
|
10
|
-
const form = document.getElementById(formId);
|
|
11
|
-
if (!form) return;
|
|
12
|
-
|
|
13
|
-
if (!form) return;
|
|
14
|
-
|
|
15
|
-
// Name input validation (category or server)
|
|
16
|
-
const nameInput = form.querySelector('input[name="name"]');
|
|
17
|
-
if (nameInput) {
|
|
18
|
-
nameInput.addEventListener('input', (e) => {
|
|
19
|
-
const value = e.target.value.trim();
|
|
20
|
-
if (!value) {
|
|
21
|
-
showValidationMessage(e.target, 'Name is required');
|
|
22
|
-
} else if (!/^[a-zA-Z0-9-_]+$/.test(value)) {
|
|
23
|
-
showValidationMessage(e.target, 'Only alphanumeric characters, hyphens, and underscores allowed');
|
|
24
|
-
} else if (value.length > 50) {
|
|
25
|
-
showValidationMessage(e.target, 'Must not exceed 50 characters');
|
|
26
|
-
} else {
|
|
27
|
-
showValidationMessage(e.target, 'Valid name', false);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Display name validation
|
|
33
|
-
const displayNameInput = form.querySelector('input[name="displayName"]');
|
|
34
|
-
if (displayNameInput) {
|
|
35
|
-
displayNameInput.addEventListener('input', (e) => {
|
|
36
|
-
const value = e.target.value.trim();
|
|
37
|
-
if (!value) {
|
|
38
|
-
showValidationMessage(e.target, 'Display name is required');
|
|
39
|
-
} else if (value.length > 100) {
|
|
40
|
-
showValidationMessage(e.target, 'Must not exceed 100 characters');
|
|
41
|
-
} else {
|
|
42
|
-
showValidationMessage(e.target, 'Valid display name', false);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Description validation
|
|
48
|
-
const descriptionInput = form.querySelector('textarea[name="description"]');
|
|
49
|
-
if (descriptionInput) {
|
|
50
|
-
descriptionInput.addEventListener('input', (e) => {
|
|
51
|
-
const value = e.target.value.trim();
|
|
52
|
-
if (value.length > 500) {
|
|
53
|
-
showValidationMessage(e.target, 'Must not exceed 500 characters');
|
|
54
|
-
} else if (value.length === 0) {
|
|
55
|
-
showValidationMessage(e.target, 'Description is required');
|
|
56
|
-
} else {
|
|
57
|
-
showValidationMessage(e.target, 'Valid description', false);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Repository URL validation
|
|
63
|
-
const repoInput = form.querySelector('input[name="repository"]');
|
|
64
|
-
if (repoInput) {
|
|
65
|
-
repoInput.addEventListener('input', (e) => {
|
|
66
|
-
const value = e.target.value.trim();
|
|
67
|
-
if (value) {
|
|
68
|
-
try {
|
|
69
|
-
new URL(value);
|
|
70
|
-
showValidationMessage(e.target, 'Valid URL', false);
|
|
71
|
-
} catch (err) {
|
|
72
|
-
showValidationMessage(e.target, 'Invalid URL format');
|
|
73
|
-
}
|
|
74
|
-
} else {
|
|
75
|
-
e.target.nextElementSibling?.remove(); // Remove validation message if empty
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Server mode validation
|
|
81
|
-
form.addEventListener('change', (e) => {
|
|
82
|
-
if (e.target.name?.match(/servers\[\d+\]\.mode/)) {
|
|
83
|
-
const serverIndex = e.target.name.match(/servers\[(\d+)\]/)[1];
|
|
84
|
-
const commandInput = form.querySelector(`[name="servers[${serverIndex}].installation.command"]`);
|
|
85
|
-
const urlInput = form.querySelector(`[name="servers[${serverIndex}].installation.url"]`);
|
|
86
|
-
const argsInput = form.querySelector(`[name="servers[${serverIndex}].installation.args"]`);
|
|
87
|
-
|
|
88
|
-
if (e.target.value === 'stdio') {
|
|
89
|
-
if (commandInput) {
|
|
90
|
-
commandInput.required = true;
|
|
91
|
-
showValidationMessage(commandInput, 'Command is required for stdio mode');
|
|
92
|
-
}
|
|
93
|
-
if (urlInput) {
|
|
94
|
-
urlInput.required = false;
|
|
95
|
-
urlInput.value = '';
|
|
96
|
-
urlInput.nextElementSibling?.remove();
|
|
97
|
-
}
|
|
98
|
-
} else if (e.target.value === 'sse') {
|
|
99
|
-
if (urlInput) {
|
|
100
|
-
urlInput.required = true;
|
|
101
|
-
showValidationMessage(urlInput, 'URL is required for sse mode');
|
|
102
|
-
}
|
|
103
|
-
if (commandInput) {
|
|
104
|
-
commandInput.required = false;
|
|
105
|
-
commandInput.value = '';
|
|
106
|
-
commandInput.nextElementSibling?.remove();
|
|
107
|
-
}
|
|
108
|
-
if (argsInput) {
|
|
109
|
-
argsInput.value = '';
|
|
110
|
-
argsInput.nextElementSibling?.remove();
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Environment variable validation
|
|
117
|
-
form.addEventListener('input', (e) => {
|
|
118
|
-
if (e.target.name?.includes('.installation.env[')) {
|
|
119
|
-
const matches = e.target.name.match(/servers\[(\d+)\]\.installation\.env\[(\d+)\]\.(name|default)/);
|
|
120
|
-
if (matches) {
|
|
121
|
-
const [, serverIndex, envIndex, field] = matches;
|
|
122
|
-
if (field === 'name') {
|
|
123
|
-
const value = e.target.value.trim();
|
|
124
|
-
if (!value) {
|
|
125
|
-
showValidationMessage(e.target, 'Environment variable name is required');
|
|
126
|
-
} else if (!/^[A-Z_][A-Z0-9_]*$/.test(value)) {
|
|
127
|
-
showValidationMessage(e.target, 'Must be uppercase with only letters, numbers, and underscores');
|
|
128
|
-
} else if (value.length > 50) {
|
|
129
|
-
showValidationMessage(e.target, 'Must not exceed 50 characters');
|
|
130
|
-
} else {
|
|
131
|
-
showValidationMessage(e.target, 'Valid name', false);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Submits the form data for onboarding or updating a server category.
|
|
141
|
-
* @param {Event} event - The form submission event.
|
|
142
|
-
* @param {string} activeTab - Identifier for the active tab ('create-category' or 'create-server').
|
|
143
|
-
* @param {object|null} currentSelectedCategoryData - Full FeedConfiguration of the selected category if activeTab is 'create-server'.
|
|
144
|
-
*/
|
|
145
|
-
export async function submitForm(event, activeTab, currentSelectedCategoryData = null) {
|
|
146
|
-
event.preventDefault();
|
|
147
|
-
const formElement = event.target;
|
|
148
|
-
let feedConfiguration;
|
|
149
|
-
let isUpdateOperation = false;
|
|
150
|
-
|
|
151
|
-
if (activeTab === 'create-category') {
|
|
152
|
-
try {
|
|
153
|
-
feedConfiguration = formDataToFeedConfiguration(formElement);
|
|
154
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
155
|
-
isUpdateOperation = urlParams.get('action') === 'edit' && urlParams.get('category') === feedConfiguration.name;
|
|
156
|
-
} catch (validationError) {
|
|
157
|
-
console.error('Validation error:', validationError);
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
} else if (activeTab === 'create-server') {
|
|
161
|
-
if (!currentSelectedCategoryData || !currentSelectedCategoryData.name) {
|
|
162
|
-
showToast('No existing category selected or category data is missing.', 'error');
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Get the complete state of servers and their requirements from the current form.
|
|
167
|
-
// The `true` flag for `forExistingCategoryTab` tells formDataToFeedConfiguration
|
|
168
|
-
// to only process server data and not category-level fields from this form.
|
|
169
|
-
// It should return ALL servers currently in this form, including modified adhoc and new ones.
|
|
170
|
-
const formDerivedData = formDataToFeedConfiguration(formElement, true, currentSelectedCategoryData);
|
|
171
|
-
|
|
172
|
-
// Start with a deep clone of the original category data (for name, description, etc.)
|
|
173
|
-
feedConfiguration = JSON.parse(JSON.stringify(currentSelectedCategoryData));
|
|
174
|
-
|
|
175
|
-
// Replace the mcpServers list entirely with what was parsed from the form.
|
|
176
|
-
// This ensures that the list reflects the current state of the form (original read-only, modified adhoc, new).
|
|
177
|
-
feedConfiguration.mcpServers = formDerivedData.mcpServers || [];
|
|
178
|
-
|
|
179
|
-
// Merge requirements: start with original requirements, then add any new ones from the form.
|
|
180
|
-
// `formDerivedData.requirements` should ideally only contain requirements introduced by
|
|
181
|
-
// servers in the current form that are not already in `currentSelectedCategoryData.requirements`.
|
|
182
|
-
// The existing logic for merging requirements seems okay if `formDerivedData.requirements` is correctly populated.
|
|
183
|
-
const existingReqKeys = new Set((feedConfiguration.requirements || []).map(r => `${r.type}|${r.name}|${r.version}`));
|
|
184
|
-
(formDerivedData.requirements || []).forEach(newReq => {
|
|
185
|
-
const reqKey = `${newReq.type}|${newReq.name}|${newReq.version}`;
|
|
186
|
-
if (!existingReqKeys.has(reqKey)) {
|
|
187
|
-
feedConfiguration.requirements.push(newReq);
|
|
188
|
-
existingReqKeys.add(reqKey);
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
isUpdateOperation = true;
|
|
192
|
-
} else {
|
|
193
|
-
showToast('Invalid tab context for submission.', 'error');
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
const response = await fetch('/api/categories/onboard', {
|
|
199
|
-
method: 'POST',
|
|
200
|
-
headers: { 'Content-Type': 'application/json' },
|
|
201
|
-
body: JSON.stringify({
|
|
202
|
-
categoryData: feedConfiguration,
|
|
203
|
-
isUpdate: isUpdateOperation
|
|
204
|
-
})
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
if (!response.ok) {
|
|
208
|
-
const errorData = await response.json().catch(() => ({ error: 'Failed to parse error response' }));
|
|
209
|
-
throw new Error(`HTTP error! status: ${response.status}, message: ${errorData.error || response.statusText}`);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const result = await response.json();
|
|
213
|
-
// console.log('Form submitted successfully:', result);
|
|
214
|
-
showToast('Form submitted successfully! See console for operation details.', 'success');
|
|
215
|
-
} catch (error) {
|
|
216
|
-
console.error('Error submitting form:', error);
|
|
217
|
-
showToast(`Error submitting form: ${error.message}. Please try again.`, 'error');
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Retrieves form data from a given HTMLFormElement.
|
|
223
|
-
* @param {HTMLFormElement} formElement - The form element to process.
|
|
224
|
-
* @param {boolean} [forExistingCategoryTab=false] - True if processing for the "Create Server in Existing Category" tab.
|
|
225
|
-
* @param {object|null} [baseCategoryData=null] - The base FeedConfiguration of the selected category (used if forExistingCategoryTab is true).
|
|
226
|
-
* @returns {object} The FeedConfiguration object (or partial for new servers if forExistingCategoryTab is true).
|
|
227
|
-
*/
|
|
228
|
-
export function getFormData(formElement, forExistingCategoryTab = false, baseCategoryData = null) {
|
|
229
|
-
return formDataToFeedConfiguration(formElement, forExistingCategoryTab, baseCategoryData);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Converts form data to a FeedConfiguration object.
|
|
234
|
-
* @param {HTMLFormElement} formElement - The form element.
|
|
235
|
-
* @param {boolean} [forExistingCategoryTab=false] - True if processing for the "Create Server in Existing Category" tab.
|
|
236
|
-
* @param {object|null} [baseCategoryData=null] - The base FeedConfiguration of the selected category.
|
|
237
|
-
* @returns {object} The FeedConfiguration object. If forExistingCategoryTab is true, this will be a partial
|
|
238
|
-
* FeedConfiguration containing only newly added servers and their requirements.
|
|
239
|
-
*/
|
|
240
|
-
function validateServerConfig(serverConfig) {
|
|
241
|
-
const errors = [];
|
|
242
|
-
|
|
243
|
-
// Validate required fields
|
|
244
|
-
if (!serverConfig.name) {
|
|
245
|
-
errors.push('Server name is required');
|
|
246
|
-
} else if (!/^[a-zA-Z0-9-_]+$/.test(serverConfig.name)) {
|
|
247
|
-
errors.push('Server name must only contain alphanumeric characters, hyphens, and underscores');
|
|
248
|
-
} else if (serverConfig.name.length > 50) {
|
|
249
|
-
errors.push('Server name must not exceed 50 characters');
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (!serverConfig.description || serverConfig.description.trim() === '') {
|
|
253
|
-
errors.push('Server description is required');
|
|
254
|
-
} else if (serverConfig.description.length > 200) {
|
|
255
|
-
errors.push('Server description must not exceed 200 characters');
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (!serverConfig.mode) {
|
|
259
|
-
errors.push('Server mode is required');
|
|
260
|
-
} else if (!['stdio', 'sse'].includes(serverConfig.mode)) {
|
|
261
|
-
errors.push('Server mode must be either "stdio" or "sse"');
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Validate installation config based on mode
|
|
265
|
-
if (serverConfig.mode === 'stdio') {
|
|
266
|
-
if (!serverConfig.installation?.command) {
|
|
267
|
-
errors.push('Command is required for stdio server');
|
|
268
|
-
} else if (!/^[a-zA-Z0-9-_.\\/]+$/.test(serverConfig.installation.command)) {
|
|
269
|
-
errors.push('Command must only contain alphanumeric characters, hyphens, underscores, dots, and slashes');
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Validate arguments format if present
|
|
273
|
-
if (serverConfig.installation?.args?.length > 0) {
|
|
274
|
-
serverConfig.installation.args.forEach((arg, index) => {
|
|
275
|
-
if (!/^[a-zA-Z0-9-_./\\]+$/.test(arg)) {
|
|
276
|
-
errors.push(`Argument ${index + 1} must only contain alphanumeric characters, hyphens, underscores, dots, and slashes`);
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
} else if (serverConfig.mode === 'sse') {
|
|
281
|
-
if (!serverConfig.installation?.url) {
|
|
282
|
-
errors.push('URL is required for sse server');
|
|
283
|
-
} else {
|
|
284
|
-
try {
|
|
285
|
-
new URL(serverConfig.installation.url);
|
|
286
|
-
} catch (e) {
|
|
287
|
-
errors.push('Invalid URL format');
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Validate env variables if present
|
|
293
|
-
if (serverConfig.installation?.env) {
|
|
294
|
-
Object.entries(serverConfig.installation.env).forEach(([name, config]) => {
|
|
295
|
-
if (!name) {
|
|
296
|
-
errors.push('Environment variable name is required');
|
|
297
|
-
} else if (!/^[A-Z_][A-Z0-9_]*$/.test(name)) {
|
|
298
|
-
errors.push(`Environment variable name "${name}" must be uppercase with only letters, numbers, and underscores`);
|
|
299
|
-
} else if (name.length > 50) {
|
|
300
|
-
errors.push(`Environment variable name "${name}" must not exceed 50 characters`);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (config.Required && !config.Default) {
|
|
304
|
-
errors.push(`Required environment variable "${name}" should have a default value`);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
if (config.Description && config.Description.length > 200) {
|
|
308
|
-
errors.push(`Description for environment variable "${name}" must not exceed 200 characters`);
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Validate schemas if present
|
|
314
|
-
if (serverConfig.schemas) {
|
|
315
|
-
try {
|
|
316
|
-
JSON.parse(serverConfig.schemas);
|
|
317
|
-
} catch (e) {
|
|
318
|
-
errors.push('Invalid JSON format in schemas');
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
return errors;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
export function formDataToFeedConfiguration(formElement, forExistingCategoryTab = false, baseCategoryData = null) {
|
|
326
|
-
const currentFormData = new FormData(formElement);
|
|
327
|
-
const feedConfiguration = {
|
|
328
|
-
requirements: [],
|
|
329
|
-
mcpServers: []
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
let serversListId;
|
|
333
|
-
if (formElement.id === 'onboardForm') {
|
|
334
|
-
serversListId = 'serversList';
|
|
335
|
-
} else if (formElement.id === 'onboardServerForm') {
|
|
336
|
-
serversListId = 'existingCategoryServersList';
|
|
337
|
-
} else {
|
|
338
|
-
console.warn('[formDataToFeedConfiguration] Could not determine serversListId from formElement.id:', formElement.id);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (!forExistingCategoryTab) {
|
|
342
|
-
feedConfiguration.name = currentFormData.get('name') || '';
|
|
343
|
-
feedConfiguration.displayName = currentFormData.get('displayName') || '';
|
|
344
|
-
feedConfiguration.description = currentFormData.get('description') || '';
|
|
345
|
-
feedConfiguration.repository = currentFormData.get('repository') || undefined;
|
|
346
|
-
} else if (baseCategoryData) {
|
|
347
|
-
feedConfiguration.name = baseCategoryData.name;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const serverDataMap = new Map();
|
|
351
|
-
const globalRequirementsMap = new Map();
|
|
352
|
-
|
|
353
|
-
for (const [key, value] of currentFormData.entries()) {
|
|
354
|
-
if (forExistingCategoryTab && !key.startsWith('servers[')) {
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const serverMatchKey = key.match(/^servers\[(\d+)\]\.(.+)$/);
|
|
359
|
-
if (serverMatchKey) {
|
|
360
|
-
let serverIndexInForm = parseInt(serverMatchKey[1], 10);
|
|
361
|
-
const fieldPathFromMatch = serverMatchKey[2];
|
|
362
|
-
let actualServerDataIndex = serverIndexInForm;
|
|
363
|
-
|
|
364
|
-
if (!serverDataMap.has(actualServerDataIndex)) {
|
|
365
|
-
serverDataMap.set(actualServerDataIndex, {
|
|
366
|
-
installation: { env: new Map() },
|
|
367
|
-
dependencies: { requirements: new Map() },
|
|
368
|
-
isNew: true // Assuming all servers processed this way are new additions to the form
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
const currentServerData = serverDataMap.get(actualServerDataIndex);
|
|
372
|
-
|
|
373
|
-
if (fieldPathFromMatch === 'name') currentServerData.name = value;
|
|
374
|
-
else if (fieldPathFromMatch === 'description') currentServerData.description = value;
|
|
375
|
-
else if (fieldPathFromMatch === 'mode') currentServerData.mode = value;
|
|
376
|
-
else if (fieldPathFromMatch === 'repository') currentServerData.repository = value || undefined;
|
|
377
|
-
else if (fieldPathFromMatch === 'schemas') currentServerData.schemas = value || undefined;
|
|
378
|
-
else if (fieldPathFromMatch === 'installation.command') currentServerData.installation.command = value;
|
|
379
|
-
else if (fieldPathFromMatch === 'installation.args') {
|
|
380
|
-
currentServerData.installation.args = value ? value.split(',').map(arg => arg.trim()).filter(arg => arg) : [];
|
|
381
|
-
}
|
|
382
|
-
else if (fieldPathFromMatch === 'installation.url') {
|
|
383
|
-
currentServerData.installation.url = value;
|
|
384
|
-
}
|
|
385
|
-
else if (fieldPathFromMatch.startsWith('installation.env[')) {
|
|
386
|
-
const envMatch = fieldPathFromMatch.match(/^installation\.env\[(\d+)\]\.(name|default|required|description)$/);
|
|
387
|
-
if (envMatch) {
|
|
388
|
-
const envIndex = parseInt(envMatch[1], 10);
|
|
389
|
-
const envField = envMatch[2];
|
|
390
|
-
if (!currentServerData.installation.env.has(envIndex)) {
|
|
391
|
-
currentServerData.installation.env.set(envIndex, {});
|
|
392
|
-
}
|
|
393
|
-
const currentEnv = currentServerData.installation.env.get(envIndex);
|
|
394
|
-
if (envField === 'required') {
|
|
395
|
-
currentEnv[envField] = currentFormData.get(key) === 'on';
|
|
396
|
-
} else {
|
|
397
|
-
currentEnv[envField] = value || undefined;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
else if (fieldPathFromMatch.startsWith('requirements[')) {
|
|
402
|
-
const reqMatch = fieldPathFromMatch.match(/^requirements\[(\d+)\]\.(.+)$/);
|
|
403
|
-
if (reqMatch) {
|
|
404
|
-
const reqIndex = parseInt(reqMatch[1], 10);
|
|
405
|
-
const reqFieldPath = reqMatch[2];
|
|
406
|
-
|
|
407
|
-
if (!currentServerData.dependencies.requirements.has(reqIndex)) {
|
|
408
|
-
currentServerData.dependencies.requirements.set(reqIndex, { registry: {} });
|
|
409
|
-
}
|
|
410
|
-
const currentReq = currentServerData.dependencies.requirements.get(reqIndex);
|
|
411
|
-
|
|
412
|
-
if (reqFieldPath === 'name') currentReq.name = value;
|
|
413
|
-
else if (reqFieldPath === 'type') currentReq.type = value;
|
|
414
|
-
else if (reqFieldPath === 'version') currentReq.version = value;
|
|
415
|
-
else if (reqFieldPath === 'order') currentReq.order = value ? parseInt(value, 10) : undefined;
|
|
416
|
-
else if (reqFieldPath === 'alias') currentReq.alias = value || undefined;
|
|
417
|
-
else if (reqFieldPath === 'registryType') {
|
|
418
|
-
currentReq.registryType = value;
|
|
419
|
-
} else if (reqFieldPath.startsWith('registry.githubRelease.')) {
|
|
420
|
-
currentReq.registry.githubRelease = currentReq.registry.githubRelease || {};
|
|
421
|
-
currentReq.registry.githubRelease[reqFieldPath.substring('registry.githubRelease.'.length)] = value || undefined;
|
|
422
|
-
} else if (reqFieldPath.startsWith('registry.artifacts.')) {
|
|
423
|
-
currentReq.registry.artifacts = currentReq.registry.artifacts || {};
|
|
424
|
-
currentReq.registry.artifacts[reqFieldPath.substring('registry.artifacts.'.length)] = value || undefined;
|
|
425
|
-
} else if (reqFieldPath.startsWith('registry.local.')) {
|
|
426
|
-
currentReq.registry.local = currentReq.registry.local || {};
|
|
427
|
-
currentReq.registry.local[reqFieldPath.substring('registry.local.'.length)] = value || undefined;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// console.log('[formDataToFeedConfiguration] serverDataMap keys before processing:', Array.from(serverDataMap.keys())); // DEBUG
|
|
435
|
-
// console.log('[formDataToFeedConfiguration] serverDataMap size:', serverDataMap.size); // DEBUG
|
|
436
|
-
|
|
437
|
-
// Process servers found in the form data (new/adhoc or edited original servers)
|
|
438
|
-
serverDataMap.forEach((serverRaw, serverIndex) => {
|
|
439
|
-
let mcpServer;
|
|
440
|
-
// ... (existing logic to build mcpServer from serverRaw)
|
|
441
|
-
if (serverRaw.mode === 'sse') {
|
|
442
|
-
mcpServer = {
|
|
443
|
-
name: serverRaw.name,
|
|
444
|
-
description: serverRaw.description,
|
|
445
|
-
mode: serverRaw.mode,
|
|
446
|
-
schemas: serverRaw.schemas,
|
|
447
|
-
repository: serverRaw.repository,
|
|
448
|
-
installation: {
|
|
449
|
-
url: serverRaw.installation.url || ''
|
|
450
|
-
},
|
|
451
|
-
dependencies: {
|
|
452
|
-
requirements: []
|
|
453
|
-
}
|
|
454
|
-
};
|
|
455
|
-
} else { // stdio or other modes
|
|
456
|
-
mcpServer = {
|
|
457
|
-
name: serverRaw.name,
|
|
458
|
-
description: serverRaw.description,
|
|
459
|
-
mode: serverRaw.mode,
|
|
460
|
-
schemas: serverRaw.schemas,
|
|
461
|
-
repository: serverRaw.repository,
|
|
462
|
-
installation: {
|
|
463
|
-
command: serverRaw.installation.command,
|
|
464
|
-
args: serverRaw.installation.args || [],
|
|
465
|
-
env: {} // Initialize env
|
|
466
|
-
},
|
|
467
|
-
dependencies: {
|
|
468
|
-
requirements: []
|
|
469
|
-
}
|
|
470
|
-
};
|
|
471
|
-
|
|
472
|
-
const envVars = {};
|
|
473
|
-
serverRaw.installation.env.forEach(env => {
|
|
474
|
-
if (env.name) {
|
|
475
|
-
envVars[env.name] = {
|
|
476
|
-
Required: env.required || false,
|
|
477
|
-
Description: env.description || '',
|
|
478
|
-
Default: env.default || undefined
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
if (Object.keys(envVars).length > 0) {
|
|
483
|
-
mcpServer.installation.env = envVars;
|
|
484
|
-
} else {
|
|
485
|
-
delete mcpServer.installation.env; // Clean up if no env vars
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
serverRaw.dependencies.requirements.forEach(reqRaw => {
|
|
490
|
-
if (!reqRaw.name || !reqRaw.type || !reqRaw.version) return;
|
|
491
|
-
|
|
492
|
-
const fullRequirementConfig = {
|
|
493
|
-
name: reqRaw.name,
|
|
494
|
-
type: reqRaw.type,
|
|
495
|
-
version: reqRaw.version,
|
|
496
|
-
alias: reqRaw.alias,
|
|
497
|
-
order: reqRaw.order,
|
|
498
|
-
};
|
|
499
|
-
|
|
500
|
-
if (reqRaw.registryType && reqRaw.registryType !== 'public') {
|
|
501
|
-
fullRequirementConfig.registry = {};
|
|
502
|
-
if (reqRaw.registryType === 'github' && reqRaw.registry.githubRelease) {
|
|
503
|
-
fullRequirementConfig.registry.githubRelease = {
|
|
504
|
-
repository: reqRaw.registry.githubRelease.repository,
|
|
505
|
-
assetsName: reqRaw.registry.githubRelease.assetsName,
|
|
506
|
-
assetName: reqRaw.registry.githubRelease.assetName,
|
|
507
|
-
};
|
|
508
|
-
} else if (reqRaw.registryType === 'artifacts' && reqRaw.registry.artifacts) {
|
|
509
|
-
fullRequirementConfig.registry.artifacts = {
|
|
510
|
-
registryUrl: reqRaw.registry.artifacts.registryUrl,
|
|
511
|
-
registryName: reqRaw.registry.artifacts.registryName,
|
|
512
|
-
};
|
|
513
|
-
} else if (reqRaw.registryType === 'local' && reqRaw.registry.local) {
|
|
514
|
-
fullRequirementConfig.registry.local = {
|
|
515
|
-
localPath: reqRaw.registry.local.localPath,
|
|
516
|
-
assetName: reqRaw.registry.local.assetName,
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
// Clean up empty registry objects
|
|
520
|
-
if (fullRequirementConfig.registry.githubRelease && !fullRequirementConfig.registry.githubRelease.repository) delete fullRequirementConfig.registry.githubRelease;
|
|
521
|
-
if (fullRequirementConfig.registry.artifacts && !fullRequirementConfig.registry.artifacts.registryUrl) delete fullRequirementConfig.registry.artifacts;
|
|
522
|
-
if (fullRequirementConfig.registry.local && !fullRequirementConfig.registry.local.localPath) delete fullRequirementConfig.registry.local;
|
|
523
|
-
if (Object.keys(fullRequirementConfig.registry).length === 0) delete fullRequirementConfig.registry;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
mcpServer.dependencies.requirements.push({
|
|
527
|
-
name: reqRaw.name,
|
|
528
|
-
version: reqRaw.version,
|
|
529
|
-
order: reqRaw.order
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
const reqKey = `${reqRaw.type}|${reqRaw.name}|${reqRaw.version}`;
|
|
533
|
-
if (!globalRequirementsMap.has(reqKey)) {
|
|
534
|
-
const { order, ...reqConfigForGlobal } = fullRequirementConfig; // Exclude order for global list
|
|
535
|
-
globalRequirementsMap.set(reqKey, reqConfigForGlobal);
|
|
536
|
-
}
|
|
537
|
-
});
|
|
538
|
-
if (mcpServer.dependencies.requirements.length === 0) {
|
|
539
|
-
delete mcpServer.dependencies; // Clean up if no requirements
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// Attempt to retrieve and add systemTags from the DOM element,
|
|
543
|
-
// ONLY if we are in the 'Create Server in Existing Category' tab context.
|
|
544
|
-
// For 'Create New Category' tab, systemTags should not be assigned from DOM.
|
|
545
|
-
if (formElement.id === 'onboardServerForm' && serversListId) {
|
|
546
|
-
const selector = `#${serversListId} .server-item[data-index="${serverIndex}"]`;
|
|
547
|
-
const serverItemElement = document.querySelector(selector);
|
|
548
|
-
if (serverItemElement && serverItemElement.dataset.systemTags) {
|
|
549
|
-
try {
|
|
550
|
-
mcpServer.systemTags = JSON.parse(serverItemElement.dataset.systemTags);
|
|
551
|
-
} catch (e) {
|
|
552
|
-
console.error(`[formDataToFeedConfiguration] Error parsing systemTags for server index ${serverIndex} ('${mcpServer.name}') on ${formElement.id}:`, e, serverItemElement.dataset.systemTags);
|
|
553
|
-
mcpServer.systemTags = { parseError: true };
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
// If no dataset.systemTags or not onboardServerForm, mcpServer.systemTags remains undefined.
|
|
557
|
-
} else {
|
|
558
|
-
// Ensure systemTags is not carried over if not in the correct context or not present in dataset
|
|
559
|
-
delete mcpServer.systemTags;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
feedConfiguration.mcpServers.push(mcpServer);
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
// If processing for an existing category, ensure original non-adhoc servers are included
|
|
567
|
-
// if they weren't picked up by the form (e.g., because they were read-only and disabled).
|
|
568
|
-
if (forExistingCategoryTab && baseCategoryData && Array.isArray(baseCategoryData.mcpServers)) {
|
|
569
|
-
baseCategoryData.mcpServers.forEach(originalServer => {
|
|
570
|
-
// Check if this original server (by name) is already in our processed list.
|
|
571
|
-
// We use name as the primary identifier for existing servers.
|
|
572
|
-
const isAlreadyProcessed = feedConfiguration.mcpServers.some(
|
|
573
|
-
processedServer => processedServer.name === originalServer.name
|
|
574
|
-
);
|
|
575
|
-
|
|
576
|
-
if (!isAlreadyProcessed && (!originalServer.systemTags || originalServer.systemTags.adhoc !== 'true')) {
|
|
577
|
-
// This original server was not in the form data (likely read-only) and is not adhoc. Add it.
|
|
578
|
-
// Ensure its requirements are also added to the global list if not already present.
|
|
579
|
-
feedConfiguration.mcpServers.push(JSON.parse(JSON.stringify(originalServer))); // Add a clone
|
|
580
|
-
|
|
581
|
-
if (originalServer.dependencies && Array.isArray(originalServer.dependencies.requirements)) {
|
|
582
|
-
originalServer.dependencies.requirements.forEach(req => {
|
|
583
|
-
// We need the full requirement definition for the global map.
|
|
584
|
-
// This might require looking up the full definition from baseCategoryData.requirements
|
|
585
|
-
// if originalServer.dependencies.requirements only has name/version/order.
|
|
586
|
-
// For simplicity here, we assume baseCategoryData.requirements contains full definitions.
|
|
587
|
-
const originalGlobalReq = baseCategoryData.requirements?.find(
|
|
588
|
-
gReq => gReq.name === req.name && gReq.type && gReq.version === req.version // Type might not be in server's dep list
|
|
589
|
-
);
|
|
590
|
-
|
|
591
|
-
if (originalGlobalReq) {
|
|
592
|
-
const reqKey = `${originalGlobalReq.type}|${originalGlobalReq.name}|${originalGlobalReq.version}`;
|
|
593
|
-
if (!globalRequirementsMap.has(reqKey)) {
|
|
594
|
-
globalRequirementsMap.set(reqKey, JSON.parse(JSON.stringify(originalGlobalReq)));
|
|
595
|
-
}
|
|
596
|
-
} else {
|
|
597
|
-
// Fallback if full definition not found, add what we have, though type might be missing.
|
|
598
|
-
// This part might need refinement based on actual structure of baseCategoryData.requirements
|
|
599
|
-
// and how server-specific dependencies link to global ones.
|
|
600
|
-
// The current server-specific req usually has name, version, order. Type is global.
|
|
601
|
-
// We need to find the type from the global requirements list.
|
|
602
|
-
// This logic assumes that if a server has a dependency, its full definition (including type)
|
|
603
|
-
// must exist in the global `baseCategoryData.requirements`.
|
|
604
|
-
|
|
605
|
-
// Let's find the type from baseCategoryData.requirements based on name and version
|
|
606
|
-
const matchingGlobalReqForType = baseCategoryData.requirements?.find(
|
|
607
|
-
gReq => gReq.name === req.name && gReq.version === req.version
|
|
608
|
-
);
|
|
609
|
-
if (matchingGlobalReqForType && matchingGlobalReqForType.type) {
|
|
610
|
-
const reqKey = `${matchingGlobalReqForType.type}|${req.name}|${req.version}`;
|
|
611
|
-
if (!globalRequirementsMap.has(reqKey)) {
|
|
612
|
-
// Construct a basic global requirement if not found, though ideally it should exist.
|
|
613
|
-
globalRequirementsMap.set(reqKey, {
|
|
614
|
-
name: req.name,
|
|
615
|
-
version: req.version,
|
|
616
|
-
type: matchingGlobalReqForType.type
|
|
617
|
-
// Other fields like alias, registry would be missing here if not in matchingGlobalReqForType
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
} else {
|
|
621
|
-
console.warn(`Could not find full global requirement definition (or type) for ${req.name} v${req.version} from original server ${originalServer.name}`);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
feedConfiguration.requirements = Array.from(globalRequirementsMap.values());
|
|
632
|
-
|
|
633
|
-
// Validate the entire configuration
|
|
634
|
-
const errors = [];
|
|
635
|
-
|
|
636
|
-
// Validate basic category information
|
|
637
|
-
if (!forExistingCategoryTab) {
|
|
638
|
-
if (!feedConfiguration.name) {
|
|
639
|
-
errors.push('Category name is required');
|
|
640
|
-
} else if (!/^[a-zA-Z0-9-_]+$/.test(feedConfiguration.name)) {
|
|
641
|
-
errors.push('Category name must only contain alphanumeric characters, hyphens, and underscores');
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
if (!feedConfiguration.displayName) {
|
|
645
|
-
errors.push('Display name is required');
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
// Validate each server configuration
|
|
650
|
-
feedConfiguration.mcpServers.forEach((server, index) => {
|
|
651
|
-
const serverErrors = validateServerConfig(server);
|
|
652
|
-
if (serverErrors.length > 0) {
|
|
653
|
-
errors.push(`Server ${index + 1} (${server.name || 'unnamed'}): ${serverErrors.join(', ')}`);
|
|
654
|
-
}
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
// Validate requirements
|
|
658
|
-
feedConfiguration.requirements.forEach((req, index) => {
|
|
659
|
-
if (!req.name || !req.type || !req.version) {
|
|
660
|
-
errors.push(`Requirement ${index + 1}: name, type, and version are required`);
|
|
661
|
-
}
|
|
662
|
-
if (req.registry) {
|
|
663
|
-
const registryType = Object.keys(req.registry)[0];
|
|
664
|
-
if (!['githubRelease', 'artifacts', 'local'].includes(registryType)) {
|
|
665
|
-
errors.push(`Requirement ${index + 1}: Invalid registry type ${registryType}`);
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
if (errors.length > 0) {
|
|
671
|
-
// Remove previous error messages
|
|
672
|
-
formElement.querySelectorAll('.validation-error-message').forEach(el => el.remove());
|
|
673
|
-
|
|
674
|
-
// Map field names to input selectors
|
|
675
|
-
const fieldMap = {
|
|
676
|
-
'Category name is required': 'input[name="name"]',
|
|
677
|
-
'Category name must only contain alphanumeric characters, hyphens, and underscores': 'input[name="name"]',
|
|
678
|
-
'Category name must not exceed 50 characters': 'input[name="name"]',
|
|
679
|
-
'Display name is required': 'input[name="displayName"]',
|
|
680
|
-
'Display name must not exceed 100 characters': 'input[name="displayName"]',
|
|
681
|
-
'Description must not exceed 500 characters': 'textarea[name="description"]',
|
|
682
|
-
'Repository URL must be a valid URL': 'input[name="repository"]'
|
|
683
|
-
};
|
|
684
|
-
|
|
685
|
-
// Show error messages below relevant fields
|
|
686
|
-
errors.forEach(errorMsg => {
|
|
687
|
-
let selector = null;
|
|
688
|
-
if (errorMsg.includes('Category name')) selector = 'input[name="name"]';
|
|
689
|
-
else if (errorMsg.includes('Display name')) selector = 'input[name="displayName"]';
|
|
690
|
-
// Add more mappings as needed
|
|
691
|
-
|
|
692
|
-
if (selector) {
|
|
693
|
-
const input = formElement.querySelector(selector);
|
|
694
|
-
if (input) {
|
|
695
|
-
const errorDiv = document.createElement('div');
|
|
696
|
-
errorDiv.className = 'validation-error-message text-red-500 text-xs mt-1';
|
|
697
|
-
errorDiv.textContent = errorMsg;
|
|
698
|
-
input.insertAdjacentElement('afterend', errorDiv);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
return feedConfiguration;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
/**
|
|
708
|
-
* Populates the form with data from a FeedConfiguration object.
|
|
709
|
-
* @param {object} feedConfig - The FeedConfiguration object.
|
|
710
|
-
* @param {string} [formId='onboardForm'] - The ID of the form to populate.
|
|
711
|
-
* @param {boolean} [renderServersAsReadOnly=false] - If true, renders servers from feedConfig as read-only.
|
|
712
|
-
* This is used for the "Create Server in Existing Category" tab.
|
|
713
|
-
* @param {string|null} [targetServersListId=null] - The ID of the servers list container. Defaults based on formId.
|
|
714
|
-
*/
|
|
715
|
-
export function populateForm(feedConfig, formId = 'onboardForm', renderServersAsReadOnly = false, targetServersListId = null) {
|
|
716
|
-
const currentForm = document.getElementById(formId);
|
|
717
|
-
if (!currentForm) {
|
|
718
|
-
console.error(`populateForm: Form with ID "${formId}" not found.`);
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
const serversListId = targetServersListId || (formId === 'onboardForm' ? 'serversList' : 'existingCategoryServersList');
|
|
723
|
-
|
|
724
|
-
resetOnboardFormDynamicContent(formId, serversListId); // This also clears the target server list's innerHTML
|
|
725
|
-
|
|
726
|
-
// Setup real-time validation
|
|
727
|
-
setupRealTimeValidation(formId);
|
|
728
|
-
|
|
729
|
-
if (!feedConfig) return;
|
|
730
|
-
|
|
731
|
-
if (formId === 'onboardForm') {
|
|
732
|
-
currentForm.querySelector('[name="name"]').value = feedConfig.name || '';
|
|
733
|
-
currentForm.querySelector('[name="displayName"]').value = feedConfig.displayName || '';
|
|
734
|
-
currentForm.querySelector('[name="description"]').value = feedConfig.description || '';
|
|
735
|
-
if (feedConfig.repository) {
|
|
736
|
-
currentForm.querySelector('[name="repository"]').value = feedConfig.repository;
|
|
737
|
-
}
|
|
738
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
739
|
-
if (urlParams.get('action') === 'edit' && urlParams.get('category') === feedConfig.name) {
|
|
740
|
-
const nameInput = currentForm.querySelector('[name="name"]');
|
|
741
|
-
if (nameInput) {
|
|
742
|
-
nameInput.readOnly = true;
|
|
743
|
-
nameInput.classList.add('bg-gray-100', 'cursor-not-allowed');
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
} else if (formId === 'onboardServerForm' && feedConfig) {
|
|
747
|
-
// Populate basic category information for the "Create Server in Existing Category" tab
|
|
748
|
-
// These fields are read-only in this tab, but their values need to be set from feedConfig.
|
|
749
|
-
currentForm.querySelector('input[name="name"]').value = feedConfig.name || '';
|
|
750
|
-
currentForm.querySelector('input[name="displayName"]').value = feedConfig.displayName || '';
|
|
751
|
-
currentForm.querySelector('textarea[name="description"]').value = feedConfig.description || '';
|
|
752
|
-
currentForm.querySelector('input[name="repository"]').value = feedConfig.repository || '';
|
|
753
|
-
|
|
754
|
-
// Ensure the containers for basic info and MCP servers are visible,
|
|
755
|
-
// as resetOnboardFormDynamicContent might have hidden them.
|
|
756
|
-
const basicInfoContainer = document.getElementById('existingCategoryBasicInfoContainer');
|
|
757
|
-
if (basicInfoContainer) {
|
|
758
|
-
basicInfoContainer.classList.remove('hidden');
|
|
759
|
-
}
|
|
760
|
-
const mcpServersContainer = document.getElementById('existingCategoryMcpServersContainer');
|
|
761
|
-
if (mcpServersContainer) {
|
|
762
|
-
// Visibility of mcpServersContainer is also handled by whether servers exist,
|
|
763
|
-
// but ensuring it's not hidden here is a good measure after reset.
|
|
764
|
-
mcpServersContainer.classList.remove('hidden');
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
// Reset tab-specific server counters before populating a list of servers from data.
|
|
769
|
-
// This ensures that servers rendered by this populateForm call start their numbering fresh for this tab.
|
|
770
|
-
setServerCounter(serversListId, 0);
|
|
771
|
-
clearEnvCountersForTab(serversListId);
|
|
772
|
-
clearServerRequirementCountersForTab(serversListId);
|
|
773
|
-
|
|
774
|
-
(feedConfig.mcpServers || []).forEach((serverData) => { // Renamed 'server' to 'serverData' for clarity
|
|
775
|
-
// Get the correct current index for this tab before adding the server
|
|
776
|
-
const currentServerIndex = getServerCounter(serversListId);
|
|
777
|
-
// window.addServer will increment the counter for serversListId internally
|
|
778
|
-
// It also returns the server item, but we'll query it again for safety after DOM manipulation.
|
|
779
|
-
window.addServer(serversListId, renderServersAsReadOnly, serverData);
|
|
780
|
-
|
|
781
|
-
// After addServer, the server item should exist in the DOM.
|
|
782
|
-
const serverItem = currentForm.querySelector(`#${serversListId} .server-item[data-index="${currentServerIndex}"]`);
|
|
783
|
-
|
|
784
|
-
if (serverItem) {
|
|
785
|
-
if (formId === 'onboardServerForm') {
|
|
786
|
-
if (serverData.systemTags?.adhoc === "true") {
|
|
787
|
-
// Case 1: JSON data explicitly marks it as adhoc. Respect this.
|
|
788
|
-
serverItem.dataset.systemTags = JSON.stringify(serverData.systemTags);
|
|
789
|
-
// console.log(`[populateForm] Server ${serverData.name || currentServerIndex} in ${formId} retains adhoc status from JSON.`);
|
|
790
|
-
} else {
|
|
791
|
-
// Case 2: JSON data does NOT explicitly mark it adhoc.
|
|
792
|
-
// Check if it was an original server from the category.
|
|
793
|
-
// state.originalServerNamesForFormPopulation is set when toggling from JSON to Form view for an existing category.
|
|
794
|
-
if (state.originalServerNamesForFormPopulation && serverData.name && state.originalServerNamesForFormPopulation.has(serverData.name)) {
|
|
795
|
-
// It's an original server from the category, now treated as adhoc because it passed through JSON view.
|
|
796
|
-
serverItem.dataset.systemTags = JSON.stringify({ adhoc: "true" });
|
|
797
|
-
// console.log(`[populateForm] Original server ${serverData.name} in ${formId} marked as adhoc after JSON view.`);
|
|
798
|
-
} else {
|
|
799
|
-
// It's a new server (not in original list) and JSON didn't mark it adhoc.
|
|
800
|
-
// Or, it's an original server but its JSON representation explicitly removed/lacked adhoc tag.
|
|
801
|
-
// Ensure it's NOT adhoc.
|
|
802
|
-
// If serverData.systemTags exists but doesn't have adhoc:true, preserve those other tags.
|
|
803
|
-
if (serverData.systemTags && Object.keys(serverData.systemTags).length > 0) {
|
|
804
|
-
const newTags = { ...serverData.systemTags };
|
|
805
|
-
delete newTags.adhoc; // Ensure adhoc is not true
|
|
806
|
-
if (Object.keys(newTags).length > 0) {
|
|
807
|
-
serverItem.dataset.systemTags = JSON.stringify(newTags);
|
|
808
|
-
} else {
|
|
809
|
-
delete serverItem.dataset.systemTags;
|
|
810
|
-
}
|
|
811
|
-
} else {
|
|
812
|
-
delete serverItem.dataset.systemTags;
|
|
813
|
-
}
|
|
814
|
-
// console.log(`[populateForm] Server ${serverData.name || currentServerIndex} in ${formId} is NOT marked adhoc.`);
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
} else {
|
|
818
|
-
// For 'onboardForm' (Create Category tab), or if serverData had systemTags not making it adhoc.
|
|
819
|
-
// If serverData has systemTags, reflect them. Otherwise, ensure no systemTags.
|
|
820
|
-
if (serverData.systemTags) {
|
|
821
|
-
serverItem.dataset.systemTags = JSON.stringify(serverData.systemTags);
|
|
822
|
-
} else {
|
|
823
|
-
delete serverItem.dataset.systemTags;
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
// Continue with populating fields using serverData
|
|
828
|
-
const serverNameInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].name"]`);
|
|
829
|
-
if (serverNameInput) serverNameInput.value = serverData.name || '';
|
|
830
|
-
|
|
831
|
-
const modeInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].mode"]`);
|
|
832
|
-
if (modeInput) {
|
|
833
|
-
modeInput.value = serverData.mode || 'stdio';
|
|
834
|
-
if (typeof window.renderInstallationConfig === 'function') {
|
|
835
|
-
window.renderInstallationConfig(currentServerIndex, serversListId, serverData.mode || 'stdio', renderServersAsReadOnly, serverData.installation);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
const descInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].description"]`);
|
|
839
|
-
if (descInput) descInput.value = serverData.description || '';
|
|
840
|
-
|
|
841
|
-
if (serverData.schemas) {
|
|
842
|
-
const schemaPathEl = document.getElementById(`schema-path-${currentServerIndex}`);
|
|
843
|
-
if (schemaPathEl) schemaPathEl.value = serverData.schemas;
|
|
844
|
-
}
|
|
845
|
-
if (serverData.repository) {
|
|
846
|
-
const repoInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].repository"]`);
|
|
847
|
-
if (repoInput) repoInput.value = serverData.repository;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
if (serverData.installation) {
|
|
851
|
-
if (serverData.mode === 'sse') {
|
|
852
|
-
const urlInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].installation.url"]`);
|
|
853
|
-
if (urlInput) urlInput.value = serverData.installation.url || '';
|
|
854
|
-
} else {
|
|
855
|
-
const cmdInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].installation.command"]`);
|
|
856
|
-
if (cmdInput) cmdInput.value = serverData.installation.command || '';
|
|
857
|
-
if (serverData.installation.args && Array.isArray(serverData.installation.args)) {
|
|
858
|
-
const argsInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].installation.args"]`);
|
|
859
|
-
if (argsInput) argsInput.value = serverData.installation.args.join(', ');
|
|
860
|
-
}
|
|
861
|
-
if (serverData.installation.env) {
|
|
862
|
-
Object.entries(serverData.installation.env).forEach(([envName, envConfig]) => {
|
|
863
|
-
const currentEnvIndex = window.addEnvVariable(currentServerIndex, serversListId, renderServersAsReadOnly);
|
|
864
|
-
|
|
865
|
-
const nameInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].installation.env[${currentEnvIndex}].name"]`);
|
|
866
|
-
if (nameInput) nameInput.value = envName;
|
|
867
|
-
if (envConfig.Default) {
|
|
868
|
-
const defInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].installation.env[${currentEnvIndex}].default"]`);
|
|
869
|
-
if (defInput) defInput.value = envConfig.Default;
|
|
870
|
-
}
|
|
871
|
-
if (envConfig.Required) {
|
|
872
|
-
const reqInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].installation.env[${currentEnvIndex}].required"]`);
|
|
873
|
-
if (reqInput) reqInput.checked = true;
|
|
874
|
-
}
|
|
875
|
-
if (envConfig.Description) {
|
|
876
|
-
const descInput = currentForm.querySelector(`[name="servers[${currentServerIndex}].installation.env[${currentEnvIndex}].description"]`);
|
|
877
|
-
if (descInput) descInput.value = envConfig.Description;
|
|
878
|
-
}
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
if (serverData.dependencies && serverData.dependencies.requirements) {
|
|
885
|
-
// Determine if this server's requirements should be effectively read-only
|
|
886
|
-
const serverIsEffectivelyReadOnlyForReqs = renderServersAsReadOnly && !(serverData.systemTags?.adhoc === 'true');
|
|
887
|
-
// console.log(`[populateForm] Server: ${serverData.name}, Adhoc: ${serverData.systemTags?.adhoc === 'true'}, renderServersAsReadOnly: ${renderServersAsReadOnly}, Calculated serverIsEffectivelyReadOnlyForReqs: ${serverIsEffectivelyReadOnlyForReqs}`); // DEBUG
|
|
888
|
-
|
|
889
|
-
serverData.dependencies.requirements.forEach((depReq) => {
|
|
890
|
-
// Find requirement by name only, as requested.
|
|
891
|
-
const fullReq = (feedConfig.requirements || []).find(r => r.name === depReq.name);
|
|
892
|
-
if (!fullReq) {
|
|
893
|
-
console.warn(`Could not find full requirement config for dependency name: ${depReq.name} (version ${depReq.version} specified by server, but lookup is by name only). Skipping.`);
|
|
894
|
-
return;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
// Pass serverIsEffectivelyReadOnlyForReqs to addServerRequirement
|
|
898
|
-
const currentReqIndex = window.addServerRequirement(currentServerIndex, serversListId, serverIsEffectivelyReadOnlyForReqs);
|
|
899
|
-
|
|
900
|
-
currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].name"]`).value = fullReq.name || '';
|
|
901
|
-
currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].type"]`).value = fullReq.type || '';
|
|
902
|
-
currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].version"]`).value = fullReq.version || '';
|
|
903
|
-
|
|
904
|
-
if (depReq.order !== undefined) {
|
|
905
|
-
currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].order"]`).value = depReq.order;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
if (fullReq.type === 'command' && fullReq.alias) {
|
|
909
|
-
currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].alias"]`).value = fullReq.alias;
|
|
910
|
-
window.toggleServerAliasField(currentServerIndex, currentReqIndex, serversListId);
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
let registryType = 'public';
|
|
914
|
-
if (fullReq.registry) {
|
|
915
|
-
if (fullReq.registry.githubRelease) registryType = 'github';
|
|
916
|
-
else if (fullReq.registry.artifacts) registryType = 'artifacts';
|
|
917
|
-
else if (fullReq.registry.local) registryType = 'local';
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
const registrySelect = currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].registryType"]`);
|
|
921
|
-
if (registrySelect) {
|
|
922
|
-
registrySelect.value = registryType;
|
|
923
|
-
// Pass serversListId to ensure context is correct for toggling UI elements
|
|
924
|
-
window.toggleServerRegistryConfig(currentServerIndex, currentReqIndex, serversListId);
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
if (registryType === 'github' && fullReq.registry.githubRelease) {
|
|
928
|
-
const gh = fullReq.registry.githubRelease;
|
|
929
|
-
if (gh.repository) currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].registry.githubRelease.repository"]`).value = gh.repository;
|
|
930
|
-
if (gh.assetsName) currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].registry.githubRelease.assetsName"]`).value = gh.assetsName;
|
|
931
|
-
if (gh.assetName) currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].registry.githubRelease.assetName"]`).value = gh.assetName;
|
|
932
|
-
} else if (registryType === 'artifacts' && fullReq.registry.artifacts) {
|
|
933
|
-
const art = fullReq.registry.artifacts;
|
|
934
|
-
if (art.registryUrl) currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].registry.artifacts.registryUrl"]`).value = art.registryUrl;
|
|
935
|
-
if (art.registryName) currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].registry.artifacts.registryName"]`).value = art.registryName;
|
|
936
|
-
} else if (registryType === 'local' && fullReq.registry.local) {
|
|
937
|
-
const loc = fullReq.registry.local;
|
|
938
|
-
if (loc.localPath) currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].registry.local.localPath"]`).value = loc.localPath;
|
|
939
|
-
if (loc.assetName) currentForm.querySelector(`[name="servers[${currentServerIndex}].requirements[${currentReqIndex}].registry.local.assetName"]`).value = loc.assetName;
|
|
940
|
-
}
|
|
941
|
-
});
|
|
942
|
-
}
|
|
943
|
-
});
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
/**
|
|
947
|
-
* Resets the dynamic parts of the onboarding form, specifically the servers list.
|
|
948
|
-
* Also resets server-related counters in the state.
|
|
949
|
-
* This function is called before populating the form with new data (e.g., when loading an existing category
|
|
950
|
-
* or switching from JSON view to form view).
|
|
951
|
-
* @param {string} formId - The ID of the form whose dynamic content is to be reset.
|
|
952
|
-
* @param {string} serversListId - The ID of the server list container to clear.
|
|
953
|
-
*/
|
|
954
|
-
export function resetOnboardFormDynamicContent(formId = 'onboardForm', serversListId = 'serversList') {
|
|
955
|
-
const formToReset = document.getElementById(formId);
|
|
956
|
-
if (formToReset) {
|
|
957
|
-
// formToReset.reset(); // This resets the entire form, including the category dropdown.
|
|
958
|
-
// We want to avoid resetting the dropdown when a category is selected.
|
|
959
|
-
// Other parts of this function handle clearing specific dynamic content.
|
|
960
|
-
if (formId !== 'onboardServerForm') { // Only reset other forms, not the one with the category select that triggers loading
|
|
961
|
-
formToReset.reset();
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
const serversListContainer = document.getElementById(serversListId);
|
|
966
|
-
if (serversListContainer) {
|
|
967
|
-
serversListContainer.innerHTML = '';
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
setServerCounter(0);
|
|
971
|
-
if (state && state.envCounters && typeof state.envCounters.clear === 'function') {
|
|
972
|
-
state.envCounters.clear();
|
|
973
|
-
}
|
|
974
|
-
if (state && state.serverRequirementCounters && typeof state.serverRequirementCounters.clear === 'function') {
|
|
975
|
-
state.serverRequirementCounters.clear();
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
// Determine the correct validation panel and content IDs based on formId
|
|
979
|
-
let validationPanelIdToReset;
|
|
980
|
-
let validationContentIdToReset;
|
|
981
|
-
|
|
982
|
-
if (formId === 'onboardForm') {
|
|
983
|
-
validationPanelIdToReset = 'validationStatusPanelNewCategory';
|
|
984
|
-
validationContentIdToReset = 'validationStatusContentNewCategory';
|
|
985
|
-
} else if (formId === 'onboardServerForm') {
|
|
986
|
-
validationPanelIdToReset = 'validationStatusPanelExistingCategory';
|
|
987
|
-
validationContentIdToReset = 'validationStatusContentExistingCategoryTab';
|
|
988
|
-
}
|
|
989
|
-
// else if other forms have validation panels, add conditions here
|
|
990
|
-
|
|
991
|
-
if (validationPanelIdToReset) {
|
|
992
|
-
const validationPanel = document.getElementById(validationPanelIdToReset);
|
|
993
|
-
if (validationPanel) {
|
|
994
|
-
validationPanel.classList.add('hidden');
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
if (validationContentIdToReset) {
|
|
998
|
-
const validationContent = document.getElementById(validationContentIdToReset);
|
|
999
|
-
if (validationContent) {
|
|
1000
|
-
validationContent.innerHTML = '';
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
if (formId === 'onboardServerForm') {
|
|
1005
|
-
// const existingCategorySelect = document.getElementById('existingCategorySelect');
|
|
1006
|
-
// if (existingCategorySelect) existingCategorySelect.value = ''; // This was incorrectly resetting the dropdown
|
|
1007
|
-
|
|
1008
|
-
const basicInfoContainer = document.getElementById('existingCategoryBasicInfoContainer');
|
|
1009
|
-
if (basicInfoContainer) basicInfoContainer.classList.add('hidden');
|
|
1010
|
-
|
|
1011
|
-
const mcpServersContainer = document.getElementById('existingCategoryMcpServersContainer');
|
|
1012
|
-
if (mcpServersContainer) mcpServersContainer.classList.add('hidden');
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
// Setup real-time validation after reset
|
|
1016
|
-
setupRealTimeValidation(formId);
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
/**
|
|
1020
|
-
* Shows validation message under an input field
|
|
1021
|
-
* @param {HTMLElement} element - The element to show validation message for
|
|
1022
|
-
* @param {string} message - The validation message to display
|
|
1023
|
-
* @param {boolean} isError - Whether this is an error message
|
|
1024
|
-
*/
|
|
1025
|
-
export function showValidationMessage(element, message, isError = true) {
|
|
1026
|
-
let messageDiv = element.nextElementSibling;
|
|
1027
|
-
if (!messageDiv || !messageDiv.classList.contains('validation-message')) {
|
|
1028
|
-
messageDiv = document.createElement('div');
|
|
1029
|
-
messageDiv.className = `validation-message text-xs mt-1 ${isError ? 'text-red-500' : 'text-green-500'}`;
|
|
1030
|
-
element.insertAdjacentElement('afterend', messageDiv);
|
|
1031
|
-
}
|
|
1032
|
-
messageDiv.textContent = message;
|
|
1033
|
-
messageDiv.className = `validation-message text-xs mt-1 ${isError ? 'text-red-500' : 'text-green-500'}`;
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
// Export setupRealTimeValidation for external use if needed
|
|
1037
|
-
export { setupRealTimeValidation };
|