@aifabrix/builder 2.40.2 → 2.42.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/docs-rules.mdc +30 -0
- package/README.md +7 -5
- package/integration/hubspot/README.md +8 -4
- package/integration/hubspot/application.json +54 -0
- package/integration/hubspot/create-hubspot.js +9 -136
- package/integration/hubspot/env.template +3 -4
- package/integration/hubspot/hubspot-datasource-company.json +343 -5
- package/integration/hubspot/hubspot-datasource-contact.json +413 -5
- package/integration/hubspot/hubspot-datasource-deal.json +341 -4
- package/integration/hubspot/hubspot-datasource-users.json +116 -0
- package/integration/hubspot/hubspot-deploy.json +1250 -108
- package/integration/hubspot/hubspot-system.json +15 -32
- package/integration/hubspot/test-dataplane-down-tests.js +17 -16
- package/integration/hubspot/test-dataplane-down.js +2 -2
- package/integration/hubspot/test.js +1 -1
- package/jest.config.manual.js +2 -1
- package/lib/api/credential.api.js +40 -0
- package/lib/api/dev.api.js +423 -0
- package/lib/api/external-test.api.js +111 -0
- package/lib/api/index.js +42 -19
- package/lib/api/pipeline.api.js +66 -120
- package/lib/api/types/credential.types.js +23 -0
- package/lib/api/types/dev.types.js +140 -0
- package/lib/api/types/pipeline.types.js +37 -0
- package/lib/api/wizard-platform.api.js +61 -0
- package/lib/api/wizard.api.js +34 -1
- package/lib/app/config.js +44 -11
- package/lib/app/down.js +2 -1
- package/lib/app/index.js +12 -1
- package/lib/app/prompts.js +44 -29
- package/lib/app/push.js +36 -12
- package/lib/app/readme.js +9 -6
- package/lib/app/run-env-compose.js +264 -0
- package/lib/app/run-helpers.js +121 -118
- package/lib/app/run.js +148 -28
- package/lib/app/show-display.js +1 -1
- package/lib/app/show.js +5 -2
- package/lib/build/index.js +11 -3
- package/lib/cli/setup-app.js +172 -15
- package/lib/cli/setup-credential-deployment.js +31 -6
- package/lib/cli/setup-dev.js +206 -16
- package/lib/cli/setup-environment.js +16 -6
- package/lib/cli/setup-external-system.js +89 -24
- package/lib/cli/setup-infra.js +82 -15
- package/lib/cli/setup-secrets.js +52 -5
- package/lib/cli/setup-utility.js +129 -24
- package/lib/commands/app-install.js +172 -0
- package/lib/commands/app-shell.js +75 -0
- package/lib/commands/app-test.js +282 -0
- package/lib/commands/app.js +1 -1
- package/lib/commands/credential-env.js +162 -0
- package/lib/commands/credential-list.js +17 -22
- package/lib/commands/credential-push.js +96 -0
- package/lib/commands/datasource.js +77 -6
- package/lib/commands/dev-cli-handlers.js +141 -0
- package/lib/commands/dev-down.js +114 -0
- package/lib/commands/dev-init.js +347 -0
- package/lib/commands/repair-auth-config.js +99 -0
- package/lib/commands/repair-datasource-keys.js +208 -0
- package/lib/commands/repair-datasource.js +235 -0
- package/lib/commands/repair-env-template.js +348 -0
- package/lib/commands/repair-internal.js +85 -0
- package/lib/commands/repair-rbac.js +158 -0
- package/lib/commands/repair.js +507 -0
- package/lib/commands/secrets-list.js +118 -0
- package/lib/commands/secrets-remove.js +97 -0
- package/lib/commands/secrets-set.js +30 -17
- package/lib/commands/secrets-validate.js +50 -0
- package/lib/commands/test-e2e-external.js +165 -0
- package/lib/commands/up-dataplane.js +2 -2
- package/lib/commands/up-miso.js +0 -25
- package/lib/commands/upload.js +96 -40
- package/lib/commands/wizard-core-helpers.js +226 -4
- package/lib/commands/wizard-core.js +67 -29
- package/lib/commands/wizard-dataplane.js +1 -1
- package/lib/commands/wizard-entity-selection.js +43 -0
- package/lib/commands/wizard-headless.js +44 -5
- package/lib/commands/wizard-helpers.js +7 -3
- package/lib/commands/wizard.js +86 -64
- package/lib/core/admin-secrets.js +96 -0
- package/lib/core/config.js +7 -1
- package/lib/core/secrets-ensure.js +378 -0
- package/lib/core/secrets-env-write.js +157 -0
- package/lib/core/secrets.js +176 -89
- package/lib/datasource/deploy.js +12 -3
- package/lib/datasource/field-reference-validator.js +91 -0
- package/lib/datasource/test-e2e.js +219 -0
- package/lib/datasource/test-integration.js +154 -0
- package/lib/datasource/validate.js +21 -3
- package/lib/deployment/deployer.js +7 -5
- package/lib/deployment/environment-config.js +137 -0
- package/lib/deployment/environment.js +21 -98
- package/lib/deployment/push.js +32 -2
- package/lib/external-system/download.js +188 -203
- package/lib/external-system/generator.js +204 -56
- package/lib/external-system/test-auth.js +7 -3
- package/lib/external-system/test-execution.js +2 -1
- package/lib/external-system/test-system-level.js +73 -0
- package/lib/external-system/test.js +56 -19
- package/lib/generator/external-controller-manifest.js +29 -2
- package/lib/generator/external-schema-utils.js +1 -1
- package/lib/generator/external.js +10 -3
- package/lib/generator/index.js +177 -25
- package/lib/generator/split-readme.js +1 -0
- package/lib/generator/split-variables.js +7 -1
- package/lib/generator/split.js +194 -54
- package/lib/generator/wizard-prompts-secondary.js +294 -0
- package/lib/generator/wizard-prompts.js +105 -106
- package/lib/generator/wizard-readme.js +88 -0
- package/lib/generator/wizard.js +155 -158
- package/lib/infrastructure/compose.js +11 -1
- package/lib/infrastructure/helpers.js +103 -20
- package/lib/infrastructure/index.js +98 -12
- package/lib/infrastructure/services.js +88 -22
- package/lib/schema/application-schema.json +32 -8
- package/lib/schema/external-datasource.schema.json +49 -26
- package/lib/schema/external-system.schema.json +509 -411
- package/lib/schema/wizard-config.schema.json +16 -0
- package/lib/utils/api.js +41 -13
- package/lib/utils/app-register-auth.js +25 -3
- package/lib/utils/auth-headers.js +8 -7
- package/lib/utils/cli-utils.js +20 -0
- package/lib/utils/compose-generator.js +77 -76
- package/lib/utils/compose-handlebars-helpers.js +54 -0
- package/lib/utils/compose-vector-helper.js +18 -0
- package/lib/utils/config-format-preference.js +51 -0
- package/lib/utils/config-format.js +36 -0
- package/lib/utils/config-paths.js +127 -2
- package/lib/utils/configuration-env-resolver.js +179 -0
- package/lib/utils/credential-display.js +83 -0
- package/lib/utils/credential-secrets-env.js +357 -0
- package/lib/utils/dataplane-pipeline-warning.js +28 -0
- package/lib/utils/deployment-validation-helpers.js +4 -4
- package/lib/utils/dev-ca-install.js +139 -0
- package/lib/utils/dev-cert-helper.js +122 -0
- package/lib/utils/device-code-helpers.js +224 -0
- package/lib/utils/device-code.js +37 -336
- package/lib/utils/docker-build.js +40 -8
- package/lib/utils/env-copy.js +103 -13
- package/lib/utils/env-map.js +35 -5
- package/lib/utils/env-template.js +6 -5
- package/lib/utils/error-formatters/http-status-errors.js +20 -2
- package/lib/utils/error-formatters/permission-errors.js +0 -1
- package/lib/utils/error-formatters/validation-errors.js +0 -1
- package/lib/utils/external-readme.js +56 -29
- package/lib/utils/external-system-display.js +59 -1
- package/lib/utils/external-system-test-helpers.js +21 -8
- package/lib/utils/external-system-validators.js +3 -0
- package/lib/utils/file-upload.js +20 -50
- package/lib/utils/help-builder.js +16 -2
- package/lib/utils/infra-status.js +80 -45
- package/lib/utils/local-secrets.js +7 -52
- package/lib/utils/mutagen-install.js +195 -0
- package/lib/utils/mutagen.js +146 -0
- package/lib/utils/paths.js +128 -37
- package/lib/utils/port-resolver.js +28 -16
- package/lib/utils/remote-dev-auth.js +38 -0
- package/lib/utils/remote-docker-env.js +43 -0
- package/lib/utils/remote-secrets-loader.js +60 -0
- package/lib/utils/secrets-canonical.js +93 -0
- package/lib/utils/secrets-generator.js +114 -6
- package/lib/utils/secrets-helpers.js +108 -114
- package/lib/utils/secrets-path.js +2 -2
- package/lib/utils/secrets-utils.js +52 -1
- package/lib/utils/secrets-validation.js +84 -0
- package/lib/utils/ssh-key-helper.js +116 -0
- package/lib/utils/test-log-writer.js +56 -0
- package/lib/utils/token-manager-messages.js +90 -0
- package/lib/utils/token-manager.js +29 -36
- package/lib/utils/variable-transformer.js +3 -3
- package/lib/validation/env-template-auth.js +157 -0
- package/lib/validation/env-template-kv.js +41 -0
- package/lib/validation/external-manifest-validator.js +25 -0
- package/lib/validation/external-system-auth-rules.js +86 -0
- package/lib/validation/validate-batch.js +149 -0
- package/lib/validation/validate-datasource-keys-api.js +33 -0
- package/lib/validation/validate-display.js +94 -16
- package/lib/validation/validate.js +25 -12
- package/lib/validation/validator.js +72 -9
- package/lib/validation/wizard-datasource-validation.js +50 -0
- package/package.json +8 -3
- package/scripts/install-local.js +34 -15
- package/templates/README.md +0 -1
- package/templates/applications/README.md.hbs +4 -4
- package/templates/applications/dataplane/application.yaml +6 -5
- package/templates/applications/dataplane/env.template +15 -10
- package/templates/applications/dataplane/rbac.yaml +2 -2
- package/templates/applications/keycloak/env.template +2 -0
- package/templates/applications/miso-controller/application.yaml +1 -0
- package/templates/applications/miso-controller/env.template +12 -10
- package/templates/external-system/README.md.hbs +65 -25
- package/templates/external-system/deploy.js.hbs +4 -2
- package/templates/external-system/external-datasource.yaml.hbs +217 -0
- package/templates/external-system/external-system.json.hbs +1 -18
- package/templates/infra/compose.yaml.hbs +6 -0
- package/templates/python/docker-compose.hbs +49 -23
- package/templates/typescript/docker-compose.hbs +48 -22
- package/integration/hubspot/application.yaml +0 -37
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Wizard platform API - getPlatformDetails, discoverEntities
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { ApiClient } = require('./index');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get platform details including available datasources
|
|
11
|
+
* GET /api/v1/wizard/platforms/{platformKey}
|
|
12
|
+
* @requiresPermission {Dataplane} external-system:read
|
|
13
|
+
* @async
|
|
14
|
+
* @function getPlatformDetails
|
|
15
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
16
|
+
* @param {Object} authConfig - Authentication configuration
|
|
17
|
+
* @param {string} platformKey - Platform key (e.g. 'hubspot')
|
|
18
|
+
* @returns {Promise<Object>} Platform details including datasources: [{ key, displayName, entity }]
|
|
19
|
+
* @throws {Error} If request fails or platform not found (404)
|
|
20
|
+
*/
|
|
21
|
+
async function getPlatformDetails(dataplaneUrl, authConfig, platformKey) {
|
|
22
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
23
|
+
const response = await client.get(`/api/v1/wizard/platforms/${encodeURIComponent(platformKey)}`);
|
|
24
|
+
if (!response.success) {
|
|
25
|
+
const msg = response.status === 404
|
|
26
|
+
? `Platform '${platformKey}' not found`
|
|
27
|
+
: response.formattedError || response.error || 'Failed to get platform details';
|
|
28
|
+
const err = new Error(msg);
|
|
29
|
+
err.status = response.status;
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
return response;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Discover entities from OpenAPI spec (for multi-entity flows)
|
|
37
|
+
* POST /api/v1/wizard/discover-entities
|
|
38
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
39
|
+
* @async
|
|
40
|
+
* @function discoverEntities
|
|
41
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
42
|
+
* @param {Object} authConfig - Authentication configuration
|
|
43
|
+
* @param {Object} openapiSpec - OpenAPI specification object
|
|
44
|
+
* @returns {Promise<Object>} Response with entities: [{ name, pathCount, schemaMatch }]
|
|
45
|
+
* @throws {Error} If request fails
|
|
46
|
+
*/
|
|
47
|
+
async function discoverEntities(dataplaneUrl, authConfig, openapiSpec) {
|
|
48
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
49
|
+
const response = await client.post('/api/v1/wizard/discover-entities', {
|
|
50
|
+
body: { openapiSpec }
|
|
51
|
+
});
|
|
52
|
+
if (!response.success) {
|
|
53
|
+
const msg = response.formattedError || response.error || 'Failed to discover entities';
|
|
54
|
+
const err = new Error(msg);
|
|
55
|
+
err.status = response.status;
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
return response;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = { getPlatformDetails, discoverEntities };
|
package/lib/api/wizard.api.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const { ApiClient } = require('./index');
|
|
8
8
|
const { uploadFile } = require('../utils/file-upload');
|
|
9
|
+
const { getPlatformDetails, discoverEntities } = require('./wizard-platform.api');
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Create wizard session
|
|
@@ -178,8 +179,36 @@ async function detectType(dataplaneUrl, authConfig, openapiSpec) {
|
|
|
178
179
|
}
|
|
179
180
|
|
|
180
181
|
/**
|
|
181
|
-
*
|
|
182
|
+
* Get platform configuration for a known platform (no OpenAPI parsing)
|
|
183
|
+
* POST /api/v1/wizard/platforms/{platformKey}/config
|
|
184
|
+
* Use this when sourceType=known-platform; do NOT use generate-config which requires openapiSpec.
|
|
185
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
186
|
+
* @async
|
|
187
|
+
* @function getPlatformConfig
|
|
188
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
189
|
+
* @param {Object} authConfig - Authentication configuration
|
|
190
|
+
* @param {string} platformKey - Platform key (e.g. 'hubspot')
|
|
191
|
+
* @param {Object} config - Configuration payload (no openapiSpec)
|
|
192
|
+
* @param {string} config.mode - Wizard mode ('create-system' | 'add-datasource')
|
|
193
|
+
* @param {string} [config.systemIdOrKey] - Existing system ID/key (required for add-datasource)
|
|
194
|
+
* @param {string} [config.credentialIdOrKey] - Credential ID or key
|
|
195
|
+
* @param {string} [config.intent] - User intent
|
|
196
|
+
* @param {string} [config.fieldOnboardingLevel] - Field onboarding level ('full' | 'standard' | 'minimal')
|
|
197
|
+
* @param {Object} [config.userPreferences] - User preferences
|
|
198
|
+
* @returns {Promise<Object>} Generated configuration response
|
|
199
|
+
* @throws {Error} If request fails
|
|
200
|
+
*/
|
|
201
|
+
async function getPlatformConfig(dataplaneUrl, authConfig, platformKey, config) {
|
|
202
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
203
|
+
return await client.post(`/api/v1/wizard/platforms/${encodeURIComponent(platformKey)}/config`, {
|
|
204
|
+
body: config
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Generate configuration via AI (OpenAPI-based)
|
|
182
210
|
* POST /api/v1/wizard/generate-config
|
|
211
|
+
* Do NOT use for sourceType=known-platform; use getPlatformConfig instead.
|
|
183
212
|
* @requiresPermission {Dataplane} external-system:create
|
|
184
213
|
* @async
|
|
185
214
|
* @function generateConfig
|
|
@@ -198,6 +227,7 @@ async function detectType(dataplaneUrl, authConfig, openapiSpec) {
|
|
|
198
227
|
* @param {boolean} [config.userPreferences.enableMCP] - Enable MCP
|
|
199
228
|
* @param {boolean} [config.userPreferences.enableABAC] - Enable ABAC
|
|
200
229
|
* @param {boolean} [config.userPreferences.enableRBAC] - Enable RBAC
|
|
230
|
+
* @param {string} [config.entityName] - Entity for multi-entity OpenAPI (from discover-entities)
|
|
201
231
|
* @returns {Promise<Object>} Generated configuration response
|
|
202
232
|
* @throws {Error} If request fails
|
|
203
233
|
*/
|
|
@@ -417,6 +447,9 @@ module.exports = {
|
|
|
417
447
|
parseOpenApi,
|
|
418
448
|
credentialSelection,
|
|
419
449
|
detectType,
|
|
450
|
+
getPlatformDetails,
|
|
451
|
+
discoverEntities,
|
|
452
|
+
getPlatformConfig,
|
|
420
453
|
generateConfig,
|
|
421
454
|
generateConfigStream,
|
|
422
455
|
validateWizardConfig,
|
package/lib/app/config.js
CHANGED
|
@@ -15,6 +15,7 @@ const { generateVariablesYaml, generateEnvTemplate, generateRbacYaml } = require
|
|
|
15
15
|
const { generateEnvTemplate: generateEnvTemplateFromReader } = require('../core/env-reader');
|
|
16
16
|
const { generateReadmeMdFile } = require('./readme');
|
|
17
17
|
const logger = require('../utils/logger');
|
|
18
|
+
const { systemKeyToKvPrefix } = require('../utils/credential-secrets-env');
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Checks if a file exists
|
|
@@ -31,6 +32,26 @@ async function fileExists(filePath) {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Renames legacy variables.yaml to application.yaml if only variables.yaml exists.
|
|
37
|
+
* Ensures create always results in application.yaml.
|
|
38
|
+
* @async
|
|
39
|
+
* @param {string} appPath - Path to application directory
|
|
40
|
+
*/
|
|
41
|
+
async function normalizeLegacyVariablesYaml(appPath) {
|
|
42
|
+
const applicationYaml = path.join(appPath, 'application.yaml');
|
|
43
|
+
const applicationYml = path.join(appPath, 'application.yml');
|
|
44
|
+
const applicationJson = path.join(appPath, 'application.json');
|
|
45
|
+
const variablesYaml = path.join(appPath, 'variables.yaml');
|
|
46
|
+
const hasAppYaml = await fileExists(applicationYaml);
|
|
47
|
+
const hasAppYml = await fileExists(applicationYml);
|
|
48
|
+
const hasAppJson = await fileExists(applicationJson);
|
|
49
|
+
const hasVariables = await fileExists(variablesYaml);
|
|
50
|
+
if (hasVariables && !hasAppYaml && !hasAppYml && !hasAppJson) {
|
|
51
|
+
await fs.rename(variablesYaml, applicationYaml);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
/**
|
|
35
56
|
* Generates application.yaml file if no application config exists
|
|
36
57
|
* @async
|
|
@@ -39,6 +60,7 @@ async function fileExists(filePath) {
|
|
|
39
60
|
* @param {Object} config - Application configuration
|
|
40
61
|
*/
|
|
41
62
|
async function generateVariablesYamlFile(appPath, appName, config) {
|
|
63
|
+
await normalizeLegacyVariablesYaml(appPath);
|
|
42
64
|
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
43
65
|
try {
|
|
44
66
|
resolveApplicationConfigPath(appPath);
|
|
@@ -52,7 +74,8 @@ async function generateVariablesYamlFile(appPath, appName, config) {
|
|
|
52
74
|
}
|
|
53
75
|
|
|
54
76
|
/**
|
|
55
|
-
* Generates env.template content for external systems based on authentication type
|
|
77
|
+
* Generates env.template content for external systems based on authentication type.
|
|
78
|
+
* Uses KV_<APPKEY>_<VAR> convention (e.g. KV_HUBSPOT_CLIENTID) for credential push.
|
|
56
79
|
* @param {Object} config - Application configuration with authType and systemKey
|
|
57
80
|
* @param {string} appName - Application name (used as fallback for systemKey)
|
|
58
81
|
* @returns {string} Environment template content
|
|
@@ -60,23 +83,33 @@ async function generateVariablesYamlFile(appPath, appName, config) {
|
|
|
60
83
|
function generateExternalSystemEnvTemplate(config, appName) {
|
|
61
84
|
const systemKey = config.systemKey || appName;
|
|
62
85
|
const authType = config.authType || 'apikey';
|
|
86
|
+
const prefix = systemKeyToKvPrefix(systemKey);
|
|
87
|
+
if (!prefix) return '';
|
|
88
|
+
|
|
63
89
|
const lines = [
|
|
64
|
-
`# ${systemKey} ${authType.toUpperCase()} Configuration`,
|
|
65
|
-
'#
|
|
66
|
-
'# Values are stored in Key Vault automatically by the platform',
|
|
90
|
+
`# ${systemKey} ${String(authType).toUpperCase()} Configuration`,
|
|
91
|
+
'# Use KV_* variables for credential push (aifabrix credential push).',
|
|
92
|
+
'# Values are stored in Key Vault automatically by the platform.',
|
|
67
93
|
''
|
|
68
94
|
];
|
|
69
95
|
|
|
70
|
-
if (authType === 'oauth2') {
|
|
71
|
-
lines.push(
|
|
72
|
-
lines.push(
|
|
73
|
-
lines.push('
|
|
96
|
+
if (authType === 'oauth2' || authType === 'aad') {
|
|
97
|
+
lines.push(`KV_${prefix}_CLIENTID=`);
|
|
98
|
+
lines.push(`KV_${prefix}_CLIENTSECRET=`);
|
|
99
|
+
lines.push('TOKEN_URL=https://api.example.com/oauth/token');
|
|
74
100
|
} else if (authType === 'apikey') {
|
|
75
|
-
lines.push(
|
|
101
|
+
lines.push(`KV_${prefix}_APIKEY=`);
|
|
76
102
|
} else if (authType === 'basic') {
|
|
77
|
-
lines.push(
|
|
78
|
-
lines.push(
|
|
103
|
+
lines.push(`KV_${prefix}_USERNAME=`);
|
|
104
|
+
lines.push(`KV_${prefix}_PASSWORD=`);
|
|
105
|
+
} else if (authType === 'queryParam') {
|
|
106
|
+
lines.push(`KV_${prefix}_PARAMVALUE=`);
|
|
107
|
+
} else if (authType === 'oidc') {
|
|
108
|
+
lines.push('# OIDC: variables only (openIdConfigUrl, clientId); no security keys');
|
|
109
|
+
} else if (authType === 'hmac') {
|
|
110
|
+
lines.push(`KV_${prefix}_SIGNINGSECRET=`);
|
|
79
111
|
}
|
|
112
|
+
// none: no security keys
|
|
80
113
|
|
|
81
114
|
return lines.join('\n');
|
|
82
115
|
}
|
package/lib/app/down.js
CHANGED
package/lib/app/index.js
CHANGED
|
@@ -31,6 +31,8 @@ const {
|
|
|
31
31
|
processTemplateFiles,
|
|
32
32
|
setupAppFiles
|
|
33
33
|
} = require('./helpers');
|
|
34
|
+
const path = require('path');
|
|
35
|
+
const secretsEnsure = require('../core/secrets-ensure');
|
|
34
36
|
|
|
35
37
|
/**
|
|
36
38
|
* Creates new application with scaffolded configuration files
|
|
@@ -130,10 +132,19 @@ async function generateApplicationFiles(finalAppPath, appName, config, options)
|
|
|
130
132
|
|
|
131
133
|
await generateConfigFiles(finalAppPath, appName, config, existingEnv);
|
|
132
134
|
|
|
135
|
+
const envTemplatePath = path.join(finalAppPath, 'env.template');
|
|
136
|
+
try {
|
|
137
|
+
await secretsEnsure.ensureSecretsFromEnvTemplate(envTemplatePath, {});
|
|
138
|
+
} catch (err) {
|
|
139
|
+
if (err.code !== 'ENOENT') throw err;
|
|
140
|
+
}
|
|
141
|
+
|
|
133
142
|
// Generate external system files if type is external
|
|
134
143
|
if (config.type === 'external') {
|
|
144
|
+
const configModule = require('../core/config');
|
|
145
|
+
const format = (await configModule.getFormat()) || 'yaml';
|
|
135
146
|
const externalGenerator = require('../external-system/generator');
|
|
136
|
-
await externalGenerator.generateExternalSystemFiles(finalAppPath, appName, config);
|
|
147
|
+
await externalGenerator.generateExternalSystemFiles(finalAppPath, appName, config, format);
|
|
137
148
|
}
|
|
138
149
|
|
|
139
150
|
if (options.app) {
|
package/lib/app/prompts.js
CHANGED
|
@@ -176,8 +176,13 @@ function buildExternalSystemTypeQuestions(options) {
|
|
|
176
176
|
message: 'What authentication type does the system use?',
|
|
177
177
|
choices: [
|
|
178
178
|
{ name: 'OAuth2', value: 'oauth2' },
|
|
179
|
+
{ name: 'Azure AD', value: 'aad' },
|
|
179
180
|
{ name: 'API Key', value: 'apikey' },
|
|
180
|
-
{ name: 'Basic Auth', value: 'basic' }
|
|
181
|
+
{ name: 'Basic Auth', value: 'basic' },
|
|
182
|
+
{ name: 'Query Parameter', value: 'queryParam' },
|
|
183
|
+
{ name: 'OpenID Connect', value: 'oidc' },
|
|
184
|
+
{ name: 'HMAC Signature', value: 'hmac' },
|
|
185
|
+
{ name: 'None', value: 'none' }
|
|
181
186
|
],
|
|
182
187
|
default: 'apikey'
|
|
183
188
|
});
|
|
@@ -185,6 +190,28 @@ function buildExternalSystemTypeQuestions(options) {
|
|
|
185
190
|
return questions;
|
|
186
191
|
}
|
|
187
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Build entityType question for external system datasources
|
|
195
|
+
* @param {Object} options - Provided options
|
|
196
|
+
* @returns {Array} Array of question objects
|
|
197
|
+
*/
|
|
198
|
+
function buildEntityTypeQuestion(options) {
|
|
199
|
+
if (options.entityType) return [];
|
|
200
|
+
return [{
|
|
201
|
+
type: 'list',
|
|
202
|
+
name: 'entityType',
|
|
203
|
+
message: 'What entity type do the datasources represent?',
|
|
204
|
+
choices: [
|
|
205
|
+
{ name: 'Record storage (CRM, deals, contacts)', value: 'recordStorage' },
|
|
206
|
+
{ name: 'Document storage (with vector)', value: 'documentStorage' },
|
|
207
|
+
{ name: 'Vector store', value: 'vectorStore' },
|
|
208
|
+
{ name: 'Message service', value: 'messageService' },
|
|
209
|
+
{ name: 'None', value: 'none' }
|
|
210
|
+
],
|
|
211
|
+
default: 'recordStorage'
|
|
212
|
+
}];
|
|
213
|
+
}
|
|
214
|
+
|
|
188
215
|
function buildExternalSystemDatasourceQuestion(options) {
|
|
189
216
|
if (options.datasourceCount) return [];
|
|
190
217
|
return [{
|
|
@@ -210,6 +237,7 @@ function buildExternalSystemQuestions(options, appName) {
|
|
|
210
237
|
return [
|
|
211
238
|
...buildExternalSystemIdentityQuestions(options, appName),
|
|
212
239
|
...buildExternalSystemTypeQuestions(options),
|
|
240
|
+
...buildEntityTypeQuestion(options),
|
|
213
241
|
...buildExternalSystemDatasourceQuestion(options)
|
|
214
242
|
];
|
|
215
243
|
}
|
|
@@ -325,6 +353,16 @@ function resolveExternalSystemField(options, answers, fieldName, defaultValue) {
|
|
|
325
353
|
return null;
|
|
326
354
|
}
|
|
327
355
|
|
|
356
|
+
const EXTERNAL_SYSTEM_FIELD_SPECS = [
|
|
357
|
+
{ key: 'systemKey', default: undefined },
|
|
358
|
+
{ key: 'systemDisplayName', default: undefined },
|
|
359
|
+
{ key: 'systemDescription', default: undefined },
|
|
360
|
+
{ key: 'systemType', default: 'openapi' },
|
|
361
|
+
{ key: 'authType', default: 'apikey' },
|
|
362
|
+
{ key: 'entityType', default: 'recordStorage' },
|
|
363
|
+
{ key: 'datasourceCount', default: 1, transform: (v) => parseInt(v, 10) }
|
|
364
|
+
];
|
|
365
|
+
|
|
328
366
|
/**
|
|
329
367
|
* Resolve external system fields and add to config
|
|
330
368
|
* @function resolveExternalSystemFields
|
|
@@ -334,34 +372,11 @@ function resolveExternalSystemField(options, answers, fieldName, defaultValue) {
|
|
|
334
372
|
* @returns {void}
|
|
335
373
|
*/
|
|
336
374
|
function resolveExternalSystemFields(options, answers, config) {
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const systemDisplayName = resolveExternalSystemField(options, answers, 'systemDisplayName', undefined);
|
|
343
|
-
if (systemDisplayName !== null) {
|
|
344
|
-
config.systemDisplayName = systemDisplayName;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const systemDescription = resolveExternalSystemField(options, answers, 'systemDescription', undefined);
|
|
348
|
-
if (systemDescription !== null) {
|
|
349
|
-
config.systemDescription = systemDescription;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const systemType = resolveExternalSystemField(options, answers, 'systemType', 'openapi');
|
|
353
|
-
if (systemType !== null) {
|
|
354
|
-
config.systemType = systemType;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const authType = resolveExternalSystemField(options, answers, 'authType', 'apikey');
|
|
358
|
-
if (authType !== null) {
|
|
359
|
-
config.authType = authType;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const datasourceCount = resolveExternalSystemField(options, answers, 'datasourceCount', 1);
|
|
363
|
-
if (datasourceCount !== null) {
|
|
364
|
-
config.datasourceCount = parseInt(datasourceCount, 10);
|
|
375
|
+
for (const { key, default: defaultValue, transform } of EXTERNAL_SYSTEM_FIELD_SPECS) {
|
|
376
|
+
const value = resolveExternalSystemField(options, answers, key, defaultValue);
|
|
377
|
+
if (value !== null) {
|
|
378
|
+
config[key] = transform ? transform(value) : value;
|
|
379
|
+
}
|
|
365
380
|
}
|
|
366
381
|
}
|
|
367
382
|
|
package/lib/app/push.js
CHANGED
|
@@ -40,21 +40,38 @@ function validateAppName(appName) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* @param {
|
|
46
|
-
* @returns {
|
|
43
|
+
* Returns effective config (unwrap variables wrapper if present so image/app are at top level).
|
|
44
|
+
* application.yaml may have top-level image/app or a variables: { image, app } wrapper.
|
|
45
|
+
* @param {Object} config - Raw config from loadConfigFile
|
|
46
|
+
* @returns {Object} Config with image and app at top level
|
|
47
|
+
*/
|
|
48
|
+
function getEffectiveConfig(config) {
|
|
49
|
+
if (!config || typeof config !== 'object') return config || {};
|
|
50
|
+
if (config.variables && typeof config.variables === 'object' && (config.variables.image !== undefined || config.variables.app !== undefined)) {
|
|
51
|
+
return config.variables;
|
|
52
|
+
}
|
|
53
|
+
return config;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Extracts image name from configuration using the same logic as build command.
|
|
58
|
+
* Uses image.name (e.g. aifabrix/dataplane) so ACR repository matches application.yaml.
|
|
59
|
+
* @param {Object} config - Configuration object from application.yaml (or effective config)
|
|
60
|
+
* @param {string} appName - Application name (fallback when image not set)
|
|
61
|
+
* @returns {string} Image name (e.g. aifabrix/dataplane, not just dataplane)
|
|
47
62
|
*/
|
|
48
63
|
function extractImageName(config, appName) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return
|
|
64
|
+
const c = getEffectiveConfig(config);
|
|
65
|
+
if (typeof c.image === 'string') {
|
|
66
|
+
return c.image.split(':')[0];
|
|
67
|
+
}
|
|
68
|
+
if (c.image?.name) {
|
|
69
|
+
return c.image.name;
|
|
70
|
+
}
|
|
71
|
+
if (c.app?.key) {
|
|
72
|
+
return c.app.key;
|
|
55
73
|
}
|
|
56
74
|
return appName;
|
|
57
|
-
|
|
58
75
|
}
|
|
59
76
|
|
|
60
77
|
/**
|
|
@@ -72,7 +89,8 @@ async function loadPushConfig(appName, options) {
|
|
|
72
89
|
try {
|
|
73
90
|
const configPath = resolveApplicationConfigPath(appPath);
|
|
74
91
|
const config = loadConfigFile(configPath);
|
|
75
|
-
const
|
|
92
|
+
const effective = getEffectiveConfig(config);
|
|
93
|
+
const registry = options.registry || effective.image?.registry;
|
|
76
94
|
if (!registry) {
|
|
77
95
|
throw new Error('Registry URL is required. Provide via --registry flag or configure in application config under image.registry');
|
|
78
96
|
}
|
|
@@ -110,6 +128,12 @@ async function validatePushConfig(registry, imageName, appName) {
|
|
|
110
128
|
if (!await pushUtils.checkAzureCLIInstalled()) {
|
|
111
129
|
throw new Error('Azure CLI is not installed. Install from: https://docs.microsoft.com/cli/azure/install-azure-cli');
|
|
112
130
|
}
|
|
131
|
+
|
|
132
|
+
if (!await pushUtils.checkAzureLogin()) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
'Not logged in to Azure. Run "az login" first, then run push again.'
|
|
135
|
+
);
|
|
136
|
+
}
|
|
113
137
|
}
|
|
114
138
|
|
|
115
139
|
/**
|
package/lib/app/readme.js
CHANGED
|
@@ -92,20 +92,23 @@ function extractServiceFlags(config) {
|
|
|
92
92
|
/**
|
|
93
93
|
* Builds placeholder datasources for external README generation
|
|
94
94
|
* @function buildExternalDatasourcePlaceholders
|
|
95
|
+
* @param {string} systemKey - System key
|
|
95
96
|
* @param {number} datasourceCount - Datasource count
|
|
97
|
+
* @param {string} [fileExt='.json'] - File extension (e.g. '.json', '.yaml')
|
|
96
98
|
* @returns {Array<Object>} Datasource placeholders
|
|
97
99
|
*/
|
|
98
|
-
function buildExternalDatasourcePlaceholders(systemKey, datasourceCount) {
|
|
100
|
+
function buildExternalDatasourcePlaceholders(systemKey, datasourceCount, fileExt = '.json') {
|
|
99
101
|
const normalizedCount = Number.isInteger(datasourceCount)
|
|
100
102
|
? datasourceCount
|
|
101
103
|
: parseInt(datasourceCount, 10);
|
|
102
104
|
const total = Number.isFinite(normalizedCount) && normalizedCount > 0 ? normalizedCount : 0;
|
|
105
|
+
const ext = fileExt && fileExt.startsWith('.') ? fileExt : `.${fileExt || 'json'}`;
|
|
103
106
|
return Array.from({ length: total }, (_value, index) => {
|
|
104
107
|
const entityType = `entity${index + 1}`;
|
|
105
108
|
return {
|
|
106
109
|
entityType,
|
|
107
110
|
displayName: `Datasource ${index + 1}`,
|
|
108
|
-
fileName: `${systemKey}-datasource-${entityType}
|
|
111
|
+
fileName: `${systemKey}-datasource-${entityType}${ext}`
|
|
109
112
|
};
|
|
110
113
|
});
|
|
111
114
|
}
|
|
@@ -120,9 +123,7 @@ function buildExternalDatasourcePlaceholders(systemKey, datasourceCount) {
|
|
|
120
123
|
function buildReadmeContext(appName, config) {
|
|
121
124
|
const displayName = config.displayName || formatAppDisplayName(appName);
|
|
122
125
|
const port = config.port ?? 3000;
|
|
123
|
-
const localPort =
|
|
124
|
-
? config.build.localPort
|
|
125
|
-
: port;
|
|
126
|
+
const localPort = port;
|
|
126
127
|
const imageName = config.image?.name || `aifabrix/${appName}`;
|
|
127
128
|
// Extract registry from nested structure (config.image.registry) or flattened (config.registry)
|
|
128
129
|
const registry = config.image?.registry || config.registry || 'myacr.azurecr.io';
|
|
@@ -145,15 +146,17 @@ function buildReadmeContext(appName, config) {
|
|
|
145
146
|
function generateReadmeMd(appName, config) {
|
|
146
147
|
if (config.type === 'external') {
|
|
147
148
|
const systemKey = config.systemKey || appName;
|
|
149
|
+
const fileExt = config.fileExt !== undefined ? config.fileExt : '.json';
|
|
148
150
|
const datasources = Array.isArray(config.datasources) && config.datasources.length > 0
|
|
149
151
|
? config.datasources
|
|
150
|
-
: buildExternalDatasourcePlaceholders(systemKey, config.datasourceCount);
|
|
152
|
+
: buildExternalDatasourcePlaceholders(systemKey, config.datasourceCount, fileExt);
|
|
151
153
|
return generateExternalReadmeContent({
|
|
152
154
|
appName,
|
|
153
155
|
systemKey,
|
|
154
156
|
systemType: config.systemType,
|
|
155
157
|
displayName: config.systemDisplayName,
|
|
156
158
|
description: config.systemDescription,
|
|
159
|
+
fileExt: config.fileExt,
|
|
157
160
|
datasources
|
|
158
161
|
});
|
|
159
162
|
}
|