@aifabrix/builder 2.39.3 → 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 +1 -1
- 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 +36 -29
- 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 +28 -20
- 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/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-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/app/show.js
CHANGED
|
@@ -13,10 +13,9 @@
|
|
|
13
13
|
'use strict';
|
|
14
14
|
|
|
15
15
|
const path = require('path');
|
|
16
|
-
const fs = require('fs');
|
|
17
|
-
const yaml = require('js-yaml');
|
|
18
16
|
const logger = require('../utils/logger');
|
|
19
|
-
const { detectAppType } = require('../utils/paths');
|
|
17
|
+
const { detectAppType, resolveApplicationConfigPath } = require('../utils/paths');
|
|
18
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
20
19
|
const generator = require('../generator');
|
|
21
20
|
const { getConfig, normalizeControllerUrl } = require('../core/config');
|
|
22
21
|
const { getOrRefreshDeviceToken } = require('../utils/token-manager');
|
|
@@ -24,6 +23,7 @@ const { resolveControllerUrl } = require('../utils/controller-url');
|
|
|
24
23
|
const { resolveEnvironment } = require('../core/config');
|
|
25
24
|
const { getApplication } = require('../api/applications.api');
|
|
26
25
|
const {
|
|
26
|
+
getExternalSystem,
|
|
27
27
|
getExternalSystemConfig,
|
|
28
28
|
listOpenAPIFiles,
|
|
29
29
|
listOpenAPIEndpoints
|
|
@@ -37,26 +37,23 @@ const { display: displayShow } = require('./show-display');
|
|
|
37
37
|
const DEPLOYMENT_KEY_TRUNCATE_LEN = 12;
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* Load
|
|
40
|
+
* Load application config from app path (no validation).
|
|
41
|
+
* Uses resolver + converter; supports application.yaml, application.json, or legacy variables.yaml.
|
|
41
42
|
* @param {string} appPath - Application directory path
|
|
42
|
-
* @returns {Object} Parsed
|
|
43
|
-
* @throws {Error} If
|
|
43
|
+
* @returns {Object} Parsed config object
|
|
44
|
+
* @throws {Error} If config not found or invalid
|
|
44
45
|
*/
|
|
45
46
|
function loadVariablesFromPath(appPath) {
|
|
46
|
-
const
|
|
47
|
-
if (!fs.existsSync(variablesPath)) {
|
|
48
|
-
throw new Error(`variables.yaml not found for app (path: ${variablesPath}). Use aifabrix validate to check.`);
|
|
49
|
-
}
|
|
50
|
-
const content = fs.readFileSync(variablesPath, 'utf8');
|
|
47
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
51
48
|
try {
|
|
52
|
-
const parsed =
|
|
49
|
+
const parsed = loadConfigFile(configPath);
|
|
53
50
|
if (!parsed || typeof parsed !== 'object') {
|
|
54
|
-
throw new Error('
|
|
51
|
+
throw new Error('Application config is empty or invalid');
|
|
55
52
|
}
|
|
56
53
|
return parsed;
|
|
57
54
|
} catch (error) {
|
|
58
|
-
if (error.message.includes('
|
|
59
|
-
throw new Error(`Invalid
|
|
55
|
+
if (error.message.includes('Application config') || error.message.includes('not found')) throw error;
|
|
56
|
+
throw new Error(`Invalid application config: ${error.message}`);
|
|
60
57
|
}
|
|
61
58
|
}
|
|
62
59
|
|
|
@@ -207,9 +204,9 @@ function buildApplicationFromVariables(variables) {
|
|
|
207
204
|
}
|
|
208
205
|
|
|
209
206
|
/**
|
|
210
|
-
* Build offline summary object from
|
|
211
|
-
* @param {Object} variables - Parsed
|
|
212
|
-
* @param {string} sourcePath - Path to
|
|
207
|
+
* Build offline summary object from application config (for display and JSON).
|
|
208
|
+
* @param {Object} variables - Parsed application config (application.yaml/application.json)
|
|
209
|
+
* @param {string} sourcePath - Path to application config for display
|
|
213
210
|
* @returns {Object} Summary with application, roles, permissions, etc.
|
|
214
211
|
*/
|
|
215
212
|
function buildOfflineSummary(variables, sourcePath) {
|
|
@@ -294,17 +291,57 @@ async function fetchOpenApiLists(dataplaneUrl, appKey, authConfig) {
|
|
|
294
291
|
return { openapiFiles, openapiEndpoints };
|
|
295
292
|
}
|
|
296
293
|
|
|
297
|
-
|
|
294
|
+
/**
|
|
295
|
+
* Normalize GET /api/v1/external/systems/{id} response to the entity object.
|
|
296
|
+
* Handles: (1) our API client shape { success, data }; (2) body wrapped in .data by dataplane.
|
|
297
|
+
* @param {Object} systemResponse - Raw response from getExternalSystem
|
|
298
|
+
* @returns {Object|null} Entity object (id, key, displayName, status, config, ...) or null
|
|
299
|
+
*/
|
|
300
|
+
function normalizeExternalSystemResponse(systemResponse) {
|
|
301
|
+
if (!systemResponse) return null;
|
|
302
|
+
const body = systemResponse.data ?? systemResponse;
|
|
303
|
+
if (!body || typeof body !== 'object') return null;
|
|
304
|
+
return body.data ?? body;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function pickExternalDisplayName(res, system, application, appKey) {
|
|
308
|
+
return (res && res.displayName) || system.displayName || application.displayName || appKey;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function pickExternalType(res, config, system, application) {
|
|
312
|
+
return (res && res.type) || config.type || system.type || application.type || '—';
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function pickExternalStatus(system, res) {
|
|
316
|
+
return system.status || (res && res.status) || '—';
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function pickExternalVersion(res, config, system) {
|
|
320
|
+
return (res && res.version) ?? config.version ?? system.version ?? '—';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function resolveExternalSystemMeta(res, config, system, application, appKey) {
|
|
324
|
+
return {
|
|
325
|
+
displayName: pickExternalDisplayName(res, system, application, appKey),
|
|
326
|
+
type: pickExternalType(res, config, system, application),
|
|
327
|
+
status: pickExternalStatus(system, res),
|
|
328
|
+
version: pickExternalVersion(res, config, system)
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function buildBaseExternalResult(configData, appKey, openapiFiles, openapiEndpoints, res) {
|
|
298
333
|
const system = configData.system || configData;
|
|
299
334
|
const dataSources = configData.dataSources || configData.dataSources || [];
|
|
300
335
|
const application = configData.application || configData.app || {};
|
|
336
|
+
const config = res && res.config && typeof res.config === 'object' ? res.config : {};
|
|
337
|
+
const meta = resolveExternalSystemMeta(res, config, system, application, appKey);
|
|
301
338
|
return {
|
|
302
339
|
dataplaneUrl: null,
|
|
303
340
|
systemKey: appKey,
|
|
304
|
-
displayName:
|
|
305
|
-
type:
|
|
306
|
-
status:
|
|
307
|
-
version:
|
|
341
|
+
displayName: meta.displayName,
|
|
342
|
+
type: meta.type,
|
|
343
|
+
status: meta.status,
|
|
344
|
+
version: meta.version,
|
|
308
345
|
dataSources: dataSources.map((ds) => ({
|
|
309
346
|
key: ds.key,
|
|
310
347
|
displayName: ds.displayName,
|
|
@@ -322,8 +359,37 @@ function buildExternalSystemResult(configData, appKey, openapiFiles, openapiEndp
|
|
|
322
359
|
};
|
|
323
360
|
}
|
|
324
361
|
|
|
362
|
+
function applySystemResponseToResult(result, res) {
|
|
363
|
+
result.dataplaneStatus = res.status;
|
|
364
|
+
if (res.credentialId !== undefined && res.credentialId !== null) result.credentialId = res.credentialId;
|
|
365
|
+
if (res.showOpenApiDocs !== undefined) result.showOpenApiDocs = res.showOpenApiDocs;
|
|
366
|
+
if (res.mcpServerUrl !== undefined && res.mcpServerUrl !== null) result.mcpServerUrl = res.mcpServerUrl;
|
|
367
|
+
if (res.apiDocumentUrl !== undefined && res.apiDocumentUrl !== null) result.apiDocumentUrl = res.apiDocumentUrl;
|
|
368
|
+
if (res.openApiDocsPageUrl !== undefined && res.openApiDocsPageUrl !== null) result.openApiDocsPageUrl = res.openApiDocsPageUrl;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Build external system result from config and optional ExternalSystemResponse.
|
|
373
|
+
* Dataplane GET /api/v1/external/systems/{id} returns: id, key, displayName, description,
|
|
374
|
+
* credentialId, config (optional type, description, dataSources), status, version,
|
|
375
|
+
* showOpenApiDocs, mcpServerUrl, apiDocumentUrl, openApiDocsPageUrl.
|
|
376
|
+
* @param {Object} configData - Config response (system, dataSources, application)
|
|
377
|
+
* @param {string} appKey - Application/system key
|
|
378
|
+
* @param {Array} openapiFiles - OpenAPI files list
|
|
379
|
+
* @param {Array} openapiEndpoints - OpenAPI endpoints list
|
|
380
|
+
* @param {Object} [systemResponse] - Optional GET /api/v1/external/systems/{id} response
|
|
381
|
+
* @returns {Object} Result with dataplaneStatus, credentialId, showOpenApiDocs, URLs when systemResponse present
|
|
382
|
+
*/
|
|
383
|
+
function buildExternalSystemResult(configData, appKey, openapiFiles, openapiEndpoints, systemResponse) {
|
|
384
|
+
const res = normalizeExternalSystemResponse(systemResponse);
|
|
385
|
+
const result = buildBaseExternalResult(configData, appKey, openapiFiles, openapiEndpoints, res);
|
|
386
|
+
if (res) applySystemResponseToResult(result, res);
|
|
387
|
+
return result;
|
|
388
|
+
}
|
|
389
|
+
|
|
325
390
|
/**
|
|
326
391
|
* Fetch external system section from dataplane (for --online and type external).
|
|
392
|
+
* Calls getExternalSystem (ExternalSystemResponse) and getExternalSystemConfig, then merges into one result.
|
|
327
393
|
* @param {string} dataplaneUrl - Dataplane URL
|
|
328
394
|
* @param {string} appKey - Application key (system key)
|
|
329
395
|
* @param {Object} authConfig - Auth config
|
|
@@ -331,11 +397,19 @@ function buildExternalSystemResult(configData, appKey, openapiFiles, openapiEndp
|
|
|
331
397
|
*/
|
|
332
398
|
async function fetchExternalSystemFromDataplane(dataplaneUrl, appKey, authConfig) {
|
|
333
399
|
try {
|
|
400
|
+
let systemResponse = null;
|
|
401
|
+
try {
|
|
402
|
+
systemResponse = await getExternalSystem(dataplaneUrl, appKey, authConfig);
|
|
403
|
+
} catch {
|
|
404
|
+
// optional: continue with config only
|
|
405
|
+
}
|
|
334
406
|
const configRes = await getExternalSystemConfig(dataplaneUrl, appKey, authConfig);
|
|
335
407
|
const data = configRes.data || configRes;
|
|
336
408
|
const configData = data.data || data;
|
|
337
409
|
const { openapiFiles, openapiEndpoints } = await fetchOpenApiLists(dataplaneUrl, appKey, authConfig);
|
|
338
|
-
const result = buildExternalSystemResult(
|
|
410
|
+
const result = buildExternalSystemResult(
|
|
411
|
+
configData, appKey, openapiFiles, openapiEndpoints, systemResponse
|
|
412
|
+
);
|
|
339
413
|
result.dataplaneUrl = dataplaneUrl;
|
|
340
414
|
return result;
|
|
341
415
|
} catch (error) {
|
|
@@ -405,6 +479,7 @@ function pickAppCfg(key, app, cfg, fallback) {
|
|
|
405
479
|
function buildApplicationFromAppCfg(app, cfg, portalInputConfigurations) {
|
|
406
480
|
const deploymentKey = cfg.deploymentKey ?? app.deploymentKey;
|
|
407
481
|
const truncatedDeploy = truncateDeploymentKey(deploymentKey) || (deploymentKey ?? '—');
|
|
482
|
+
const version = pickAppCfg('version', app, cfg, undefined);
|
|
408
483
|
const application = {
|
|
409
484
|
key: pickAppCfg('key', app, cfg, '—'),
|
|
410
485
|
displayName: pickAppCfg('displayName', app, cfg, '—'),
|
|
@@ -425,6 +500,7 @@ function buildApplicationFromAppCfg(app, cfg, portalInputConfigurations) {
|
|
|
425
500
|
portalInputConfigurations,
|
|
426
501
|
databases: resolveDatabasesFromAppCfg(app, cfg)
|
|
427
502
|
};
|
|
503
|
+
if (version !== undefined && version !== null) application.version = version;
|
|
428
504
|
return application;
|
|
429
505
|
}
|
|
430
506
|
|
|
@@ -483,11 +559,11 @@ function buildOnlineSummary(apiApp, controllerUrl, externalSystem) {
|
|
|
483
559
|
}
|
|
484
560
|
|
|
485
561
|
/**
|
|
486
|
-
* Run show in offline mode: generate manifest (same as aifabrix json) and use it; else fall back to
|
|
562
|
+
* Run show in offline mode: generate manifest (same as aifabrix json) and use it; else fall back to application config.
|
|
487
563
|
* @param {string} appKey - Application key
|
|
488
564
|
* @param {boolean} json - Output as JSON
|
|
489
565
|
* @param {boolean} [permissionsOnly] - When true, output only permissions
|
|
490
|
-
* @throws {Error} If
|
|
566
|
+
* @throws {Error} If application config not found or invalid
|
|
491
567
|
*/
|
|
492
568
|
async function runOffline(appKey, json, permissionsOnly) {
|
|
493
569
|
let summary;
|
|
@@ -498,9 +574,9 @@ async function runOffline(appKey, json, permissionsOnly) {
|
|
|
498
574
|
summary = buildOfflineSummaryFromDeployJson(deployment, sourcePath);
|
|
499
575
|
} catch (_err) {
|
|
500
576
|
const { appPath } = await detectAppType(appKey);
|
|
501
|
-
const
|
|
577
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
502
578
|
const variables = loadVariablesFromPath(appPath);
|
|
503
|
-
const sourcePath = path.relative(process.cwd(),
|
|
579
|
+
const sourcePath = path.relative(process.cwd(), configPath) || configPath;
|
|
504
580
|
summary = buildOfflineSummary(variables, sourcePath);
|
|
505
581
|
}
|
|
506
582
|
|
|
@@ -573,27 +649,29 @@ function outputOnlineJson(summary, permissionsOnly) {
|
|
|
573
649
|
logger.log(JSON.stringify(out, null, 2));
|
|
574
650
|
return;
|
|
575
651
|
}
|
|
652
|
+
const app = summary.application;
|
|
576
653
|
const out = {
|
|
577
654
|
source: summary.source,
|
|
578
655
|
controllerUrl: summary.controllerUrl,
|
|
579
656
|
appKey: summary.appKey,
|
|
580
657
|
application: {
|
|
581
|
-
key:
|
|
582
|
-
displayName:
|
|
583
|
-
description:
|
|
584
|
-
type:
|
|
585
|
-
status:
|
|
586
|
-
url:
|
|
587
|
-
internalUrl:
|
|
588
|
-
port:
|
|
589
|
-
configuration:
|
|
590
|
-
roles:
|
|
591
|
-
permissions:
|
|
592
|
-
authentication:
|
|
593
|
-
portalInputConfigurations:
|
|
594
|
-
databases:
|
|
658
|
+
key: app.key,
|
|
659
|
+
displayName: app.displayName,
|
|
660
|
+
description: app.description,
|
|
661
|
+
type: app.type,
|
|
662
|
+
status: app.status,
|
|
663
|
+
url: app.url,
|
|
664
|
+
internalUrl: app.internalUrl,
|
|
665
|
+
port: app.port,
|
|
666
|
+
configuration: app.configuration,
|
|
667
|
+
roles: app.roles,
|
|
668
|
+
permissions: app.permissions,
|
|
669
|
+
authentication: app.authentication,
|
|
670
|
+
portalInputConfigurations: app.portalInputConfigurations,
|
|
671
|
+
databases: app.databases
|
|
595
672
|
}
|
|
596
673
|
};
|
|
674
|
+
if (app.version !== undefined && app.version !== null) out.application.version = app.version;
|
|
597
675
|
if (summary.externalSystem !== undefined && summary.externalSystem !== null) {
|
|
598
676
|
out.externalSystem = summary.externalSystem && summary.externalSystem.error
|
|
599
677
|
? { error: summary.externalSystem.error }
|
|
@@ -618,7 +696,9 @@ async function runOnline(appKey, json, permissionsOnly) {
|
|
|
618
696
|
const response = await getApplication(controllerUrl, appKey, authConfig);
|
|
619
697
|
const apiApp = ensureApplicationResponse(response, appKey, authResult);
|
|
620
698
|
const appData = apiApp.data || apiApp;
|
|
621
|
-
const
|
|
699
|
+
const cfg = appData.configuration && typeof appData.configuration === 'object' ? appData.configuration : {};
|
|
700
|
+
const isExternalApp = appData.type === 'external' || cfg.type === 'external';
|
|
701
|
+
const externalSystem = isExternalApp
|
|
622
702
|
? await fetchExternalSystemForOnline(controllerUrl, appKey, authConfig)
|
|
623
703
|
: null;
|
|
624
704
|
const summary = buildOnlineSummary(apiApp, authResult.actualControllerUrl, externalSystem);
|
package/lib/build/index.js
CHANGED
|
@@ -15,8 +15,9 @@ const fsSync = require('fs');
|
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const paths = require('../utils/paths');
|
|
17
17
|
const { detectAppType, getProjectRoot } = require('../utils/paths');
|
|
18
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
19
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
18
20
|
const chalk = require('chalk');
|
|
19
|
-
const yaml = require('js-yaml');
|
|
20
21
|
const secrets = require('../core/secrets');
|
|
21
22
|
const config = require('../core/config');
|
|
22
23
|
const logger = require('../utils/logger');
|
|
@@ -27,30 +28,22 @@ const { buildDevImageName } = require('../utils/image-name');
|
|
|
27
28
|
const buildHelpers = require('../utils/build-helpers');
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
|
-
* Loads
|
|
31
|
+
* Loads application config for an application
|
|
31
32
|
* @param {string} appName - Application name
|
|
32
33
|
* @returns {Promise<Object>} Configuration object
|
|
33
34
|
* @throws {Error} If file cannot be loaded or parsed
|
|
34
35
|
*/
|
|
35
36
|
async function loadVariablesYaml(appName) {
|
|
36
|
-
// Detect app type and get correct path (integration or builder)
|
|
37
37
|
const { appPath } = await detectAppType(appName);
|
|
38
|
-
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
39
|
-
|
|
40
|
-
if (!fsSync.existsSync(variablesPath)) {
|
|
41
|
-
throw new Error(`Configuration not found. Run 'aifabrix create ${appName}' first.`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const content = fsSync.readFileSync(variablesPath, 'utf8');
|
|
45
|
-
let variables;
|
|
46
|
-
|
|
47
38
|
try {
|
|
48
|
-
|
|
39
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
40
|
+
return loadConfigFile(configPath);
|
|
49
41
|
} catch (error) {
|
|
50
|
-
|
|
42
|
+
if (error.message && error.message.includes('not found')) {
|
|
43
|
+
throw new Error(`Configuration not found. Run 'aifabrix create ${appName}' first.`);
|
|
44
|
+
}
|
|
45
|
+
throw new Error(`Invalid application config: ${error.message}`);
|
|
51
46
|
}
|
|
52
|
-
|
|
53
|
-
return variables;
|
|
54
47
|
}
|
|
55
48
|
|
|
56
49
|
/**
|
|
@@ -115,7 +108,7 @@ function detectLanguage(appPath) {
|
|
|
115
108
|
* @function generateDockerfile
|
|
116
109
|
* @param {string} appNameOrPath - Application name or path (backward compatibility)
|
|
117
110
|
* @param {string} language - Target language ('typescript', 'python')
|
|
118
|
-
* @param {Object} config - Application configuration from
|
|
111
|
+
* @param {Object} config - Application configuration from application.yaml
|
|
119
112
|
* @returns {Promise<string>} Path to generated Dockerfile
|
|
120
113
|
* @throws {Error} If template generation fails
|
|
121
114
|
*
|
|
@@ -326,7 +319,7 @@ function prepareBuildContext(buildConfig, devDir) {
|
|
|
326
319
|
throw new Error(
|
|
327
320
|
`Build context path does not exist: ${contextPath}\n` +
|
|
328
321
|
`Expected dev directory: ${devDir}\n` +
|
|
329
|
-
'Please ensure files were copied correctly or update the context in
|
|
322
|
+
'Please ensure files were copied correctly or update the context in application.yaml.'
|
|
330
323
|
);
|
|
331
324
|
}
|
|
332
325
|
|
package/lib/cli/setup-app.js
CHANGED
|
@@ -91,11 +91,7 @@ async function handleCreateCommand(appName, options) {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
* Sets up application lifecycle commands
|
|
96
|
-
* @param {Command} program - Commander program instance
|
|
97
|
-
*/
|
|
98
|
-
function setupAppCommands(program) {
|
|
94
|
+
function setupCreateCommand(program) {
|
|
99
95
|
program.command('create <app>')
|
|
100
96
|
.description('Create new application with configuration files')
|
|
101
97
|
.option('-p, --port <port>', 'Application port', '3000')
|
|
@@ -124,13 +120,10 @@ function setupAppCommands(program) {
|
|
|
124
120
|
process.exit(1);
|
|
125
121
|
}
|
|
126
122
|
});
|
|
123
|
+
}
|
|
127
124
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
.option('-a, --app <app>', 'Application name (synonym for positional appName)')
|
|
131
|
-
.option('--config <file>', 'Run headless using a wizard.yaml file (appName, mode, source, credential, preferences)')
|
|
132
|
-
.option('--silent', 'Run with saved integration/<app>/wizard.yaml only; no prompts (requires app name and existing wizard.yaml)')
|
|
133
|
-
.addHelpText('after', `
|
|
125
|
+
function setupWizardCommand(program) {
|
|
126
|
+
const wizardHelp = `
|
|
134
127
|
Examples:
|
|
135
128
|
$ aifabrix wizard Run interactively (mode first, then prompts)
|
|
136
129
|
$ aifabrix wizard my-integration Load wizard.yaml if present → show summary → "Run with saved config?" or start from step 1
|
|
@@ -141,7 +134,13 @@ Examples:
|
|
|
141
134
|
Config path: When appName is provided, integration/<appName>/wizard.yaml is used for load/save and error.log.
|
|
142
135
|
To change settings after a run, edit that file and run "aifabrix wizard <app>" again.
|
|
143
136
|
Headless config must include: appName, mode (create-system|add-datasource), source (type + filePath/url/platform).
|
|
144
|
-
See integration/hubspot/wizard-hubspot-e2e.yaml for an example
|
|
137
|
+
See integration/hubspot/wizard-hubspot-e2e.yaml for an example.`;
|
|
138
|
+
program.command('wizard [appName]')
|
|
139
|
+
.description('Create or extend external systems (OpenAPI, MCP, or known platforms like HubSpot) via guided steps or a config file')
|
|
140
|
+
.option('-a, --app <app>', 'Application name (synonym for positional appName)')
|
|
141
|
+
.option('--config <file>', 'Run headless using a wizard.yaml file (appName, mode, source, credential, preferences)')
|
|
142
|
+
.option('--silent', 'Run with saved integration/<app>/wizard.yaml only; no prompts (requires app name and existing wizard.yaml)')
|
|
143
|
+
.addHelpText('after', wizardHelp)
|
|
145
144
|
.action(async(positionalAppName, options) => {
|
|
146
145
|
try {
|
|
147
146
|
const appName = positionalAppName || options.app;
|
|
@@ -153,12 +152,14 @@ See integration/hubspot/wizard-hubspot-e2e.yaml for an example.`)
|
|
|
153
152
|
process.exit(1);
|
|
154
153
|
}
|
|
155
154
|
});
|
|
155
|
+
}
|
|
156
156
|
|
|
157
|
+
function setupBuildRunLogsDownCommands(program) {
|
|
157
158
|
program.command('build <app>')
|
|
158
159
|
.description('Build container image (auto-detects runtime)')
|
|
159
160
|
.option('-l, --language <lang>', 'Override language detection')
|
|
160
161
|
.option('-f, --force-template', 'Force rebuild from template')
|
|
161
|
-
.option('-t, --tag <tag>', 'Image tag (default: latest). Set image.tag in
|
|
162
|
+
.option('-t, --tag <tag>', 'Image tag (default: latest). Set image.tag in application.yaml to match for deploy.')
|
|
162
163
|
.action(async(appName, options) => {
|
|
163
164
|
try {
|
|
164
165
|
const imageTag = await app.buildApp(appName, options);
|
|
@@ -173,7 +174,7 @@ See integration/hubspot/wizard-hubspot-e2e.yaml for an example.`)
|
|
|
173
174
|
.description('Run application locally')
|
|
174
175
|
.option('-p, --port <port>', 'Override local port')
|
|
175
176
|
.option('-d, --debug', 'Enable debug output with detailed container information')
|
|
176
|
-
.option('-t, --tag <tag>', 'Image tag to run (e.g. v1.0.0); overrides
|
|
177
|
+
.option('-t, --tag <tag>', 'Image tag to run (e.g. v1.0.0); overrides application.yaml image.tag')
|
|
177
178
|
.action(async(appName, options) => {
|
|
178
179
|
try {
|
|
179
180
|
await app.runApp(appName, options);
|
|
@@ -193,11 +194,7 @@ See integration/hubspot/wizard-hubspot-e2e.yaml for an example.`)
|
|
|
193
194
|
const { runAppLogs } = require('../commands/app-logs');
|
|
194
195
|
const tailNum = parseInt(options.tail, 10);
|
|
195
196
|
const level = options.level !== undefined && options.level !== null && options.level !== '' ? String(options.level).trim() : undefined;
|
|
196
|
-
await runAppLogs(appName, {
|
|
197
|
-
follow: options.f,
|
|
198
|
-
tail: Number.isNaN(tailNum) ? 100 : tailNum,
|
|
199
|
-
level
|
|
200
|
-
});
|
|
197
|
+
await runAppLogs(appName, { follow: options.f, tail: Number.isNaN(tailNum) ? 100 : tailNum, level });
|
|
201
198
|
} catch (error) {
|
|
202
199
|
handleCommandError(error, 'logs');
|
|
203
200
|
process.exit(1);
|
|
@@ -216,10 +213,12 @@ See integration/hubspot/wizard-hubspot-e2e.yaml for an example.`)
|
|
|
216
213
|
process.exit(1);
|
|
217
214
|
}
|
|
218
215
|
});
|
|
216
|
+
}
|
|
219
217
|
|
|
218
|
+
function setupPushDeployDockerfileCommands(program) {
|
|
220
219
|
program.command('push <app>')
|
|
221
220
|
.description('Push image to Azure Container Registry')
|
|
222
|
-
.option('-r, --registry <registry>', 'ACR registry URL (overrides
|
|
221
|
+
.option('-r, --registry <registry>', 'ACR registry URL (overrides application.yaml)')
|
|
223
222
|
.option('-t, --tag <tag>', 'Image tag(s) - comma-separated for multiple (default: latest)')
|
|
224
223
|
.action(async(appName, options) => {
|
|
225
224
|
try {
|
|
@@ -232,21 +231,18 @@ See integration/hubspot/wizard-hubspot-e2e.yaml for an example.`)
|
|
|
232
231
|
|
|
233
232
|
program.command('deploy <app>')
|
|
234
233
|
.description('Deploy to Azure via Miso Controller')
|
|
235
|
-
.option('--
|
|
236
|
-
.option('--type <type>', 'Application type: external to deploy from integration/<app> (no app register needed)')
|
|
234
|
+
.option('--local', 'Send manifest to controller then run app locally (app: same as aifabrix run <app>; external: restart dataplane)')
|
|
237
235
|
.option('--client-id <id>', 'Client ID (overrides config)')
|
|
238
236
|
.option('--client-secret <secret>', 'Client Secret (overrides config)')
|
|
239
237
|
.option('--poll', 'Poll for deployment status', true)
|
|
240
238
|
.option('--no-poll', 'Do not poll for status')
|
|
241
239
|
.action(async(appName, options) => {
|
|
242
240
|
try {
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (target === 'local') {
|
|
249
|
-
await app.runApp(appName, options);
|
|
241
|
+
const opts = { ...options, local: !!options.local };
|
|
242
|
+
const outcome = await app.deployApp(appName, opts);
|
|
243
|
+
if (opts.local && outcome) {
|
|
244
|
+
if (outcome.usedExternalDeploy) await app.restartApp('dataplane');
|
|
245
|
+
else await app.runApp(appName, opts);
|
|
250
246
|
}
|
|
251
247
|
} catch (error) {
|
|
252
248
|
handleCommandError(error, 'deploy');
|
|
@@ -270,4 +266,15 @@ See integration/hubspot/wizard-hubspot-e2e.yaml for an example.`)
|
|
|
270
266
|
});
|
|
271
267
|
}
|
|
272
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Sets up application lifecycle commands
|
|
271
|
+
* @param {Command} program - Commander program instance
|
|
272
|
+
*/
|
|
273
|
+
function setupAppCommands(program) {
|
|
274
|
+
setupCreateCommand(program);
|
|
275
|
+
setupWizardCommand(program);
|
|
276
|
+
setupBuildRunLogsDownCommands(program);
|
|
277
|
+
setupPushDeployDockerfileCommands(program);
|
|
278
|
+
}
|
|
279
|
+
|
|
273
280
|
module.exports = { setupAppCommands };
|
package/lib/cli/setup-auth.js
CHANGED
|
@@ -14,11 +14,7 @@ const { handleLogout } = require('../commands/logout');
|
|
|
14
14
|
const { handleAuthStatus } = require('../commands/auth-status');
|
|
15
15
|
const { handleAuthConfig } = require('../commands/auth-config');
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
* Sets up authentication commands
|
|
19
|
-
* @param {Command} program - Commander program instance
|
|
20
|
-
*/
|
|
21
|
-
function setupAuthCommands(program) {
|
|
17
|
+
function setupLoginCommand(program) {
|
|
22
18
|
program.command('login')
|
|
23
19
|
.description('Authenticate with Miso Controller')
|
|
24
20
|
.option('-c, --controller <url>', 'Controller URL (default: from config or developer ID, e.g. http://localhost:3000)')
|
|
@@ -37,7 +33,9 @@ function setupAuthCommands(program) {
|
|
|
37
33
|
process.exit(1);
|
|
38
34
|
}
|
|
39
35
|
});
|
|
36
|
+
}
|
|
40
37
|
|
|
38
|
+
function setupLogoutCommand(program) {
|
|
41
39
|
program.command('logout')
|
|
42
40
|
.description('Clear authentication tokens')
|
|
43
41
|
.option('-c, --controller <url>', 'Clear device tokens for specific controller')
|
|
@@ -51,7 +49,9 @@ function setupAuthCommands(program) {
|
|
|
51
49
|
process.exit(1);
|
|
52
50
|
}
|
|
53
51
|
});
|
|
52
|
+
}
|
|
54
53
|
|
|
54
|
+
function setupAuthSubcommands(program) {
|
|
55
55
|
const authStatusHandler = async(options) => {
|
|
56
56
|
try {
|
|
57
57
|
await handleAuthStatus(options);
|
|
@@ -60,18 +60,11 @@ function setupAuthCommands(program) {
|
|
|
60
60
|
process.exit(1);
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
.command('auth')
|
|
66
|
-
.description('Authentication commands');
|
|
67
|
-
|
|
68
|
-
auth
|
|
69
|
-
.command('status')
|
|
63
|
+
const auth = program.command('auth').description('Authentication commands');
|
|
64
|
+
auth.command('status')
|
|
70
65
|
.description('Display authentication status for current controller and environment')
|
|
71
66
|
.action(authStatusHandler);
|
|
72
|
-
|
|
73
|
-
auth
|
|
74
|
-
.command('config')
|
|
67
|
+
auth.command('config')
|
|
75
68
|
.description('Configure authentication settings (controller, environment)')
|
|
76
69
|
.option('--set-controller <url>', 'Set default controller URL')
|
|
77
70
|
.option('--set-environment <env>', 'Set default environment')
|
|
@@ -85,4 +78,14 @@ function setupAuthCommands(program) {
|
|
|
85
78
|
});
|
|
86
79
|
}
|
|
87
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Sets up authentication commands
|
|
83
|
+
* @param {Command} program - Commander program instance
|
|
84
|
+
*/
|
|
85
|
+
function setupAuthCommands(program) {
|
|
86
|
+
setupLoginCommand(program);
|
|
87
|
+
setupLogoutCommand(program);
|
|
88
|
+
setupAuthSubcommands(program);
|
|
89
|
+
}
|
|
90
|
+
|
|
88
91
|
module.exports = { setupAuthCommands };
|
|
@@ -24,14 +24,16 @@ function setupCredentialDeploymentCommands(program) {
|
|
|
24
24
|
|
|
25
25
|
credential
|
|
26
26
|
.command('list')
|
|
27
|
-
.description('List credentials from
|
|
27
|
+
.description('List credentials from Dataplane (GET /api/v1/credential). Controller does not expose this endpoint.')
|
|
28
28
|
.option('--controller <url>', 'Controller URL (default: from config)')
|
|
29
|
+
.option('--dataplane <url>', 'Dataplane URL (default: resolved from controller + environment)')
|
|
29
30
|
.option('--active-only', 'List only active credentials')
|
|
30
31
|
.option('--page-size <n>', 'Items per page', '50')
|
|
31
32
|
.action(async(options) => {
|
|
32
33
|
try {
|
|
33
34
|
const opts = {
|
|
34
35
|
controller: options.controller,
|
|
36
|
+
dataplane: options.dataplane,
|
|
35
37
|
activeOnly: options.activeOnly,
|
|
36
38
|
pageSize: parseInt(options.pageSize, 10) || 50
|
|
37
39
|
};
|