@aifabrix/builder 2.39.2 → 2.40.0

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 (116) hide show
  1. package/.cursor/rules/project-rules.mdc +6 -6
  2. package/README.md +2 -2
  3. package/babel.config.js +6 -0
  4. package/integration/hubspot/README.md +53 -141
  5. package/integration/hubspot/application.yaml +37 -0
  6. package/integration/hubspot/env.template +2 -11
  7. package/integration/hubspot/hubspot-deploy.json +1 -0
  8. package/integration/hubspot/test.js +5 -5
  9. package/lib/api/credentials.api.js +5 -5
  10. package/lib/api/deployments.api.js +2 -2
  11. package/lib/api/pipeline.api.js +17 -17
  12. package/lib/api/wizard.api.js +2 -2
  13. package/lib/app/config.js +11 -6
  14. package/lib/app/deploy-config.js +13 -16
  15. package/lib/app/deploy.js +29 -22
  16. package/lib/app/display.js +1 -1
  17. package/lib/app/dockerfile.js +11 -12
  18. package/lib/app/helpers.js +51 -13
  19. package/lib/app/index.js +14 -2
  20. package/lib/app/prompts.js +37 -45
  21. package/lib/app/push.js +8 -11
  22. package/lib/app/readme.js +16 -12
  23. package/lib/app/register.js +3 -3
  24. package/lib/app/run-helpers.js +31 -22
  25. package/lib/app/run.js +44 -5
  26. package/lib/app/show-display.js +104 -44
  27. package/lib/app/show.js +123 -43
  28. package/lib/build/index.js +11 -18
  29. package/lib/cli/setup-app.js +38 -28
  30. package/lib/cli/setup-auth.js +18 -15
  31. package/lib/cli/setup-credential-deployment.js +3 -1
  32. package/lib/cli/setup-external-system.js +35 -16
  33. package/lib/cli/setup-infra.js +45 -23
  34. package/lib/cli/setup-utility.js +79 -31
  35. package/lib/commands/app-logs.js +165 -10
  36. package/lib/commands/app.js +30 -26
  37. package/lib/commands/convert.js +202 -0
  38. package/lib/commands/credential-list.js +78 -17
  39. package/lib/commands/datasource.js +24 -24
  40. package/lib/commands/deployment-list.js +13 -6
  41. package/lib/commands/up-common.js +80 -42
  42. package/lib/commands/up-dataplane.js +15 -14
  43. package/lib/commands/up-miso.js +15 -14
  44. package/lib/commands/upload.js +163 -0
  45. package/lib/commands/wizard-core.js +5 -4
  46. package/lib/core/diff.js +84 -9
  47. package/lib/core/key-generator.js +9 -12
  48. package/lib/core/secrets-docker-env.js +2 -2
  49. package/lib/core/secrets.js +3 -2
  50. package/lib/core/templates.js +2 -2
  51. package/lib/datasource/deploy.js +2 -1
  52. package/lib/deployment/deployer.js +76 -48
  53. package/lib/external-system/delete.js +0 -1
  54. package/lib/external-system/deploy-helpers.js +5 -6
  55. package/lib/external-system/deploy.js +7 -2
  56. package/lib/external-system/download-helpers.js +4 -4
  57. package/lib/external-system/download.js +11 -10
  58. package/lib/external-system/generator.js +19 -17
  59. package/lib/external-system/test.js +10 -15
  60. package/lib/generator/builders.js +1 -1
  61. package/lib/generator/external-controller-manifest.js +26 -29
  62. package/lib/generator/external-schema-utils.js +6 -18
  63. package/lib/generator/external.js +32 -27
  64. package/lib/generator/github.js +1 -1
  65. package/lib/generator/helpers.js +12 -19
  66. package/lib/generator/index.js +15 -15
  67. package/lib/generator/parse-image.js +35 -0
  68. package/lib/generator/split-readme.js +105 -0
  69. package/lib/generator/split-variables.js +149 -0
  70. package/lib/generator/split.js +86 -246
  71. package/lib/generator/wizard.js +46 -69
  72. package/lib/schema/application-schema.json +4 -4
  73. package/lib/schema/deployment-rules.yaml +0 -4
  74. package/lib/schema/external-datasource.schema.json +5 -0
  75. package/lib/schema/external-system.schema.json +10 -0
  76. package/lib/utils/app-config-resolver.js +52 -0
  77. package/lib/utils/app-register-api.js +1 -1
  78. package/lib/utils/app-register-auth.js +1 -1
  79. package/lib/utils/app-register-config.js +16 -23
  80. package/lib/utils/app-register-display.js +22 -3
  81. package/lib/utils/app-register-validator.js +2 -2
  82. package/lib/utils/cli-utils.js +47 -3
  83. package/lib/utils/config-format.js +154 -0
  84. package/lib/utils/config-paths.js +19 -52
  85. package/lib/utils/config-tokens.js +1 -0
  86. package/lib/utils/docker-build.js +71 -94
  87. package/lib/utils/dockerfile-utils.js +1 -1
  88. package/lib/utils/env-copy.js +4 -4
  89. package/lib/utils/env-ports.js +2 -2
  90. package/lib/utils/error-formatter.js +1 -1
  91. package/lib/utils/error-formatters/validation-errors.js +1 -1
  92. package/lib/utils/external-readme.js +12 -5
  93. package/lib/utils/external-system-test-helpers.js +2 -0
  94. package/lib/utils/health-check.js +55 -66
  95. package/lib/utils/image-version.js +12 -21
  96. package/lib/utils/paths.js +39 -66
  97. package/lib/utils/port-resolver.js +8 -8
  98. package/lib/utils/schema-loader.js +22 -0
  99. package/lib/utils/schema-resolver.js +23 -33
  100. package/lib/utils/secrets-helpers.js +7 -7
  101. package/lib/utils/secrets-utils.js +10 -12
  102. package/lib/utils/template-helpers.js +13 -13
  103. package/lib/utils/token-manager.js +20 -2
  104. package/lib/utils/variable-transformer.js +2 -2
  105. package/lib/validation/validate-display.js +3 -4
  106. package/lib/validation/validate.js +33 -27
  107. package/lib/validation/validator.js +50 -30
  108. package/package.json +2 -1
  109. package/templates/README.md +1 -1
  110. package/templates/applications/README.md.hbs +3 -3
  111. package/templates/applications/miso-controller/env.template +3 -1
  112. package/templates/external-system/README.md.hbs +4 -4
  113. package/integration/hubspot/variables.yaml +0 -17
  114. /package/templates/applications/dataplane/{variables.yaml → application.yaml} +0 -0
  115. /package/templates/applications/keycloak/{variables.yaml → application.yaml} +0 -0
  116. /package/templates/applications/miso-controller/{variables.yaml → application.yaml} +0 -0
