@aifabrix/builder 2.32.3 → 2.33.1

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 (127) hide show
  1. package/.cursor/rules/project-rules.mdc +8 -0
  2. package/README.md +36 -8
  3. package/bin/aifabrix.js +6 -8
  4. package/integration/hubspot/README.md +12 -11
  5. package/integration/hubspot/companies.json +2048 -0
  6. package/integration/hubspot/create-hubspot.js +665 -0
  7. package/integration/hubspot/{hubspot-deploy-company.json → hubspot-datasource-company.json} +1 -1
  8. package/integration/hubspot/{hubspot-deploy-contact.json → hubspot-datasource-contact.json} +1 -1
  9. package/integration/hubspot/{hubspot-deploy-deal.json → hubspot-datasource-deal.json} +1 -1
  10. package/integration/hubspot/hubspot-deploy.json +832 -81
  11. package/integration/hubspot/hubspot-system.json +99 -0
  12. package/integration/hubspot/test-artifacts/wizard-hubspot-credential-real.yaml +20 -0
  13. package/integration/hubspot/test-artifacts/wizard-hubspot-env-vars.yaml +9 -0
  14. package/integration/hubspot/test-artifacts/wizard-invalid-add-datasource.yaml +5 -0
  15. package/integration/hubspot/test-artifacts/wizard-invalid-app-name.yaml +5 -0
  16. package/integration/hubspot/test-artifacts/wizard-invalid-credential-create.yaml +7 -0
  17. package/integration/hubspot/test-artifacts/wizard-invalid-credential-select.yaml +7 -0
  18. package/integration/hubspot/test-artifacts/wizard-invalid-known-platform.yaml +4 -0
  19. package/integration/hubspot/test-artifacts/wizard-invalid-missing-app.yaml +4 -0
  20. package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
  21. package/integration/hubspot/test-artifacts/wizard-invalid-mode.yaml +5 -0
  22. package/integration/hubspot/test-artifacts/wizard-invalid-openapi-file.yaml +5 -0
  23. package/integration/hubspot/test-artifacts/wizard-invalid-openapi-url.yaml +4 -0
  24. package/integration/hubspot/test-artifacts/wizard-invalid-source.yaml +4 -0
  25. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-array-test.yaml +5 -0
  26. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
  27. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
  28. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-test.yaml +5 -0
  29. package/integration/hubspot/test-artifacts/wizard-valid-for-rbac-test.yaml +5 -0
  30. package/integration/hubspot/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +5 -0
  31. package/integration/hubspot/test-dataplane-down-helpers.js +246 -0
  32. package/integration/hubspot/test-dataplane-down-tests.js +419 -0
  33. package/integration/hubspot/test-dataplane-down.js +157 -0
  34. package/integration/hubspot/test.js +1517 -0
  35. package/integration/hubspot/variables.yaml +4 -4
  36. package/integration/hubspot/wizard-hubspot-e2e.yaml +16 -0
  37. package/integration/hubspot/wizard-hubspot-platform.yaml +8 -0
  38. package/lib/api/applications.api.js +1 -0
  39. package/lib/api/index.js +6 -2
  40. package/lib/api/types/wizard.types.js +176 -38
  41. package/lib/api/wizard.api.js +161 -23
  42. package/lib/app/deploy.js +116 -54
  43. package/lib/app/display.js +6 -5
  44. package/lib/app/dockerfile.js +2 -1
  45. package/lib/app/list.js +17 -10
  46. package/lib/app/readme.js +41 -112
  47. package/lib/app/register.js +44 -9
  48. package/lib/app/rotate-secret.js +48 -31
  49. package/lib/cli.js +219 -70
  50. package/lib/commands/app.js +4 -9
  51. package/lib/commands/auth-config.js +125 -0
  52. package/lib/commands/auth-status.js +7 -8
  53. package/lib/commands/datasource.js +3 -6
  54. package/lib/commands/login-credentials.js +4 -4
  55. package/lib/commands/login-device.js +26 -17
  56. package/lib/commands/login.js +12 -10
  57. package/lib/commands/wizard-config-normalizer.js +92 -0
  58. package/lib/commands/wizard-core.js +515 -0
  59. package/lib/commands/wizard-dataplane.js +122 -0
  60. package/lib/commands/wizard-headless.js +115 -0
  61. package/lib/commands/wizard.js +110 -332
  62. package/lib/core/config.js +46 -0
  63. package/lib/core/secrets.js +3 -22
  64. package/lib/core/templates-env.js +1 -1
  65. package/lib/datasource/deploy.js +59 -23
  66. package/lib/datasource/list.js +108 -19
  67. package/lib/deployment/deployer.js +25 -0
  68. package/lib/deployment/environment.js +10 -13
  69. package/lib/external-system/delete.js +151 -0
  70. package/lib/external-system/deploy.js +53 -378
  71. package/lib/external-system/download-helpers.js +45 -65
  72. package/lib/external-system/download.js +33 -13
  73. package/lib/external-system/generator.js +11 -7
  74. package/lib/external-system/test-auth.js +4 -3
  75. package/lib/generator/builders.js +3 -1
  76. package/lib/generator/external-controller-manifest.js +157 -0
  77. package/lib/generator/external-schema-utils.js +236 -0
  78. package/lib/generator/external.js +55 -3
  79. package/lib/generator/index.js +22 -10
  80. package/lib/generator/wizard-prompts.js +33 -10
  81. package/lib/generator/wizard.js +69 -86
  82. package/lib/infrastructure/compose.js +100 -0
  83. package/lib/infrastructure/helpers.js +139 -0
  84. package/lib/infrastructure/index.js +52 -311
  85. package/lib/infrastructure/services.js +168 -0
  86. package/lib/schema/application-schema.json +23 -4
  87. package/lib/schema/external-datasource.schema.json +2 -2
  88. package/lib/schema/wizard-config.schema.json +234 -0
  89. package/lib/utils/api.js +102 -52
  90. package/lib/utils/app-existence.js +42 -0
  91. package/lib/utils/app-register-config.js +7 -2
  92. package/lib/utils/auth-config-validator.js +92 -0
  93. package/lib/utils/command-header.js +43 -0
  94. package/lib/utils/compose-generator.js +113 -70
  95. package/lib/utils/controller-url.js +65 -17
  96. package/lib/utils/dataplane-health.js +115 -0
  97. package/lib/utils/dataplane-resolver.js +29 -0
  98. package/lib/utils/dev-config.js +6 -2
  99. package/lib/utils/env-copy.js +2 -1
  100. package/lib/utils/env-ports.js +2 -1
  101. package/lib/utils/env-template.js +1 -1
  102. package/lib/utils/error-formatter.js +49 -0
  103. package/lib/utils/error-formatters/network-errors.js +13 -3
  104. package/lib/utils/external-readme.js +125 -0
  105. package/lib/utils/help-builder.js +190 -0
  106. package/lib/utils/infra-status.js +13 -3
  107. package/lib/utils/paths.js +17 -2
  108. package/lib/utils/port-resolver.js +111 -0
  109. package/lib/utils/secrets-helpers.js +3 -15
  110. package/lib/utils/secrets-utils.js +2 -2
  111. package/lib/utils/token-manager.js +9 -4
  112. package/lib/utils/variable-transformer.js +7 -2
  113. package/lib/validation/external-manifest-validator.js +202 -0
  114. package/lib/validation/validate-display.js +406 -0
  115. package/lib/validation/validate.js +159 -123
  116. package/lib/validation/validator.js +36 -3
  117. package/lib/validation/wizard-config-validator.js +267 -0
  118. package/package.json +4 -2
  119. package/templates/applications/README.md.hbs +18 -16
  120. package/templates/applications/miso-controller/env.template +1 -1
  121. package/templates/applications/miso-controller/rbac.yaml +7 -7
  122. package/templates/external-system/README.md.hbs +99 -0
  123. package/templates/github/ci.yaml.hbs +44 -1
  124. package/templates/github/release.yaml.hbs +44 -0
  125. package/templates/infra/compose.yaml.hbs +35 -0
  126. package/templates/python/docker-compose.hbs +26 -0
  127. package/templates/typescript/docker-compose.hbs +26 -0
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Authentication Configuration Commands
3
+ *
4
+ * Handles setting controller, environment, and dataplane URLs in config.yaml
5
+ *
6
+ * @fileoverview Authentication configuration commands
7
+ * @author AI Fabrix Team
8
+ * @version 2.0.0
9
+ */
10
+
11
+ const chalk = require('chalk');
12
+ const {
13
+ setControllerUrl,
14
+ setCurrentEnvironment,
15
+ getControllerUrl
16
+ } = require('../core/config');
17
+ const {
18
+ validateControllerUrl,
19
+ validateEnvironment,
20
+ checkUserLoggedIn
21
+ } = require('../utils/auth-config-validator');
22
+ const logger = require('../utils/logger');
23
+
24
+ /**
25
+ * Handle set-controller command
26
+ * @async
27
+ * @function handleSetController
28
+ * @param {string} url - Controller URL to set
29
+ * @returns {Promise<void>}
30
+ * @throws {Error} If validation fails
31
+ */
32
+ async function handleSetController(url) {
33
+ try {
34
+ // Validate URL format
35
+ validateControllerUrl(url);
36
+
37
+ // Check if user is logged in to that controller
38
+ const isLoggedIn = await checkUserLoggedIn(url);
39
+ if (!isLoggedIn) {
40
+ throw new Error(
41
+ `You are not logged in to controller ${url}.\n` +
42
+ 'Please run "aifabrix login" first to authenticate with this controller.'
43
+ );
44
+ }
45
+
46
+ // Save controller URL
47
+ await setControllerUrl(url);
48
+
49
+ logger.log(chalk.green(`✓ Controller URL set to: ${url}`));
50
+ } catch (error) {
51
+ logger.error(chalk.red(`✗ Failed to set controller URL: ${error.message}`));
52
+ throw error;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Handle set-environment command
58
+ * @async
59
+ * @function handleSetEnvironment
60
+ * @param {string} environment - Environment key to set
61
+ * @returns {Promise<void>}
62
+ * @throws {Error} If validation fails
63
+ */
64
+ async function handleSetEnvironment(environment) {
65
+ try {
66
+ // Validate environment format
67
+ validateEnvironment(environment);
68
+
69
+ // Get current controller from config
70
+ const controllerUrl = await getControllerUrl();
71
+ if (!controllerUrl) {
72
+ throw new Error(
73
+ 'No controller URL found in config.\n' +
74
+ 'Please run "aifabrix login" first to set the controller URL.'
75
+ );
76
+ }
77
+
78
+ // Check if user is logged in to that controller
79
+ const isLoggedIn = await checkUserLoggedIn(controllerUrl);
80
+ if (!isLoggedIn) {
81
+ throw new Error(
82
+ `You are not logged in to controller ${controllerUrl}.\n` +
83
+ 'Please run "aifabrix login" first to authenticate with this controller.'
84
+ );
85
+ }
86
+
87
+ // Save environment
88
+ await setCurrentEnvironment(environment);
89
+
90
+ logger.log(chalk.green(`✓ Environment set to: ${environment}`));
91
+ } catch (error) {
92
+ logger.error(chalk.red(`✗ Failed to set environment: ${error.message}`));
93
+ throw error;
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Handle auth config command
99
+ * @async
100
+ * @function handleAuthConfig
101
+ * @param {Object} options - Command options
102
+ * @param {string} [options.setController] - Controller URL to set
103
+ * @param {string} [options.setEnvironment] - Environment to set
104
+ * @returns {Promise<void>}
105
+ * @throws {Error} If command fails
106
+ */
107
+ async function handleAuthConfig(options) {
108
+ if (!options.setController && !options.setEnvironment) {
109
+ throw new Error(
110
+ 'No action specified. Use one of:\n' +
111
+ ' --set-controller <url>\n' +
112
+ ' --set-environment <env>'
113
+ );
114
+ }
115
+ if (options.setController) {
116
+ await handleSetController(options.setController);
117
+ }
118
+ if (options.setEnvironment) {
119
+ await handleSetEnvironment(options.setEnvironment);
120
+ }
121
+ }
122
+
123
+ module.exports = {
124
+ handleAuthConfig
125
+ };
@@ -11,7 +11,7 @@
11
11
  const chalk = require('chalk');
12
12
  const logger = require('../utils/logger');
13
13
  const config = require('../core/config');
14
- const { getConfig, getCurrentEnvironment } = config;
14
+ const { getConfig } = config;
15
15
  const { getOrRefreshDeviceToken } = require('../utils/token-manager');
16
16
  const { getAuthUser } = require('../api/auth.api');
17
17
  const { resolveControllerUrl } = require('../utils/controller-url');
@@ -236,17 +236,16 @@ function displayStatus(controllerUrl, environment, tokenInfo) {
236
236
 
237
237
  /**
238
238
  * Handle auth status command
239
+ * Controller and environment come from config.yaml (set via aifabrix login or aifabrix auth config).
239
240
  * @async
240
241
  * @function handleAuthStatus
241
- * @param {Object} options - Command options
242
- * @param {string} [options.controller] - Controller URL (uses developer ID-based default if not provided)
243
- * @param {string} [options.environment] - Environment key (uses current environment from config if not provided)
242
+ * @param {Object} _options - Command options (unused; controller/environment from config only)
244
243
  * @returns {Promise<void>} Resolves when status is displayed
245
244
  */
246
- async function handleAuthStatus(options) {
247
- const configData = await getConfig();
248
- const controllerUrl = await resolveControllerUrl(options, configData);
249
- const environment = options.environment || await getCurrentEnvironment() || 'dev';
245
+ async function handleAuthStatus(_options) {
246
+ const { resolveEnvironment } = require('../core/config');
247
+ const controllerUrl = await resolveControllerUrl();
248
+ const environment = await resolveEnvironment();
250
249
 
251
250
  // Check device token first (preferred)
252
251
  let tokenInfo = await checkDeviceToken(controllerUrl);
@@ -50,11 +50,10 @@ function setupDatasourceCommands(program) {
50
50
  // List command
51
51
  datasource
52
52
  .command('list')
53
- .description('List datasources from environment')
54
- .requiredOption('-e, --environment <env>', 'Environment ID or key')
55
- .action(async(options) => {
53
+ .description('List datasources from environment (uses environment from config.yaml)')
54
+ .action(async() => {
56
55
  try {
57
- await listDatasources(options);
56
+ await listDatasources({});
58
57
  } catch (error) {
59
58
  logger.error(chalk.red('❌ Failed to list datasources:'), error.message);
60
59
  process.exit(1);
@@ -78,8 +77,6 @@ function setupDatasourceCommands(program) {
78
77
  datasource
79
78
  .command('deploy <myapp> <file>')
80
79
  .description('Deploy datasource to dataplane')
81
- .requiredOption('--controller <url>', 'Controller URL')
82
- .requiredOption('-e, --environment <env>', 'Environment (miso, dev, tst, pro)')
83
80
  .action(async(myapp, file, options) => {
84
81
  try {
85
82
  await deployDatasource(myapp, file, options);
@@ -54,7 +54,7 @@ async function promptForCredentials(clientId, clientSecret) {
54
54
  message: 'Client ID:',
55
55
  default: clientId || '',
56
56
  validate: (input) => {
57
- const value = input.trim();
57
+ const value = input ? input.trim() : '';
58
58
  if (!value || value.length === 0) {
59
59
  return 'Client ID is required';
60
60
  }
@@ -68,7 +68,7 @@ async function promptForCredentials(clientId, clientSecret) {
68
68
  default: clientSecret || '',
69
69
  mask: '*',
70
70
  validate: (input) => {
71
- const value = input.trim();
71
+ const value = input ? input.trim() : '';
72
72
  if (!value || value.length === 0) {
73
73
  return 'Client Secret is required';
74
74
  }
@@ -78,8 +78,8 @@ async function promptForCredentials(clientId, clientSecret) {
78
78
  ]);
79
79
 
80
80
  return {
81
- clientId: credentials.clientId.trim(),
82
- clientSecret: credentials.clientSecret.trim()
81
+ clientId: (credentials.clientId || '').trim(),
82
+ clientSecret: (credentials.clientSecret || '').trim()
83
83
  };
84
84
  }
85
85
 
@@ -11,7 +11,7 @@
11
11
  const inquirer = require('inquirer');
12
12
  const chalk = require('chalk');
13
13
  const ora = require('ora');
14
- const { setCurrentEnvironment, saveDeviceToken } = require('../core/config');
14
+ const { setCurrentEnvironment, saveDeviceToken, setControllerUrl } = require('../core/config');
15
15
  const { initiateDeviceCodeFlow } = require('../api/auth.api');
16
16
  const { pollDeviceCodeToken, displayDeviceCodeInfo } = require('../utils/api');
17
17
  const logger = require('../utils/logger');
@@ -68,6 +68,30 @@ async function saveDeviceLoginConfig(controllerUrl, token, refreshToken, expires
68
68
  await saveDeviceToken(controllerUrl, token, refreshToken, expiresAt);
69
69
  }
70
70
 
71
+ /**
72
+ * Save token configuration and display success message
73
+ * @async
74
+ * @param {string} controllerUrl - Controller URL
75
+ * @param {string} token - Access token
76
+ * @param {string} refreshToken - Refresh token
77
+ * @param {string} expiresAt - Token expiration time
78
+ * @param {string} envKey - Environment key
79
+ * @returns {Promise<void>}
80
+ */
81
+ async function saveTokenAndDisplaySuccess(controllerUrl, token, refreshToken, expiresAt, envKey) {
82
+ await saveDeviceLoginConfig(controllerUrl, token, refreshToken, expiresAt);
83
+ await setControllerUrl(controllerUrl);
84
+ if (envKey) {
85
+ await setCurrentEnvironment(envKey);
86
+ }
87
+ logger.log(chalk.green('\n✅ Successfully logged in!'));
88
+ logger.log(chalk.gray(`Controller: ${controllerUrl}`));
89
+ if (envKey) {
90
+ logger.log(chalk.gray(`Environment: ${envKey}`));
91
+ }
92
+ logger.log(chalk.gray('Token stored securely in ~/.aifabrix/config.yaml\n'));
93
+ }
94
+
71
95
  /**
72
96
  * Poll for device code token and save configuration
73
97
  * @async
@@ -105,23 +129,8 @@ async function pollAndSaveDeviceCodeToken(controllerUrl, deviceCode, interval, e
105
129
  const refreshToken = tokenResponse.refresh_token;
106
130
  const expiresAt = new Date(Date.now() + (tokenResponse.expires_in * 1000)).toISOString();
107
131
 
108
- // Save device token at root level (controller-specific, not environment-specific)
109
- await saveDeviceLoginConfig(controllerUrl, token, refreshToken, expiresAt);
110
-
111
- // Still set current environment if provided (for other purposes)
112
- if (envKey) {
113
- await setCurrentEnvironment(envKey);
114
- }
115
-
116
- logger.log(chalk.green('\n✅ Successfully logged in!'));
117
- logger.log(chalk.gray(`Controller: ${controllerUrl}`));
118
- if (envKey) {
119
- logger.log(chalk.gray(`Environment: ${envKey}`));
120
- }
121
- logger.log(chalk.gray('Token stored securely in ~/.aifabrix/config.yaml\n'));
122
-
132
+ await saveTokenAndDisplaySuccess(controllerUrl, token, refreshToken, expiresAt, envKey);
123
133
  return { token, environment: envKey };
124
-
125
134
  } catch (pollError) {
126
135
  spinner.fail('Authentication failed');
127
136
  throw pollError;
@@ -11,11 +11,11 @@
11
11
 
12
12
  const inquirer = require('inquirer');
13
13
  const chalk = require('chalk');
14
- const { setCurrentEnvironment, saveClientToken } = require('../core/config');
14
+ const { setCurrentEnvironment, saveClientToken, setControllerUrl } = require('../core/config');
15
15
  const logger = require('../utils/logger');
16
16
  const { handleCredentialsLogin } = require('./login-credentials');
17
17
  const { handleDeviceCodeLogin } = require('./login-device');
18
- const { getDefaultControllerUrl } = require('../utils/controller-url');
18
+ const { resolveControllerUrl } = require('../utils/controller-url');
19
19
 
20
20
  /**
21
21
  * Determine and validate authentication method
@@ -62,7 +62,7 @@ async function saveCredentialsLoginConfig(controllerUrl, token, expiresAt, envir
62
62
  * @async
63
63
  * @function handleLogin
64
64
  * @param {Object} options - Login options
65
- * @param {string} [options.controller] - Controller URL (default: calculated based on developer ID, e.g., 'http://localhost:3000' for dev ID 0, 'http://localhost:3100' for dev ID 1)
65
+ * @param {string} [options.controller] - Controller URL (default: from config, device tokens, or developer ID)
66
66
  * @param {string} [options.method] - Authentication method ('device' or 'credentials', default: 'device')
67
67
  * @param {string} [options.app] - Application name (for credentials method, reads from secrets.local.yaml)
68
68
  * @param {string} [options.clientId] - Client ID (for credentials method, overrides secrets.local.yaml)
@@ -72,8 +72,7 @@ async function saveCredentialsLoginConfig(controllerUrl, token, expiresAt, envir
72
72
  * @throws {Error} If login fails
73
73
  */
74
74
  /**
75
- * Normalizes and logs controller URL
76
- * Calculates default URL based on developer ID if not provided
75
+ * Resolves and logs controller URL from --controller, config, device tokens, or developer-ID default
77
76
  * @async
78
77
  * @function normalizeControllerUrl
79
78
  * @param {Object} options - Login options
@@ -82,9 +81,11 @@ async function saveCredentialsLoginConfig(controllerUrl, token, expiresAt, envir
82
81
  async function normalizeControllerUrl(options) {
83
82
  let controllerUrl = options.controller || options.url;
84
83
  if (!controllerUrl) {
85
- controllerUrl = await getDefaultControllerUrl();
84
+ controllerUrl = await resolveControllerUrl();
86
85
  }
87
- controllerUrl = controllerUrl.replace(/\/$/, '');
86
+ controllerUrl = String(controllerUrl).replace(/\/+$/, '');
87
+ // Save controller URL to config
88
+ await setControllerUrl(controllerUrl);
88
89
  logger.log(chalk.gray(`Controller URL: ${controllerUrl}`));
89
90
  return controllerUrl;
90
91
  }
@@ -144,11 +145,12 @@ async function handleCredentialsLoginFlow(controllerUrl, environment, options) {
144
145
  * @async
145
146
  * @function handleDeviceCodeLoginFlow
146
147
  * @param {string} controllerUrl - Controller URL
148
+ * @param {string} environment - Resolved environment key (from config or -e/--environment)
147
149
  * @param {Object} options - Login options
148
150
  * @returns {Promise<{token: string, environment: string}>} Login result
149
151
  */
150
- async function handleDeviceCodeLoginFlow(controllerUrl, options) {
151
- return await handleDeviceCodeLogin(controllerUrl, options.environment, options.online, options.scope);
152
+ async function handleDeviceCodeLoginFlow(controllerUrl, environment, options) {
153
+ return await handleDeviceCodeLogin(controllerUrl, environment, options.online, options.scope);
152
154
  }
153
155
 
154
156
  async function handleLogin(options) {
@@ -163,7 +165,7 @@ async function handleLogin(options) {
163
165
  if (method === 'credentials') {
164
166
  await handleCredentialsLoginFlow(controllerUrl, environment, options);
165
167
  } else if (method === 'device') {
166
- await handleDeviceCodeLoginFlow(controllerUrl, options);
168
+ await handleDeviceCodeLoginFlow(controllerUrl, environment, options);
167
169
  return; // Early return for device flow (already saved config)
168
170
  }
169
171
 
@@ -0,0 +1,92 @@
1
+ /**
2
+ * @fileoverview Normalize wizard-generated configs before validation
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const ENTITY_TYPE_FALLBACK = 'record-storage';
8
+ const VALID_ENTITY_TYPES = new Set([
9
+ 'document-storage',
10
+ 'documentStorage',
11
+ 'vector-store',
12
+ 'vectorStore',
13
+ 'record-storage',
14
+ 'recordStorage',
15
+ 'message-service',
16
+ 'messageService',
17
+ 'none'
18
+ ]);
19
+ const VALID_PORTAL_FIELDS = new Set(['text', 'textarea', 'select', 'json', 'boolean', 'number']);
20
+
21
+ /**
22
+ * Normalize system config fields to schema constraints
23
+ * @function normalizeSystemConfig
24
+ * @param {Object} systemConfig - External system config
25
+ * @returns {Object} Normalized config
26
+ */
27
+ function normalizeSystemConfig(systemConfig) {
28
+ if (!systemConfig || typeof systemConfig !== 'object') {
29
+ return systemConfig;
30
+ }
31
+ if (typeof systemConfig.description === 'string' && systemConfig.description.length > 500) {
32
+ systemConfig.description = systemConfig.description.slice(0, 500);
33
+ }
34
+ return systemConfig;
35
+ }
36
+
37
+ /**
38
+ * Normalize datasource config fields to schema constraints
39
+ * @function normalizeDatasourceConfig
40
+ * @param {Object} datasourceConfig - Datasource config
41
+ * @returns {Object} Normalized config
42
+ */
43
+ function normalizeDatasourceConfig(datasourceConfig) {
44
+ if (!datasourceConfig || typeof datasourceConfig !== 'object') {
45
+ return datasourceConfig;
46
+ }
47
+ if (datasourceConfig.entityType && !VALID_ENTITY_TYPES.has(datasourceConfig.entityType)) {
48
+ datasourceConfig.entityType = ENTITY_TYPE_FALLBACK;
49
+ }
50
+ if (Array.isArray(datasourceConfig.portalInput)) {
51
+ datasourceConfig.portalInput = datasourceConfig.portalInput.filter(item => {
52
+ if (!item || typeof item !== 'object') {
53
+ return false;
54
+ }
55
+ if (!item.name || !item.field || !item.label) {
56
+ return false;
57
+ }
58
+ return VALID_PORTAL_FIELDS.has(item.field);
59
+ });
60
+ }
61
+ if (datasourceConfig.execution?.cip?.operations) {
62
+ for (const operation of Object.values(datasourceConfig.execution.cip.operations)) {
63
+ if (!operation || !Array.isArray(operation.steps)) {
64
+ continue;
65
+ }
66
+ for (const step of operation.steps) {
67
+ if (step?.output?.mode && step.output.mode !== 'records') {
68
+ step.output.mode = 'records';
69
+ }
70
+ }
71
+ }
72
+ }
73
+ return datasourceConfig;
74
+ }
75
+
76
+ /**
77
+ * Normalize system and datasource configs
78
+ * @function normalizeWizardConfigs
79
+ * @param {Object} systemConfig - System config
80
+ * @param {Object|Object[]} datasourceConfigs - Datasource config(s)
81
+ * @returns {{ systemConfig: Object, datasourceConfigs: Object[] }} Normalized configs
82
+ */
83
+ function normalizeWizardConfigs(systemConfig, datasourceConfigs) {
84
+ const normalizedSystem = normalizeSystemConfig(systemConfig);
85
+ const configs = Array.isArray(datasourceConfigs) ? datasourceConfigs : [datasourceConfigs];
86
+ const normalizedDatasources = configs.map(normalizeDatasourceConfig);
87
+ return { systemConfig: normalizedSystem, datasourceConfigs: normalizedDatasources };
88
+ }
89
+
90
+ module.exports = {
91
+ normalizeWizardConfigs
92
+ };