@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.
- package/.cursor/rules/project-rules.mdc +6 -6
- package/README.md +2 -2
- package/babel.config.js +6 -0
- package/integration/hubspot/README.md +53 -141
- package/integration/hubspot/application.yaml +37 -0
- package/integration/hubspot/env.template +2 -11
- package/integration/hubspot/hubspot-deploy.json +1 -0
- package/integration/hubspot/test.js +5 -5
- package/lib/api/credentials.api.js +5 -5
- package/lib/api/deployments.api.js +2 -2
- package/lib/api/pipeline.api.js +17 -17
- package/lib/api/wizard.api.js +2 -2
- package/lib/app/config.js +11 -6
- package/lib/app/deploy-config.js +13 -16
- package/lib/app/deploy.js +29 -22
- package/lib/app/display.js +1 -1
- package/lib/app/dockerfile.js +11 -12
- package/lib/app/helpers.js +51 -13
- package/lib/app/index.js +14 -2
- package/lib/app/prompts.js +37 -45
- package/lib/app/push.js +8 -11
- package/lib/app/readme.js +16 -12
- package/lib/app/register.js +3 -3
- package/lib/app/run-helpers.js +31 -22
- package/lib/app/run.js +44 -5
- package/lib/app/show-display.js +104 -44
- package/lib/app/show.js +123 -43
- package/lib/build/index.js +11 -18
- package/lib/cli/setup-app.js +38 -28
- package/lib/cli/setup-auth.js +18 -15
- package/lib/cli/setup-credential-deployment.js +3 -1
- package/lib/cli/setup-external-system.js +35 -16
- package/lib/cli/setup-infra.js +45 -23
- package/lib/cli/setup-utility.js +79 -31
- package/lib/commands/app-logs.js +165 -10
- package/lib/commands/app.js +30 -26
- package/lib/commands/convert.js +202 -0
- package/lib/commands/credential-list.js +78 -17
- package/lib/commands/datasource.js +24 -24
- package/lib/commands/deployment-list.js +13 -6
- package/lib/commands/up-common.js +80 -42
- package/lib/commands/up-dataplane.js +15 -14
- package/lib/commands/up-miso.js +15 -14
- package/lib/commands/upload.js +163 -0
- package/lib/commands/wizard-core.js +5 -4
- package/lib/core/diff.js +84 -9
- package/lib/core/key-generator.js +9 -12
- package/lib/core/secrets-docker-env.js +2 -2
- package/lib/core/secrets.js +3 -2
- package/lib/core/templates.js +2 -2
- package/lib/datasource/deploy.js +2 -1
- package/lib/deployment/deployer.js +76 -48
- package/lib/external-system/delete.js +0 -1
- package/lib/external-system/deploy-helpers.js +5 -6
- package/lib/external-system/deploy.js +7 -2
- package/lib/external-system/download-helpers.js +4 -4
- package/lib/external-system/download.js +11 -10
- package/lib/external-system/generator.js +19 -17
- package/lib/external-system/test.js +10 -15
- package/lib/generator/builders.js +1 -1
- package/lib/generator/external-controller-manifest.js +26 -29
- package/lib/generator/external-schema-utils.js +6 -18
- package/lib/generator/external.js +32 -27
- package/lib/generator/github.js +1 -1
- package/lib/generator/helpers.js +12 -19
- package/lib/generator/index.js +15 -15
- package/lib/generator/parse-image.js +35 -0
- package/lib/generator/split-readme.js +105 -0
- package/lib/generator/split-variables.js +149 -0
- package/lib/generator/split.js +86 -246
- package/lib/generator/wizard.js +46 -69
- package/lib/schema/application-schema.json +4 -4
- package/lib/schema/deployment-rules.yaml +0 -4
- package/lib/schema/external-datasource.schema.json +5 -0
- package/lib/schema/external-system.schema.json +10 -0
- package/lib/utils/app-config-resolver.js +52 -0
- package/lib/utils/app-register-api.js +1 -1
- package/lib/utils/app-register-auth.js +1 -1
- package/lib/utils/app-register-config.js +16 -23
- package/lib/utils/app-register-display.js +22 -3
- package/lib/utils/app-register-validator.js +2 -2
- package/lib/utils/cli-utils.js +47 -3
- package/lib/utils/config-format.js +154 -0
- package/lib/utils/config-paths.js +19 -52
- package/lib/utils/config-tokens.js +1 -0
- package/lib/utils/docker-build.js +71 -94
- package/lib/utils/dockerfile-utils.js +1 -1
- package/lib/utils/env-copy.js +4 -4
- package/lib/utils/env-ports.js +2 -2
- package/lib/utils/error-formatter.js +1 -1
- package/lib/utils/error-formatters/validation-errors.js +1 -1
- package/lib/utils/external-readme.js +12 -5
- package/lib/utils/external-system-test-helpers.js +2 -0
- package/lib/utils/health-check.js +55 -66
- package/lib/utils/image-version.js +12 -21
- package/lib/utils/paths.js +39 -66
- package/lib/utils/port-resolver.js +8 -8
- package/lib/utils/schema-loader.js +22 -0
- package/lib/utils/schema-resolver.js +23 -33
- package/lib/utils/secrets-helpers.js +7 -7
- package/lib/utils/secrets-utils.js +10 -12
- package/lib/utils/template-helpers.js +13 -13
- package/lib/utils/token-manager.js +20 -2
- package/lib/utils/variable-transformer.js +2 -2
- package/lib/validation/validate-display.js +3 -4
- package/lib/validation/validate.js +33 -27
- package/lib/validation/validator.js +50 -30
- package/package.json +2 -1
- package/templates/README.md +1 -1
- package/templates/applications/README.md.hbs +3 -3
- package/templates/applications/miso-controller/env.template +3 -1
- package/templates/external-system/README.md.hbs +4 -4
- package/integration/hubspot/variables.yaml +0 -17
- /package/templates/applications/dataplane/{variables.yaml → application.yaml} +0 -0
- /package/templates/applications/keycloak/{variables.yaml → application.yaml} +0 -0
- /package/templates/applications/miso-controller/{variables.yaml → application.yaml} +0 -0
package/lib/utils/paths.js
CHANGED
|
@@ -371,27 +371,11 @@ function getDeployJsonPath(appName, appType, preferNew = false) {
|
|
|
371
371
|
// If neither exists, return new naming (for generation)
|
|
372
372
|
return newPath;
|
|
373
373
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
* Reads and parses variables.yaml file
|
|
377
|
-
* @param {string} variablesPath - Path to variables.yaml file
|
|
378
|
-
* @returns {Object|null} Parsed variables object or null if error
|
|
379
|
-
*/
|
|
380
|
-
function readVariablesFile(variablesPath) {
|
|
381
|
-
try {
|
|
382
|
-
if (!fs.existsSync(variablesPath)) {
|
|
383
|
-
return null;
|
|
384
|
-
}
|
|
385
|
-
const content = fs.readFileSync(variablesPath, 'utf8');
|
|
386
|
-
return yaml.load(content);
|
|
387
|
-
} catch {
|
|
388
|
-
return null;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
374
|
+
const { resolveApplicationConfigPath } = require('./app-config-resolver');
|
|
375
|
+
const { loadConfigFile } = require('./config-format');
|
|
392
376
|
/**
|
|
393
377
|
* Checks if app type is external from variables object
|
|
394
|
-
* @param {Object} variables - Parsed
|
|
378
|
+
* @param {Object} variables - Parsed application config object
|
|
395
379
|
* @returns {boolean} True if app type is external
|
|
396
380
|
*/
|
|
397
381
|
function isExternalAppType(variables) {
|
|
@@ -399,20 +383,25 @@ function isExternalAppType(variables) {
|
|
|
399
383
|
}
|
|
400
384
|
|
|
401
385
|
/**
|
|
402
|
-
* Checks integration folder for
|
|
386
|
+
* Checks integration folder for any valid application config
|
|
403
387
|
* @param {string} appName - Application name
|
|
404
|
-
* @returns {Object|null} App type info or null if
|
|
388
|
+
* @returns {Object|null} App type info or null if no config found
|
|
405
389
|
*/
|
|
406
390
|
function checkIntegrationFolder(appName) {
|
|
407
391
|
const integrationPath = getIntegrationPath(appName);
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
392
|
+
let variables;
|
|
393
|
+
try {
|
|
394
|
+
const configPath = resolveApplicationConfigPath(integrationPath);
|
|
395
|
+
variables = loadConfigFile(configPath);
|
|
396
|
+
} catch {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
if (variables) {
|
|
400
|
+
const isExternal = isExternalAppType(variables);
|
|
412
401
|
return {
|
|
413
|
-
isExternal
|
|
402
|
+
isExternal,
|
|
414
403
|
appPath: integrationPath,
|
|
415
|
-
appType: 'external',
|
|
404
|
+
appType: isExternal ? 'external' : 'regular',
|
|
416
405
|
baseDir: 'integration'
|
|
417
406
|
};
|
|
418
407
|
}
|
|
@@ -422,13 +411,20 @@ function checkIntegrationFolder(appName) {
|
|
|
422
411
|
/**
|
|
423
412
|
* Checks builder folder for app type
|
|
424
413
|
* @param {string} appName - Application name
|
|
425
|
-
* @returns {Object} App type info
|
|
414
|
+
* @returns {Object|null} App type info or null if no config found
|
|
426
415
|
*/
|
|
427
416
|
function checkBuilderFolder(appName) {
|
|
428
417
|
const builderPath = getBuilderPath(appName);
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
418
|
+
if (!fs.existsSync(builderPath)) {
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
421
|
+
let variables;
|
|
422
|
+
try {
|
|
423
|
+
const configPath = resolveApplicationConfigPath(builderPath);
|
|
424
|
+
variables = loadConfigFile(configPath);
|
|
425
|
+
} catch {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
432
428
|
if (variables) {
|
|
433
429
|
const isExternal = isExternalAppType(variables);
|
|
434
430
|
return {
|
|
@@ -438,51 +434,27 @@ function checkBuilderFolder(appName) {
|
|
|
438
434
|
baseDir: 'builder'
|
|
439
435
|
};
|
|
440
436
|
}
|
|
441
|
-
|
|
442
|
-
// Default to regular app in builder folder
|
|
443
|
-
return {
|
|
444
|
-
isExternal: false,
|
|
445
|
-
appPath: builderPath,
|
|
446
|
-
appType: 'regular',
|
|
447
|
-
baseDir: 'builder'
|
|
448
|
-
};
|
|
437
|
+
return null;
|
|
449
438
|
}
|
|
450
|
-
|
|
451
439
|
/**
|
|
452
|
-
* Detects if an app is external type by checking
|
|
453
|
-
*
|
|
440
|
+
* Detects if an app is external type by checking application config.
|
|
441
|
+
* Resolution order: integration/ first, then builder/; if neither exists, throws.
|
|
442
|
+
* No CLI flag overrides this order.
|
|
454
443
|
*
|
|
455
444
|
* @param {string} appName - Application name
|
|
456
|
-
* @param {Object} [options] - Detection options
|
|
457
|
-
* @param {string} [options.type] - Forced application type (external)
|
|
445
|
+
* @param {Object} [options] - Detection options (reserved; options.type is ignored)
|
|
458
446
|
* @returns {Promise<{isExternal: boolean, appPath: string, appType: string, baseDir?: string}>}
|
|
447
|
+
* @throws {Error} When app not found in integration/ or builder/
|
|
459
448
|
*/
|
|
460
|
-
async function detectAppType(appName,
|
|
449
|
+
async function detectAppType(appName, _options = {}) {
|
|
461
450
|
if (!appName || typeof appName !== 'string') {
|
|
462
451
|
throw new Error('App name is required and must be a string');
|
|
463
452
|
}
|
|
464
|
-
|
|
465
|
-
if (options.type === 'external') {
|
|
466
|
-
const integrationPath = getIntegrationPath(appName);
|
|
467
|
-
if (!fs.existsSync(integrationPath)) {
|
|
468
|
-
throw new Error(`External system not found in integration/${appName}`);
|
|
469
|
-
}
|
|
470
|
-
return {
|
|
471
|
-
isExternal: true,
|
|
472
|
-
appPath: integrationPath,
|
|
473
|
-
appType: 'external',
|
|
474
|
-
baseDir: 'integration'
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// Check integration folder first (new structure)
|
|
479
453
|
const integrationResult = checkIntegrationFolder(appName);
|
|
480
|
-
if (integrationResult)
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
// Check builder folder (backward compatibility)
|
|
485
|
-
return checkBuilderFolder(appName);
|
|
454
|
+
if (integrationResult) return integrationResult;
|
|
455
|
+
const builderResult = checkBuilderFolder(appName);
|
|
456
|
+
if (builderResult) return builderResult;
|
|
457
|
+
throw new Error(`App '${appName}' not found in integration/${appName} or builder/${appName}`);
|
|
486
458
|
}
|
|
487
459
|
module.exports = {
|
|
488
460
|
getAifabrixHome,
|
|
@@ -494,6 +466,7 @@ module.exports = {
|
|
|
494
466
|
getIntegrationPath,
|
|
495
467
|
getBuilderPath,
|
|
496
468
|
getDeployJsonPath,
|
|
469
|
+
resolveApplicationConfigPath,
|
|
497
470
|
detectAppType,
|
|
498
471
|
clearProjectRootCache
|
|
499
472
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AI Fabrix Builder - Centralized Port Resolution
|
|
3
3
|
*
|
|
4
|
-
* Single source of truth for resolving application port from
|
|
4
|
+
* Single source of truth for resolving application port from application config.
|
|
5
5
|
* Use getContainerPort for container/Docker/deployment/registration; use getLocalPort
|
|
6
6
|
* for local .env and dev-id–adjusted host port.
|
|
7
7
|
*
|
|
@@ -20,7 +20,7 @@ const yaml = require('js-yaml');
|
|
|
20
20
|
* Precedence: build.containerPort → port → defaultPort.
|
|
21
21
|
* Used for: Dockerfile, container .env PORT, compose, deployment, app register, variable-transformer, builders, secrets-utils.
|
|
22
22
|
*
|
|
23
|
-
* @param {Object} variables - Parsed
|
|
23
|
+
* @param {Object} variables - Parsed application config (or subset with build, port)
|
|
24
24
|
* @param {number} [defaultPort=3000] - Default when neither build.containerPort nor port is set
|
|
25
25
|
* @returns {number} Resolved container port
|
|
26
26
|
*/
|
|
@@ -34,7 +34,7 @@ function getContainerPort(variables, defaultPort = 3000) {
|
|
|
34
34
|
* Precedence: build.localPort (if number and > 0) → port → defaultPort.
|
|
35
35
|
* Used for: env-copy, env-ports, and as base for getLocalPortFromPath (secrets-helpers).
|
|
36
36
|
*
|
|
37
|
-
* @param {Object} variables - Parsed
|
|
37
|
+
* @param {Object} variables - Parsed application config
|
|
38
38
|
* @param {number} [defaultPort=3000] - Default when neither build.localPort nor port is set
|
|
39
39
|
* @returns {number} Resolved local port
|
|
40
40
|
*/
|
|
@@ -50,7 +50,7 @@ function getLocalPort(variables, defaultPort = 3000) {
|
|
|
50
50
|
/**
|
|
51
51
|
* Load variables from path. Returns null if path missing, not found, or parse error.
|
|
52
52
|
*
|
|
53
|
-
* @param {string} variablesPath - Path to
|
|
53
|
+
* @param {string} variablesPath - Path to application config
|
|
54
54
|
* @returns {Object|null} Parsed variables or null
|
|
55
55
|
*/
|
|
56
56
|
function loadVariablesFromPath(variablesPath) {
|
|
@@ -66,10 +66,10 @@ function loadVariablesFromPath(variablesPath) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
* Resolve container port from
|
|
69
|
+
* Resolve container port from application config path.
|
|
70
70
|
* Returns null when file is missing or neither build.containerPort nor port is set (for chaining with other sources).
|
|
71
71
|
*
|
|
72
|
-
* @param {string} variablesPath - Path to
|
|
72
|
+
* @param {string} variablesPath - Path to application config
|
|
73
73
|
* @returns {number|null} Container port or null
|
|
74
74
|
*/
|
|
75
75
|
function getContainerPortFromPath(variablesPath) {
|
|
@@ -82,11 +82,11 @@ function getContainerPortFromPath(variablesPath) {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
* Resolve local port from
|
|
85
|
+
* Resolve local port from application config path.
|
|
86
86
|
* Matches legacy getPortFromVariablesFile: build.localPort (if number and > 0) else variables.port or null.
|
|
87
87
|
* Returns null when file is missing or neither is set (for calculateAppPort chain).
|
|
88
88
|
*
|
|
89
|
-
* @param {string} variablesPath - Path to
|
|
89
|
+
* @param {string} variablesPath - Path to application config
|
|
90
90
|
* @returns {number|null} Local port or null
|
|
91
91
|
*/
|
|
92
92
|
function getLocalPortFromPath(variablesPath) {
|
|
@@ -290,6 +290,27 @@ function tryDetectionMethods(parsed, filePath) {
|
|
|
290
290
|
return null;
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
/**
|
|
294
|
+
* Detects schema type from already-parsed content (works with YAML or JSON).
|
|
295
|
+
* Use this when the file was parsed via loadConfigFile or similar.
|
|
296
|
+
*
|
|
297
|
+
* @function detectSchemaTypeFromParsed
|
|
298
|
+
* @param {Object} parsed - Parsed config object (from YAML or JSON)
|
|
299
|
+
* @param {string} filePath - File path (used for filename-based detection)
|
|
300
|
+
* @returns {string} 'application' | 'external-system' | 'external-datasource'
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* const parsed = yaml.load(content);
|
|
304
|
+
* const type = detectSchemaTypeFromParsed(parsed, '/path/to/application.yaml');
|
|
305
|
+
*/
|
|
306
|
+
function detectSchemaTypeFromParsed(parsed, filePath) {
|
|
307
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
308
|
+
return 'application';
|
|
309
|
+
}
|
|
310
|
+
const detected = tryDetectionMethods(parsed, filePath);
|
|
311
|
+
return detected || 'application';
|
|
312
|
+
}
|
|
313
|
+
|
|
293
314
|
function detectSchemaType(filePath, content) {
|
|
294
315
|
const parsed = readAndParseFileContent(filePath, content);
|
|
295
316
|
const detectedType = tryDetectionMethods(parsed, filePath);
|
|
@@ -300,6 +321,7 @@ module.exports = {
|
|
|
300
321
|
loadExternalSystemSchema,
|
|
301
322
|
loadExternalDataSourceSchema,
|
|
302
323
|
detectSchemaType,
|
|
324
|
+
detectSchemaTypeFromParsed,
|
|
303
325
|
resetValidators
|
|
304
326
|
};
|
|
305
327
|
|
|
@@ -11,43 +11,40 @@
|
|
|
11
11
|
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const path = require('path');
|
|
14
|
-
const yaml = require('js-yaml');
|
|
15
14
|
const { detectAppType } = require('./paths');
|
|
15
|
+
const { resolveApplicationConfigPath } = require('./app-config-resolver');
|
|
16
|
+
const { loadConfigFile } = require('./config-format');
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
|
-
* Resolves schemaBasePath from application
|
|
19
|
+
* Resolves schemaBasePath from application config
|
|
19
20
|
* Supports both absolute and relative paths
|
|
20
21
|
*
|
|
21
22
|
* @async
|
|
22
23
|
* @function resolveSchemaBasePath
|
|
23
24
|
* @param {string} appName - Application name
|
|
24
25
|
* @returns {Promise<string>} Resolved absolute path to schema base directory
|
|
25
|
-
* @throws {Error} If
|
|
26
|
+
* @throws {Error} If application config not found, externalIntegration missing, or path invalid
|
|
26
27
|
*
|
|
27
28
|
* @example
|
|
28
29
|
* const basePath = await resolveSchemaBasePath('myapp');
|
|
29
30
|
* // Returns: '/path/to/builder/myapp/schemas'
|
|
30
31
|
*/
|
|
31
32
|
/**
|
|
32
|
-
* Loads and validates
|
|
33
|
+
* Loads and validates application config for schema resolution
|
|
33
34
|
* @async
|
|
34
35
|
* @function loadAndValidateVariablesForSchema
|
|
35
36
|
* @param {string} appName - Application name
|
|
36
37
|
* @param {string} appPath - Application path
|
|
37
|
-
* @returns {Promise<Object>} Variables object
|
|
38
|
+
* @returns {Promise<{ variables: Object, configPath: string }>} Variables object and config path
|
|
38
39
|
* @throws {Error} If file not found or invalid
|
|
39
40
|
*/
|
|
40
41
|
async function loadAndValidateVariablesForSchema(appName, appPath) {
|
|
41
|
-
const
|
|
42
|
-
if (!fs.existsSync(variablesPath)) {
|
|
43
|
-
throw new Error(`variables.yaml not found: ${variablesPath}`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const content = fs.readFileSync(variablesPath, 'utf8');
|
|
42
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
47
43
|
try {
|
|
48
|
-
|
|
44
|
+
const variables = loadConfigFile(configPath);
|
|
45
|
+
return { variables, configPath };
|
|
49
46
|
} catch (error) {
|
|
50
|
-
throw new Error(`
|
|
47
|
+
throw new Error(`Application config error: ${error.message}`);
|
|
51
48
|
}
|
|
52
49
|
}
|
|
53
50
|
|
|
@@ -61,7 +58,7 @@ async function loadAndValidateVariablesForSchema(appName, appPath) {
|
|
|
61
58
|
*/
|
|
62
59
|
function validateExternalIntegrationBlock(variables, appName) {
|
|
63
60
|
if (!variables.externalIntegration) {
|
|
64
|
-
throw new Error(`externalIntegration block not found in
|
|
61
|
+
throw new Error(`externalIntegration block not found in application config for app: ${appName}`);
|
|
65
62
|
}
|
|
66
63
|
if (!variables.externalIntegration.schemaBasePath) {
|
|
67
64
|
throw new Error(`schemaBasePath not found in externalIntegration block for app: ${appName}`);
|
|
@@ -73,7 +70,7 @@ function validateExternalIntegrationBlock(variables, appName) {
|
|
|
73
70
|
* Resolves and validates schema base path
|
|
74
71
|
* @function resolveAndValidateSchemaPath
|
|
75
72
|
* @param {string} schemaBasePath - Schema base path from config
|
|
76
|
-
* @param {string} variablesPath - Path to
|
|
73
|
+
* @param {string} variablesPath - Path to application config
|
|
77
74
|
* @returns {string} Resolved and validated path
|
|
78
75
|
* @throws {Error} If path is invalid
|
|
79
76
|
*/
|
|
@@ -95,17 +92,16 @@ function resolveAndValidateSchemaPath(schemaBasePath, variablesPath) {
|
|
|
95
92
|
return resolvedPath;
|
|
96
93
|
}
|
|
97
94
|
|
|
98
|
-
async function resolveSchemaBasePath(appName) {
|
|
95
|
+
async function resolveSchemaBasePath(appName, options = {}) {
|
|
99
96
|
if (!appName || typeof appName !== 'string') {
|
|
100
97
|
throw new Error('App name is required and must be a string');
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
const { appPath } = await detectAppType(appName);
|
|
104
|
-
const variables = await loadAndValidateVariablesForSchema(appName, appPath);
|
|
100
|
+
const { appPath } = await detectAppType(appName, options);
|
|
101
|
+
const { variables, configPath } = await loadAndValidateVariablesForSchema(appName, appPath);
|
|
105
102
|
const schemaBasePath = validateExternalIntegrationBlock(variables, appName);
|
|
106
|
-
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
107
103
|
|
|
108
|
-
return resolveAndValidateSchemaPath(schemaBasePath,
|
|
104
|
+
return resolveAndValidateSchemaPath(schemaBasePath, configPath);
|
|
109
105
|
}
|
|
110
106
|
|
|
111
107
|
/**
|
|
@@ -180,7 +176,7 @@ function resolveDatasourceFiles(schemaBasePath, datasourceFiles) {
|
|
|
180
176
|
}
|
|
181
177
|
|
|
182
178
|
/**
|
|
183
|
-
* Loads and validates
|
|
179
|
+
* Loads and validates application config
|
|
184
180
|
* @async
|
|
185
181
|
* @function loadAndValidateVariables
|
|
186
182
|
* @param {string} appPath - Application path
|
|
@@ -188,33 +184,27 @@ function resolveDatasourceFiles(schemaBasePath, datasourceFiles) {
|
|
|
188
184
|
* @throws {Error} If file not found or invalid
|
|
189
185
|
*/
|
|
190
186
|
async function loadAndValidateVariables(appPath) {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
if (!fs.existsSync(variablesPath)) {
|
|
194
|
-
throw new Error(`variables.yaml not found: ${variablesPath}`);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const content = fs.readFileSync(variablesPath, 'utf8');
|
|
187
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
198
188
|
try {
|
|
199
|
-
return
|
|
189
|
+
return loadConfigFile(configPath);
|
|
200
190
|
} catch (error) {
|
|
201
|
-
throw new Error(`
|
|
191
|
+
throw new Error(`Application config: ${error.message}`);
|
|
202
192
|
}
|
|
203
193
|
}
|
|
204
194
|
|
|
205
|
-
async function resolveExternalFiles(appName) {
|
|
195
|
+
async function resolveExternalFiles(appName, options = {}) {
|
|
206
196
|
if (!appName || typeof appName !== 'string') {
|
|
207
197
|
throw new Error('App name is required and must be a string');
|
|
208
198
|
}
|
|
209
199
|
|
|
210
|
-
const { appPath } = await detectAppType(appName);
|
|
200
|
+
const { appPath } = await detectAppType(appName, options);
|
|
211
201
|
const variables = await loadAndValidateVariables(appPath);
|
|
212
202
|
|
|
213
203
|
if (!variables.externalIntegration) {
|
|
214
204
|
return [];
|
|
215
205
|
}
|
|
216
206
|
|
|
217
|
-
const schemaBasePath = await resolveSchemaBasePath(appName);
|
|
207
|
+
const schemaBasePath = await resolveSchemaBasePath(appName, options);
|
|
218
208
|
const systemFiles = resolveSystemFiles(schemaBasePath, variables.externalIntegration.systems);
|
|
219
209
|
const datasourceFiles = resolveDatasourceFiles(schemaBasePath, variables.externalIntegration.dataSources);
|
|
220
210
|
|
|
@@ -166,9 +166,9 @@ function getPortFromLocalEnv(localEnv) {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
|
-
* Gets port from
|
|
169
|
+
* Gets port from application config file (build.localPort if positive, else port). Uses port-resolver.
|
|
170
170
|
* @function getPortFromVariablesFile
|
|
171
|
-
* @param {string} variablesPath - Path to
|
|
171
|
+
* @param {string} variablesPath - Path to application config
|
|
172
172
|
* @returns {number|null} Port value or null
|
|
173
173
|
*/
|
|
174
174
|
function getPortFromVariablesFile(variablesPath) {
|
|
@@ -199,10 +199,10 @@ function applyDeveloperIdAdjustment(baseAppPort, devIdNum) {
|
|
|
199
199
|
|
|
200
200
|
/**
|
|
201
201
|
* Calculate application port following override chain and developer-id adjustment
|
|
202
|
-
* Override chain: env-config.yaml → config.yaml →
|
|
202
|
+
* Override chain: env-config.yaml → config.yaml → application.yaml build.localPort → application.yaml port
|
|
203
203
|
* @async
|
|
204
204
|
* @function calculateAppPort
|
|
205
|
-
* @param {string} [variablesPath] - Path to
|
|
205
|
+
* @param {string} [variablesPath] - Path to application config
|
|
206
206
|
* @param {Object} localEnv - Local environment config from env-config.yaml and config.yaml
|
|
207
207
|
* @param {string} envContent - Environment content for fallback
|
|
208
208
|
* @param {number} devIdNum - Developer ID number
|
|
@@ -212,7 +212,7 @@ async function calculateAppPort(variablesPath, localEnv, envContent, devIdNum) {
|
|
|
212
212
|
// Start with env-config value
|
|
213
213
|
let baseAppPort = getPortFromLocalEnv(localEnv);
|
|
214
214
|
|
|
215
|
-
// Override with
|
|
215
|
+
// Override with application config → build.localPort (strongest)
|
|
216
216
|
const variablesPort = getPortFromVariablesFile(variablesPath);
|
|
217
217
|
if (variablesPort !== null) {
|
|
218
218
|
baseAppPort = variablesPort;
|
|
@@ -249,11 +249,11 @@ function updateLocalhostUrls(content, baseAppPort, appPort) {
|
|
|
249
249
|
/**
|
|
250
250
|
* Adjust infra-related ports in resolved .env content for local environment
|
|
251
251
|
* Only handles PORT variable (other ports handled by interpolation)
|
|
252
|
-
* Follows flow: getEnvHosts() → config.yaml override →
|
|
252
|
+
* Follows flow: getEnvHosts() → config.yaml override → application config override → developer-id adjustment
|
|
253
253
|
* @async
|
|
254
254
|
* @function adjustLocalEnvPortsInContent
|
|
255
255
|
* @param {string} envContent - Resolved .env content
|
|
256
|
-
* @param {string} [variablesPath] - Path to
|
|
256
|
+
* @param {string} [variablesPath] - Path to application config (to read build.localPort)
|
|
257
257
|
* @returns {Promise<string>} Updated content with local ports
|
|
258
258
|
*/
|
|
259
259
|
/**
|
|
@@ -119,7 +119,7 @@ function buildHostnameToServiceMap(dockerHosts) {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
* Resolves port for a single URL by looking up service's
|
|
122
|
+
* Resolves port for a single URL by looking up service's application config
|
|
123
123
|
* @function resolveUrlPort
|
|
124
124
|
* @param {string} protocol - URL protocol (http:// or https://)
|
|
125
125
|
* @param {string} hostname - Service hostname
|
|
@@ -135,24 +135,22 @@ function resolveUrlPort(protocol, hostname, port, urlPath, hostnameToService) {
|
|
|
135
135
|
return `${protocol}${hostname}:${port}${urlPath}`;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
const { resolveApplicationConfigPath } = require('./app-config-resolver');
|
|
139
|
+
const { loadConfigFile } = require('./config-format');
|
|
140
|
+
const builderPath = path.join(process.cwd(), 'builder', serviceName);
|
|
141
|
+
let serviceVariablesPath;
|
|
142
|
+
try {
|
|
143
|
+
serviceVariablesPath = resolveApplicationConfigPath(builderPath);
|
|
144
|
+
} catch {
|
|
142
145
|
return `${protocol}${hostname}:${port}${urlPath}`;
|
|
143
146
|
}
|
|
144
147
|
|
|
145
148
|
try {
|
|
146
|
-
const
|
|
147
|
-
const variables = yaml.load(variablesContent);
|
|
148
|
-
|
|
149
|
+
const variables = loadConfigFile(serviceVariablesPath);
|
|
149
150
|
const containerPort = getContainerPort(variables, port);
|
|
150
|
-
|
|
151
|
-
// Replace port in URL
|
|
152
151
|
return `${protocol}${hostname}:${containerPort}${urlPath}`;
|
|
153
152
|
} catch (error) {
|
|
154
|
-
|
|
155
|
-
logger.warn(`Warning: Could not load variables.yaml for service ${serviceName}: ${error.message}`);
|
|
153
|
+
logger.warn(`Warning: Could not load application config for service ${serviceName}: ${error.message}`);
|
|
156
154
|
return `${protocol}${hostname}:${port}${urlPath}`;
|
|
157
155
|
}
|
|
158
156
|
}
|
|
@@ -14,7 +14,7 @@ const chalk = require('chalk');
|
|
|
14
14
|
const logger = require('./logger');
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Loads template variables from template's
|
|
17
|
+
* Loads template variables from template's application.yaml file
|
|
18
18
|
* @async
|
|
19
19
|
* @function loadTemplateVariables
|
|
20
20
|
* @param {string} templateName - Template name
|
|
@@ -27,15 +27,15 @@ async function loadTemplateVariables(templateName) {
|
|
|
27
27
|
|
|
28
28
|
const yaml = require('js-yaml');
|
|
29
29
|
const templatePath = path.join(__dirname, '..', '..', 'templates', 'applications', templateName);
|
|
30
|
-
const templateVariablesPath = path.join(templatePath, '
|
|
30
|
+
const templateVariablesPath = path.join(templatePath, 'application.yaml');
|
|
31
31
|
|
|
32
32
|
try {
|
|
33
33
|
const templateContent = await fs.readFile(templateVariablesPath, 'utf8');
|
|
34
34
|
return yaml.load(templateContent);
|
|
35
35
|
} catch (error) {
|
|
36
|
-
// Template
|
|
36
|
+
// Template application.yaml not found or invalid, continue without it
|
|
37
37
|
if (error.code !== 'ENOENT') {
|
|
38
|
-
logger.warn(chalk.yellow(`⚠️ Warning: Could not load template
|
|
38
|
+
logger.warn(chalk.yellow(`⚠️ Warning: Could not load template application.yaml: ${error.message}`));
|
|
39
39
|
}
|
|
40
40
|
return null;
|
|
41
41
|
}
|
|
@@ -101,7 +101,7 @@ function updateDatabaseConfig(variables, options, appName) {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/**
|
|
104
|
-
* Updates
|
|
104
|
+
* Updates application config file after copying from template
|
|
105
105
|
* Updates app.key, displayName, and port with actual values
|
|
106
106
|
* @async
|
|
107
107
|
* @function updateTemplateVariables
|
|
@@ -111,21 +111,21 @@ function updateDatabaseConfig(variables, options, appName) {
|
|
|
111
111
|
* @param {Object} config - Final configuration
|
|
112
112
|
*/
|
|
113
113
|
async function updateTemplateVariables(appPath, appName, options, config) {
|
|
114
|
-
const
|
|
114
|
+
const { resolveApplicationConfigPath } = require('./app-config-resolver');
|
|
115
|
+
const { loadConfigFile, writeConfigFile } = require('./config-format');
|
|
115
116
|
try {
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const variables = yaml.load(variablesContent);
|
|
117
|
+
const variablesPath = resolveApplicationConfigPath(appPath);
|
|
118
|
+
const variables = loadConfigFile(variablesPath) || {};
|
|
119
119
|
|
|
120
120
|
updateAppMetadata(variables, appName);
|
|
121
121
|
updatePort(variables, options, config);
|
|
122
122
|
updateBuildConfig(variables);
|
|
123
123
|
updateDatabaseConfig(variables, options, appName);
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
writeConfigFile(variablesPath, variables);
|
|
126
126
|
} catch (error) {
|
|
127
|
-
if (error.
|
|
128
|
-
logger.warn(chalk.yellow(`⚠️ Warning: Could not update
|
|
127
|
+
if (error.message && !error.message.includes('not found')) {
|
|
128
|
+
logger.warn(chalk.yellow(`⚠️ Warning: Could not update application config: ${error.message}`));
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
}
|
|
@@ -197,7 +197,7 @@ function mergeAuthentication(merged, templateVariables) {
|
|
|
197
197
|
* Merges template variables into options
|
|
198
198
|
* @function mergeTemplateVariables
|
|
199
199
|
* @param {Object} options - User-provided options
|
|
200
|
-
* @param {Object} templateVariables - Template variables from
|
|
200
|
+
* @param {Object} templateVariables - Template variables from application.yaml
|
|
201
201
|
* @returns {Object} Merged options object
|
|
202
202
|
*/
|
|
203
203
|
function mergeTemplateVariables(options, templateVariables) {
|
|
@@ -427,6 +427,24 @@ async function getDeviceOnlyAuth(controllerUrl) {
|
|
|
427
427
|
throw new Error('Device token authentication required. Run "aifabrix login" to authenticate.');
|
|
428
428
|
}
|
|
429
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Ensures auth config has a Bearer token for Dataplane pipeline endpoints.
|
|
432
|
+
* Dataplane pipeline (upload, validate, publish, test) accepts OAuth2 (Bearer) or API_KEY only;
|
|
433
|
+
* client id/secret are rejected. Call this before invoking pipeline API functions.
|
|
434
|
+
* @param {Object} authConfig - Authentication configuration (may have token, clientId, clientSecret)
|
|
435
|
+
* @throws {Error} If authConfig has only client id/secret (no token)
|
|
436
|
+
*/
|
|
437
|
+
function requireBearerForDataplanePipeline(authConfig) {
|
|
438
|
+
if (authConfig.token) {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if (authConfig.clientId || authConfig.clientSecret) {
|
|
442
|
+
throw new Error(
|
|
443
|
+
'Dataplane pipeline endpoints require OAuth2 (Bearer token). Client id/secret are not accepted. Run "aifabrix login" to authenticate, then retry.'
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
430
448
|
module.exports = {
|
|
431
449
|
getDeviceToken,
|
|
432
450
|
getClientToken,
|
|
@@ -440,6 +458,6 @@ module.exports = {
|
|
|
440
458
|
forceRefreshDeviceToken,
|
|
441
459
|
getDeploymentAuth,
|
|
442
460
|
getDeviceOnlyAuth,
|
|
443
|
-
extractClientCredentials
|
|
461
|
+
extractClientCredentials,
|
|
462
|
+
requireBearerForDataplanePipeline
|
|
444
463
|
};
|
|
445
|
-
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Variable Transformation Utilities
|
|
3
3
|
*
|
|
4
|
-
* Transforms nested
|
|
4
|
+
* Transforms nested application config structure to flat schema format
|
|
5
5
|
* Converts app.*, image.*, requires.* to schema-compatible structure
|
|
6
6
|
*
|
|
7
7
|
* @fileoverview Variable transformation utilities for AI Fabrix Builder
|
|
@@ -309,7 +309,7 @@ function transformOptionalFields(variables, transformed) {
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
/**
|
|
312
|
-
* Transforms nested
|
|
312
|
+
* Transforms nested application config structure to flat schema format
|
|
313
313
|
* Converts app.*, image.*, requires.* to schema-compatible structure
|
|
314
314
|
* Handles both flat and nested structures
|
|
315
315
|
*
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* @version 2.0.0
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
const fs = require('fs');
|
|
12
11
|
const chalk = require('chalk');
|
|
13
12
|
const logger = require('../utils/logger');
|
|
13
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Displays application validation results
|
|
@@ -44,12 +44,11 @@ function displayApplicationValidation(application) {
|
|
|
44
44
|
* Extracts dimensions from a datasource file
|
|
45
45
|
* @function extractDimensionsFromDatasource
|
|
46
46
|
* @param {string} filePath - Path to datasource file
|
|
47
|
-
* @returns {Object} Dimensions info { dimensions: Object, hasDimensions: boolean }
|
|
47
|
+
* @returns {Object} Dimensions info { dimensions: Object, dimensionKeys: string[], hasDimensions: boolean }
|
|
48
48
|
*/
|
|
49
49
|
function extractDimensionsFromDatasource(filePath) {
|
|
50
50
|
try {
|
|
51
|
-
const
|
|
52
|
-
const parsed = JSON.parse(content);
|
|
51
|
+
const parsed = loadConfigFile(filePath);
|
|
53
52
|
|
|
54
53
|
// Check fieldMappings.dimensions (primary location)
|
|
55
54
|
const dimensions = parsed.fieldMappings?.dimensions || {};
|