@@ -11,6 +11,9 @@
11
11
  const fs = require('fs').promises;
12
12
  const path = require('path');
13
13
  const yaml = require('js-yaml');
14
+ const { parseImageReference } = require('./parse-image');
15
+ const { generateReadmeFromDeployJson } = require('./split-readme');
16
+ const { extractVariablesYaml, getExternalDatasourceFileName } = require('./split-variables');
14
17
 
15
18
  /**
16
19
  * Converts configuration array back to env.template format
@@ -43,191 +46,6 @@ function extractEnvTemplate(configuration) {
43
46
  return lines.join('\n');
44
47
  }
45
48
 
46
- /**
47
- * Parses image reference string into components
48
- * @function parseImageReference
49
- * @param {string} imageString - Full image string (e.g., "registry/name:tag")
50
- * @returns {Object} Object with registry, name, and tag
51
- */
52
- function parseImageReference(imageString) {
53
- if (!imageString || typeof imageString !== 'string') {
54
- return { registry: null, name: null, tag: 'latest' };
55
- }
56
-
57
- // Handle format: registry/name:tag or name:tag or registry/name
58
- const parts = imageString.split('/');
59
- let registry = null;
60
- let nameAndTag = imageString;
61
-
62
- if (parts.length > 1) {
63
- // Check if first part looks like a registry (contains .)
64
- if (parts[0].includes('.')) {
65
- registry = parts[0];
66
- nameAndTag = parts.slice(1).join('/');
67
- } else {
68
- // No registry, just name:tag
69
- nameAndTag = imageString;
70
- }
71
- }
72
-
73
- // Split name and tag
74
- const tagIndex = nameAndTag.lastIndexOf(':');
75
- let name = nameAndTag;
76
- let tag = 'latest';
77
-
78
- if (tagIndex !== -1) {
79
- name = nameAndTag.substring(0, tagIndex);
80
- tag = nameAndTag.substring(tagIndex + 1);
81
- }
82
-
83
- return { registry, name, tag };
84
- }
85
-
86
- /**
87
- * Extract app section from deployment
88
- * @param {Object} deployment - Deployment JSON object
89
- * @returns {Object|undefined} App section or undefined
90
- */
91
- function extractAppSection(deployment) {
92
- if (!deployment.key && !deployment.displayName && !deployment.description && !deployment.type) {
93
- return undefined;
94
- }
95
-
96
- const app = {};
97
- if (deployment.key) app.key = deployment.key;
98
- if (deployment.displayName) app.displayName = deployment.displayName;
99
- if (deployment.description) app.description = deployment.description;
100
- if (deployment.type) app.type = deployment.type;
101
- if (deployment.version) app.version = deployment.version;
102
- return app;
103
- }
104
-
105
- /**
106
- * Extract image section from deployment
107
- * @param {Object} deployment - Deployment JSON object
108
- * @returns {Object|undefined} Image section or undefined
109
- */
110
- function extractImageSection(deployment) {
111
- if (!deployment.image) {
112
- return undefined;
113
- }
114
-
115
- const imageParts = parseImageReference(deployment.image);
116
- const image = {};
117
- if (imageParts.name) image.name = imageParts.name;
118
- if (imageParts.registry) image.registry = imageParts.registry;
119
- if (imageParts.tag) image.tag = imageParts.tag;
120
- if (deployment.registryMode) image.registryMode = deployment.registryMode;
121
- return image;
122
- }
123
-
124
- /**
125
- * Extract requirements section from deployment
126
- * @param {Object} deployment - Deployment JSON object
127
- * @returns {Object|undefined} Requirements section or undefined
128
- */
129
- function extractRequirementsSection(deployment) {
130
- if (!deployment.requiresDatabase && !deployment.requiresRedis && !deployment.requiresStorage && !deployment.databases) {
131
- return undefined;
132
- }
133
-
134
- const requires = {};
135
- if (deployment.requiresDatabase !== undefined) requires.database = deployment.requiresDatabase;
136
- if (deployment.requiresRedis !== undefined) requires.redis = deployment.requiresRedis;
137
- if (deployment.requiresStorage !== undefined) requires.storage = deployment.requiresStorage;
138
- if (deployment.databases) requires.databases = deployment.databases;
139
- return requires;
140
- }
141
-
142
- /**
143
- * Extract optional sections from deployment
144
- * @param {Object} deployment - Deployment JSON object
145
- * @returns {Object} Object with optional sections
146
- */
147
- /**
148
- * Extracts a single optional section if present
149
- * @function extractOptionalSection
150
- * @param {Object} deployment - Deployment object
151
- * @param {string} sectionName - Section name to extract
152
- * @param {Object} optional - Optional sections object to update
153
- */
154
- function extractOptionalSection(deployment, sectionName, optional) {
155
- if (deployment[sectionName]) {
156
- if (sectionName === 'authentication') {
157
- optional[sectionName] = { ...deployment[sectionName] };
158
- } else {
159
- optional[sectionName] = deployment[sectionName];
160
- }
161
- }
162
- }
163
-
164
- function extractOptionalSections(deployment) {
165
- const optional = {};
166
-
167
- const optionalSectionNames = [
168
- 'healthCheck',
169
- 'authentication',
170
- 'build',
171
- 'repository',
172
- 'deployment',
173
- 'startupCommand',
174
- 'runtimeVersion',
175
- 'scaling',
176
- 'frontDoorRouting',
177
- 'roles',
178
- 'permissions'
179
- ];
180
-
181
- for (const sectionName of optionalSectionNames) {
182
- extractOptionalSection(deployment, sectionName, optional);
183
- }
184
-
185
- return optional;
186
- }
187
-
188
- /**
189
- * Extracts deployment JSON into variables.yaml structure
190
- * @function extractVariablesYaml
191
- * @param {Object} deployment - Deployment JSON object
192
- * @returns {Object} Variables YAML structure
193
- */
194
- function extractVariablesYaml(deployment) {
195
- if (!deployment || typeof deployment !== 'object') {
196
- throw new Error('Deployment object is required');
197
- }
198
-
199
- const variables = {};
200
-
201
- // Extract app section
202
- const appSection = extractAppSection(deployment);
203
- if (appSection) {
204
- variables.app = appSection;
205
- }
206
-
207
- // Extract image section
208
- const imageSection = extractImageSection(deployment);
209
- if (imageSection) {
210
- variables.image = imageSection;
211
- }
212
-
213
- // Extract port
214
- if (deployment.port !== undefined) {
215
- variables.port = deployment.port;
216
- }
217
-
218
- // Extract requirements section
219
- const requirementsSection = extractRequirementsSection(deployment);
220
- if (requirementsSection) {
221
- variables.requires = requirementsSection;
222
- }
223
-
224
- // Extract optional sections
225
- const optionalSections = extractOptionalSections(deployment);
226
- Object.assign(variables, optionalSections);
227
-
228
- return variables;
229
- }
230
-
231
49
  /**
232
50
  * Extracts roles and permissions from deployment JSON
233
51
  * @function extractRbacYaml
@@ -257,62 +75,6 @@ function extractRbacYaml(deployment) {
257
75
  return rbac;
258
76
  }
259
77
 
260
- /**
261
- * Generates README.md content from deployment JSON
262
- * @function generateReadmeFromDeployJson
263
- * @param {Object} deployment - Deployment JSON object
264
- * @returns {string} README.md content
265
- */
266
- function generateReadmeFromDeployJson(deployment) {
267
- if (!deployment || typeof deployment !== 'object') {
268
- throw new Error('Deployment object is required');
269
- }
270
-
271
- const appName = deployment.key || 'application';
272
- const displayName = deployment.displayName || appName;
273
- const description = deployment.description || 'Application deployment configuration';
274
- const port = deployment.port || 3000;
275
- const image = deployment.image || 'unknown';
276
-
277
- const lines = [
278
- `# ${displayName}`,
279
- '',
280
- description,
281
- '',
282
- '## Quick Start',
283
- '',
284
- 'This application is configured via deployment JSON and component files.',
285
- '',
286
- '## Configuration',
287
- '',
288
- `- **Application Key**: \`${appName}\``,
289
- `- **Port**: \`${port}\``,
290
- `- **Image**: \`${image}\``,
291
- '',
292
- '## Files',
293
- '',
294
- '- `variables.yaml` - Application configuration',
295
- '- `env.template` - Environment variables template',
296
- '- `rbac.yml` - Roles and permissions (if applicable)',
297
- '- `README.md` - This file',
298
- '',
299
- '## Documentation',
300
- '',
301
- 'For more information, see the [AI Fabrix Builder documentation](../../docs/README.md).'
302
- ];
303
-
304
- return lines.join('\n');
305
- }
306
-
307
- /**
308
- * Splits a deployment JSON file into component files
309
- * @async
310
- * @function splitDeployJson
311
- * @param {string} deployJsonPath - Path to deployment JSON file
312
- * @param {string} [outputDir] - Directory to write component files (defaults to same directory as JSON)
313
- * @returns {Promise<Object>} Object with paths to generated files
314
- * @throws {Error} If JSON file not found or invalid
315
- */
316
78
  /**
317
79
  * Validates deployment JSON path
318
80
  * @function validateDeployJsonPath
@@ -396,8 +158,8 @@ async function writeComponentFiles(outputDir, envTemplate, variables, rbac, read
396
158
  await writeComponentFile(envTemplatePath, envTemplate);
397
159
  results.envTemplate = envTemplatePath;
398
160
 
399
- // Write variables.yaml
400
- const variablesPath = path.join(outputDir, 'variables.yaml');
161
+ // Write application.yaml
162
+ const variablesPath = path.join(outputDir, 'application.yaml');
401
163
  const variablesYaml = yaml.dump(variables, { indent: 2, lineWidth: -1 });
402
164
  await writeComponentFile(variablesPath, variablesYaml);
403
165
  results.variables = variablesPath;
@@ -418,18 +180,96 @@ async function writeComponentFiles(outputDir, envTemplate, variables, rbac, read
418
180
  return results;
419
181
  }
420
182
 
183
+ /**
184
+ * Writes external system and datasource YAML files when deployment has system (external format).
185
+ * @async
186
+ * @param {string} outputDir - Output directory
187
+ * @param {Object} deployment - Deployment with system and dataSources
188
+ * @returns {Promise<{ systemFile?: string, datasourceFiles?: string[] }>} Paths to written files
189
+ */
190
+ async function writeExternalSystemAndDatasourceFiles(outputDir, deployment) {
191
+ if (!deployment || !deployment.system) {
192
+ return {};
193
+ }
194
+ const system = deployment.system;
195
+ const systemKey = system.key || 'external-system';
196
+ const dataSourcesList = deployment.dataSources || deployment.datasources || [];
197
+ const results = {};
198
+
199
+ const systemPath = path.join(outputDir, `${systemKey}-system.yaml`);
200
+ const systemYaml = yaml.dump(system, { indent: 2, lineWidth: -1 });
201
+ await writeComponentFile(systemPath, systemYaml);
202
+ results.systemFile = systemPath;
203
+
204
+ const datasourcePaths = [];
205
+ for (let i = 0; i < dataSourcesList.length; i++) {
206
+ const ds = dataSourcesList[i];
207
+ const fileName = getExternalDatasourceFileName(systemKey, ds, i);
208
+ const dsPath = path.join(outputDir, fileName);
209
+ const dsYaml = yaml.dump(ds, { indent: 2, lineWidth: -1 });
210
+ await writeComponentFile(dsPath, dsYaml);
211
+ datasourcePaths.push(dsPath);
212
+ }
213
+ results.datasourceFiles = datasourcePaths;
214
+
215
+ return results;
216
+ }
217
+
218
+ /**
219
+ * Normalizes deployment for split: for external format (deployment.system),
220
+ * lifts configuration and roles/permissions to top level so extractors work.
221
+ * @param {Object} deployment - Raw deployment object
222
+ * @returns {Object} Deployment (mutated) with configuration/roles/permissions at top level when from system
223
+ */
224
+ function normalizeDeploymentForSplit(deployment) {
225
+ if (!deployment || !deployment.system) {
226
+ return deployment;
227
+ }
228
+ const system = deployment.system;
229
+ if (system.configuration && !deployment.configuration) {
230
+ deployment.configuration = system.configuration;
231
+ }
232
+ if (system.roles && !deployment.roles) {
233
+ deployment.roles = system.roles;
234
+ }
235
+ if (system.permissions && !deployment.permissions) {
236
+ deployment.permissions = system.permissions;
237
+ }
238
+ return deployment;
239
+ }
240
+
241
+ /**
242
+ * Splits a deployment JSON file into component files.
243
+ * @async
244
+ * @function splitDeployJson
245
+ * @param {string} deployJsonPath - Path to deployment JSON file
246
+ * @param {string} [outputDir] - Directory to write component files (defaults to same directory as JSON)
247
+ * @returns {Promise<Object>} Object with paths to generated files
248
+ * @throws {Error} If JSON file not found or invalid
249
+ */
421
250
  async function splitDeployJson(deployJsonPath, outputDir = null) {
422
251
  validateDeployJsonPath(deployJsonPath);
423
252
  const finalOutputDir = await prepareOutputDirectory(deployJsonPath, outputDir);
424
253
  const deployment = await loadDeploymentJson(deployJsonPath);
254
+ normalizeDeploymentForSplit(deployment);
425
255
 
426
- // Extract components
427
- const envTemplate = extractEnvTemplate(deployment.configuration || []);
256
+ const configArray = deployment.configuration || [];
257
+ const envTemplate = extractEnvTemplate(configArray);
428
258
  const variables = extractVariablesYaml(deployment);
429
259
  const rbac = extractRbacYaml(deployment);
430
260
  const readme = generateReadmeFromDeployJson(deployment);
431
261
 
432
- return await writeComponentFiles(finalOutputDir, envTemplate, variables, rbac, readme);
262
+ const result = await writeComponentFiles(finalOutputDir, envTemplate, variables, rbac, readme);
263
+
264
+ if (deployment.system && typeof deployment.system === 'object') {
265
+ const externalFiles = await writeExternalSystemAndDatasourceFiles(finalOutputDir, deployment);
266
+ if (externalFiles.systemFile) result.systemFile = externalFiles.systemFile;
267
+ if (externalFiles.datasourceFiles && externalFiles.datasourceFiles.length > 0) {
268
+ result.datasourceFiles = externalFiles.datasourceFiles;
269
+ }
270
+ }
271
+
272
+ return result;
433
273
  }
434
274
 
435
275
  module.exports = {
@@ -6,10 +6,11 @@
6
6
 
7
7
  const fs = require('fs').promises;
8
8
  const path = require('path');
9
- const yaml = require('js-yaml');
10
9
  const Handlebars = require('handlebars');
11
10
  const chalk = require('chalk');
12
11
  const logger = require('../utils/logger');
12
+ const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
13
+ const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
13
14
  const { generateExternalReadmeContent } = require('../utils/external-readme');
14
15
 
15
16
  /**
@@ -39,30 +40,30 @@ function toKeySegment(str) {
39
40
  /**
40
41
  * Writes system JSON file
41
42
  * @async
42
- * @function writeSystemJsonFile
43
+ * @function writeSystemYamlFile
43
44
  * @param {string} appPath - Application path
44
45
  * @param {string} finalSystemKey - Final system key
45
46
  * @param {Object} systemConfig - System configuration
46
47
  * @returns {Promise<string>} System file path
47
48
  */
48
- async function writeSystemJsonFile(appPath, finalSystemKey, systemConfig) {
49
- const systemFileName = `${finalSystemKey}-system.json`;
49
+ async function writeSystemYamlFile(appPath, finalSystemKey, systemConfig) {
50
+ const systemFileName = `${finalSystemKey}-system.yaml`;
50
51
  const systemFilePath = path.join(appPath, systemFileName);
51
- await fs.writeFile(systemFilePath, JSON.stringify(systemConfig, null, 2), 'utf8');
52
+ writeConfigFile(systemFilePath, systemConfig);
52
53
  logger.log(chalk.green(`✓ Generated system file: ${systemFileName}`));
53
54
  return systemFilePath;
54
55
  }
55
56
 
56
57
  /**
57
- * Writes datasource JSON files
58
+ * Writes datasource YAML files
58
59
  * @async
59
- * @function writeDatasourceJsonFiles
60
+ * @function writeDatasourceYamlFiles
60
61
  * @param {string} appPath - Application path
61
62
  * @param {string} finalSystemKey - Final system key
62
63
  * @param {Object[]} datasourceConfigs - Array of datasource configurations
63
64
  * @returns {Promise<string[]>} Array of datasource file names
64
65
  */
65
- async function writeDatasourceJsonFiles(appPath, finalSystemKey, datasourceConfigs) {
66
+ async function writeDatasourceYamlFiles(appPath, finalSystemKey, datasourceConfigs) {
66
67
  const datasourceFileNames = [];
67
68
  for (const datasourceConfig of datasourceConfigs) {
68
69
  const entityType = datasourceConfig.entityType || datasourceConfig.entityKey || datasourceConfig.key?.split('-').pop() || 'default';
@@ -72,9 +73,9 @@ async function writeDatasourceJsonFiles(appPath, finalSystemKey, datasourceConfi
72
73
  const datasourceKeyOnly = datasourceKey.includes('-') && datasourceKey.startsWith(`${finalSystemKey}-`)
73
74
  ? datasourceKey.substring(finalSystemKey.length + 1)
74
75
  : keySegment;
75
- const datasourceFileName = `${finalSystemKey}-datasource-${datasourceKeyOnly}.json`;
76
+ const datasourceFileName = `${finalSystemKey}-datasource-${datasourceKeyOnly}.yaml`;
76
77
  const datasourceFilePath = path.join(appPath, datasourceFileName);
77
- await fs.writeFile(datasourceFilePath, JSON.stringify(datasourceConfig, null, 2), 'utf8');
78
+ writeConfigFile(datasourceFilePath, datasourceConfig);
78
79
  datasourceFileNames.push(datasourceFileName);
79
80
  logger.log(chalk.green(`✓ Generated datasource file: ${datasourceFileName}`));
80
81
  }
@@ -99,8 +100,8 @@ async function writeDatasourceJsonFiles(appPath, finalSystemKey, datasourceConfi
99
100
  async function generateConfigFilesForWizard(params) {
100
101
  const { appPath, appName, finalSystemKey, systemFileName, datasourceFileNames, systemConfig, datasourceConfigs, aiGeneratedReadme } = params;
101
102
 
102
- // Generate or update variables.yaml with externalIntegration block
103
- await generateOrUpdateVariablesYaml({
103
+ // Generate or update application.yaml with externalIntegration block
104
+ const configPath = await generateOrUpdateVariablesYaml({
104
105
  appPath,
105
106
  appName,
106
107
  systemKey: finalSystemKey,
@@ -126,7 +127,7 @@ async function generateConfigFilesForWizard(params) {
126
127
  logger.log(chalk.green(`✓ Generated deployment manifest: ${finalSystemKey}-deploy.json`));
127
128
 
128
129
  return {
129
- variablesPath: path.join(appPath, 'variables.yaml'),
130
+ variablesPath: configPath,
130
131
  envTemplatePath: path.join(appPath, 'env.template'),
131
132
  readmePath: path.join(appPath, 'README.md'),
132
133
  applicationSchemaPath: deployManifestPath,
@@ -134,48 +135,28 @@ async function generateConfigFilesForWizard(params) {
134
135
  };
135
136
  }
136
137
 
138
+ async function prepareWizardContext(appName, systemConfig, datasourceConfigs) {
139
+ const appPath = path.join(process.cwd(), 'integration', appName);
140
+ await fs.mkdir(appPath, { recursive: true });
141
+ const finalSystemKey = appName;
142
+ const appDisplayName = appName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
143
+ const updatedSystemConfig = { ...systemConfig, key: finalSystemKey, displayName: appDisplayName };
144
+ const updatedDatasourceConfigs = datasourceConfigs.map(ds => {
145
+ const entityType = ds.entityType || ds.entityKey || ds.key?.split('-').pop() || 'default';
146
+ const keySegment = toKeySegment(entityType);
147
+ const entityDisplayName = entityType.charAt(0).toUpperCase() + entityType.slice(1).replace(/-/g, ' ');
148
+ return { ...ds, key: `${finalSystemKey}-${keySegment}`, systemKey: finalSystemKey, displayName: `${appDisplayName} ${entityDisplayName}` };
149
+ });
150
+ return { appPath, finalSystemKey, updatedSystemConfig, updatedDatasourceConfigs, appDisplayName };
151
+ }
152
+
137
153
  async function generateWizardFiles(appName, systemConfig, datasourceConfigs, systemKey, options = {}) {
138
154
  try {
139
155
  const { aiGeneratedReadme } = options || {};
140
- // Determine app path (integration directory for external systems)
141
- const appPath = path.join(process.cwd(), 'integration', appName);
142
-
143
- // Create directory if it doesn't exist
144
- await fs.mkdir(appPath, { recursive: true });
145
-
146
- // Use appName as the system key to ensure consistent naming
147
- // Priority: appName > systemKey parameter > systemConfig.key
148
- const finalSystemKey = appName;
149
-
150
- // Generate displayName from appName (e.g., "my-hubspot" -> "My Hubspot")
151
- const appDisplayName = appName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
152
-
153
- // Update system config to use the appName as key and displayName
154
- const updatedSystemConfig = {
155
- ...systemConfig,
156
- key: finalSystemKey,
157
- displayName: appDisplayName
158
- };
159
-
160
- // Update datasource configs to use appName-based keys and systemKey (key must match ^[a-z0-9-]+$)
161
- const updatedDatasourceConfigs = datasourceConfigs.map(ds => {
162
- const entityType = ds.entityType || ds.entityKey || ds.key?.split('-').pop() || 'default';
163
- const keySegment = toKeySegment(entityType);
164
- const entityDisplayName = entityType.charAt(0).toUpperCase() + entityType.slice(1).replace(/-/g, ' ');
165
- return {
166
- ...ds,
167
- key: `${finalSystemKey}-${keySegment}`,
168
- systemKey: finalSystemKey,
169
- displayName: `${appDisplayName} ${entityDisplayName}`
170
- };
171
- });
172
-
173
- // Write system and datasource JSON files
174
- const systemFilePath = await writeSystemJsonFile(appPath, finalSystemKey, updatedSystemConfig);
175
- const datasourceFileNames = await writeDatasourceJsonFiles(appPath, finalSystemKey, updatedDatasourceConfigs);
176
-
177
- // Generate configuration files
178
- const systemFileName = `${finalSystemKey}-system.json`;
156
+ const { appPath, finalSystemKey, updatedSystemConfig, updatedDatasourceConfigs } = await prepareWizardContext(appName, systemConfig, datasourceConfigs);
157
+ const systemFilePath = await writeSystemYamlFile(appPath, finalSystemKey, updatedSystemConfig);
158
+ const datasourceFileNames = await writeDatasourceYamlFiles(appPath, finalSystemKey, updatedDatasourceConfigs);
159
+ const systemFileName = `${finalSystemKey}-system.yaml`;
179
160
  const configFiles = await generateConfigFilesForWizard({
180
161
  appPath,
181
162
  appName,
@@ -186,7 +167,6 @@ async function generateWizardFiles(appName, systemConfig, datasourceConfigs, sys
186
167
  datasourceConfigs: updatedDatasourceConfigs,
187
168
  aiGeneratedReadme
188
169
  });
189
-
190
170
  return {
191
171
  appPath,
192
172
  systemFilePath,
@@ -199,7 +179,7 @@ async function generateWizardFiles(appName, systemConfig, datasourceConfigs, sys
199
179
  }
200
180
 
201
181
  /**
202
- * Generate or update variables.yaml with externalIntegration block
182
+ * Generate or update application.yaml with externalIntegration block
203
183
  * @async
204
184
  * @function generateOrUpdateVariablesYaml
205
185
  * @param {Object} params - Parameters object
@@ -209,23 +189,19 @@ async function generateWizardFiles(appName, systemConfig, datasourceConfigs, sys
209
189
  * @param {string} params.systemFileName - System file name
210
190
  * @param {string[]} params.datasourceFileNames - Array of datasource file names
211
191
  * @param {Object} params.systemConfig - System configuration
192
+ * @returns {Promise<string>} Path to application config file
212
193
  * @throws {Error} If generation fails
213
194
  */
214
195
  async function generateOrUpdateVariablesYaml(params) {
215
196
  const { appPath, appName, systemFileName, datasourceFileNames, systemConfig } = params;
197
+ let configPath;
198
+ let variables = {};
216
199
  try {
217
- const variablesPath = path.join(appPath, 'variables.yaml');
218
- let variables = {};
219
-
220
- // Try to read existing variables.yaml
221
200
  try {
222
- const existingContent = await fs.readFile(variablesPath, 'utf8');
223
- variables = yaml.load(existingContent) || {};
224
- } catch (error) {
225
- // File doesn't exist, create new one
226
- if (error.code !== 'ENOENT') {
227
- throw error;
228
- }
201
+ configPath = resolveApplicationConfigPath(appPath);
202
+ variables = loadConfigFile(configPath) || {};
203
+ } catch {
204
+ configPath = path.join(appPath, 'application.yaml');
229
205
  }
230
206
 
231
207
  // Set basic app info if not present
@@ -258,10 +234,11 @@ async function generateOrUpdateVariablesYaml(params) {
258
234
  version: systemConfig.version || '1.0.0'
259
235
  };
260
236
 
261
- await fs.writeFile(variablesPath, yaml.dump(variables, { indent: 2, lineWidth: 120, noRefs: true }), 'utf8');
262
- logger.log(chalk.green('✓ Generated/updated variables.yaml'));
237
+ writeConfigFile(configPath, variables);
238
+ logger.log(chalk.green('✓ Generated/updated application.yaml'));
239
+ return configPath;
263
240
  } catch (error) {
264
- throw new Error(`Failed to generate variables.yaml: ${error.message}`);
241
+ throw new Error(`Failed to generate application config: ${error.message}`);
265
242
  }
266
243
  }
267
244
 
@@ -445,7 +422,7 @@ async function generateReadme(appPath, appName, systemKey, systemConfig, datasou
445
422
  return {
446
423
  entityType,
447
424
  displayName: ds.displayName || ds.name || ds.key || `Datasource ${index + 1}`,
448
- fileName: `${systemKey}-datasource-${datasourceKeyOnly}.json`
425
+ fileName: `${systemKey}-datasource-${datasourceKeyOnly}.yaml`
449
426
  };
450
427
  });
451
428
 
@@ -799,19 +799,19 @@
799
799
  },
800
800
  "systems": {
801
801
  "type": "array",
802
- "description": "List of external-system JSON files to deploy via pipeline.",
802
+ "description": "List of external-system files to deploy via pipeline (.json, .yaml, or .yml).",
803
803
  "items": {
804
804
  "type": "string",
805
- "pattern": "^[^ ].+\\.json$"
805
+ "pattern": "^[^ ].+\\.(json|yaml|yml)$"
806
806
  },
807
807
  "uniqueItems": true
808
808
  },
809
809
  "dataSources": {
810
810
  "type": "array",
811
- "description": "List of external-datasource JSON files belonging to this app.",
811
+ "description": "List of external-datasource files belonging to this app (.json, .yaml, or .yml).",
812
812
  "items": {
813
813
  "type": "string",
814
- "pattern": "^[^ ].+\\.json$"
814
+ "pattern": "^[^ ].+\\.(json|yaml|yml)$"
815
815
  },
816
816
  "uniqueItems": true
817
817
  },
@@ -49,7 +49,6 @@ application:
49
49
  overridablePaths:
50
50
  - configuration.items.value
51
51
  - authentication.endpoints
52
- - deployment.controllerUrl
53
52
  - healthCheck.interval
54
53
  - healthCheck.probeIntervalInSeconds
55
54
 
@@ -60,7 +59,6 @@ externalSystem:
60
59
  - description
61
60
  - type
62
61
  - enabled
63
- - environment
64
62
  - authentication
65
63
  - openapi
66
64
  - mcp
@@ -75,8 +73,6 @@ externalSystem:
75
73
  - generateMcpContract
76
74
  - generateOpenApiContract
77
75
  overridablePaths:
78
- - environment.baseUrl
79
- - environment.region
80
76
  - authentication.oauth2
81
77
  - authentication.apikey
82
78
  - authentication.basic
@@ -1587,6 +1587,11 @@
1587
1587
  }
1588
1588
  },
1589
1589
  "additionalProperties":false
1590
+ },
1591
+ "triggerPathsHash":{
1592
+ "type":"string",
1593
+ "description":"SHA256 hash of triggerPaths payload (64-char hex). Used to detect structural changes. Optional; Dataplane computes when absent.",
1594
+ "pattern":"^[a-f0-9]{64}$"
1590
1595
  }
1591
1596
  },
1592
1597
  "additionalProperties":false
@@ -102,6 +102,11 @@
102
102
  "type": "boolean",
103
103
  "default": true
104
104
  },
105
+ "internal": {
106
+ "type": "boolean",
107
+ "description": "When true, this integration is deployed on dataplane startup (internal integration). When false or absent, deployed via pipeline only.",
108
+ "default": false
109
+ },
105
110
  "authentication": {
106
111
  "type": "object",
107
112
  "description": "Authentication configuration for external system",
@@ -414,6 +419,11 @@
414
419
  "type": "boolean",
415
420
  "description": "Reserved: whether to generate or expose OpenAPI contract on publish. Not yet implemented.",
416
421
  "default": true
422
+ },
423
+ "triggerPathsHash": {
424
+ "type": "string",
425
+ "description": "SHA256 hash of triggerPaths payload (64-char hex). Used to detect structural changes. Optional; Dataplane computes when absent.",
426
+ "pattern": "^[a-f0-9]{64}$"
417
427
  }
418
428
  },
419
429
  "additionalProperties": false