@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
|
@@ -10,6 +10,21 @@
|
|
|
10
10
|
|
|
11
11
|
const path = require('path');
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* GET /api/dev/settings response parameter names (builder-cli.md §1).
|
|
15
|
+
* Single source of truth so CLI and config merge stay aligned with the contract.
|
|
16
|
+
*/
|
|
17
|
+
const SETTINGS_RESPONSE_KEYS = [
|
|
18
|
+
'user-mutagen-folder',
|
|
19
|
+
'secrets-encryption',
|
|
20
|
+
'aifabrix-secrets',
|
|
21
|
+
'aifabrix-env-config',
|
|
22
|
+
'remote-server',
|
|
23
|
+
'docker-endpoint',
|
|
24
|
+
'sync-ssh-user',
|
|
25
|
+
'sync-ssh-host'
|
|
26
|
+
];
|
|
27
|
+
|
|
13
28
|
/**
|
|
14
29
|
* Get path configuration value
|
|
15
30
|
* @async
|
|
@@ -73,6 +88,114 @@ function createEnvConfigPathFunctions(getConfigFn, saveConfigFn) {
|
|
|
73
88
|
};
|
|
74
89
|
}
|
|
75
90
|
|
|
91
|
+
function createRemoteConfigGetters(getConfigFn) {
|
|
92
|
+
return {
|
|
93
|
+
async getRemoteServer() {
|
|
94
|
+
return getPathConfig(getConfigFn, 'remote-server');
|
|
95
|
+
},
|
|
96
|
+
async getDockerEndpoint() {
|
|
97
|
+
return getPathConfig(getConfigFn, 'docker-endpoint');
|
|
98
|
+
},
|
|
99
|
+
async getUserMutagenFolder() {
|
|
100
|
+
return getPathConfig(getConfigFn, 'user-mutagen-folder');
|
|
101
|
+
},
|
|
102
|
+
async getSyncSshUser() {
|
|
103
|
+
return getPathConfig(getConfigFn, 'sync-ssh-user');
|
|
104
|
+
},
|
|
105
|
+
async getSyncSshHost() {
|
|
106
|
+
return getPathConfig(getConfigFn, 'sync-ssh-host');
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function createRemoteConfigSetters(getConfigFn, saveConfigFn) {
|
|
112
|
+
return {
|
|
113
|
+
async setRemoteServer(value) {
|
|
114
|
+
if (value !== null && value !== undefined && typeof value !== 'string') {
|
|
115
|
+
throw new Error('remote-server must be a string');
|
|
116
|
+
}
|
|
117
|
+
const config = await getConfigFn();
|
|
118
|
+
config['remote-server'] = value ? value.trim().replace(/\/+$/, '') : undefined;
|
|
119
|
+
await saveConfigFn(config);
|
|
120
|
+
},
|
|
121
|
+
async setDockerEndpoint(value) {
|
|
122
|
+
if (value !== null && value !== undefined && typeof value !== 'string') {
|
|
123
|
+
throw new Error('docker-endpoint must be a string');
|
|
124
|
+
}
|
|
125
|
+
const config = await getConfigFn();
|
|
126
|
+
config['docker-endpoint'] = value || undefined;
|
|
127
|
+
await saveConfigFn(config);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function isHttpUrl(value) {
|
|
133
|
+
return typeof value === 'string' && (value.startsWith('http://') || value.startsWith('https://'));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Derive hostname from a URL (e.g. https://builder.aifabrix.dev -> builder.aifabrix.dev).
|
|
138
|
+
* @param {string} url - URL string
|
|
139
|
+
* @returns {string|null} Hostname or null if invalid
|
|
140
|
+
*/
|
|
141
|
+
function hostnameFromUrl(url) {
|
|
142
|
+
if (!url || typeof url !== 'string') return null;
|
|
143
|
+
const s = url.trim().replace(/\/+$/, '');
|
|
144
|
+
if (!s) return null;
|
|
145
|
+
const withProtocol = s.match(/^https?:\/\//) ? s : `https://${s}`;
|
|
146
|
+
try {
|
|
147
|
+
return new URL(withProtocol).hostname || null;
|
|
148
|
+
} catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function applySecretsUrlFromRemote(config) {
|
|
154
|
+
const remoteServer = config['remote-server'];
|
|
155
|
+
const secretsPath = config['aifabrix-secrets'];
|
|
156
|
+
if (!remoteServer || !secretsPath || isHttpUrl(secretsPath)) return;
|
|
157
|
+
const base = typeof remoteServer === 'string' ? remoteServer.trim().replace(/\/+$/, '') : '';
|
|
158
|
+
if (base) config['aifabrix-secrets'] = `${base}/api/dev/secrets`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function applySyncAndDockerFromHost(config) {
|
|
162
|
+
const host = hostnameFromUrl(config['remote-server']);
|
|
163
|
+
if (!host) return;
|
|
164
|
+
if (!config['sync-ssh-host']) config['sync-ssh-host'] = host;
|
|
165
|
+
if (!config['docker-endpoint']) config['docker-endpoint'] = `tcp://${host}:2376`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function mergeRemoteSettingsImpl(getConfigFn, saveConfigFn, settings) {
|
|
169
|
+
if (!settings || typeof settings !== 'object') return;
|
|
170
|
+
const config = await getConfigFn();
|
|
171
|
+
for (const key of SETTINGS_RESPONSE_KEYS) {
|
|
172
|
+
const raw = settings[key];
|
|
173
|
+
if (raw === undefined || raw === null) continue;
|
|
174
|
+
const value = typeof raw === 'string' ? raw.trim() : raw;
|
|
175
|
+
if (value === '') continue;
|
|
176
|
+
config[key] = value;
|
|
177
|
+
}
|
|
178
|
+
applySecretsUrlFromRemote(config);
|
|
179
|
+
applySyncAndDockerFromHost(config);
|
|
180
|
+
await saveConfigFn(config);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Remote Docker / Builder Server config. Used when remote-server is set.
|
|
185
|
+
* @param {Function} getConfigFn - Function to get config
|
|
186
|
+
* @param {Function} saveConfigFn - Function to save config
|
|
187
|
+
* @returns {Object} Remote config getters, setters, and mergeRemoteSettings
|
|
188
|
+
*/
|
|
189
|
+
function createRemoteConfigFunctions(getConfigFn, saveConfigFn) {
|
|
190
|
+
return {
|
|
191
|
+
...createRemoteConfigGetters(getConfigFn),
|
|
192
|
+
...createRemoteConfigSetters(getConfigFn, saveConfigFn),
|
|
193
|
+
async mergeRemoteSettings(settings) {
|
|
194
|
+
return mergeRemoteSettingsImpl(getConfigFn, saveConfigFn, settings);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
76
199
|
/**
|
|
77
200
|
* Create path configuration functions with config access
|
|
78
201
|
* @param {Function} getConfigFn - Function to get config
|
|
@@ -82,13 +205,15 @@ function createEnvConfigPathFunctions(getConfigFn, saveConfigFn) {
|
|
|
82
205
|
function createPathConfigFunctions(getConfigFn, saveConfigFn) {
|
|
83
206
|
return {
|
|
84
207
|
...createHomeAndSecretsPathFunctions(getConfigFn, saveConfigFn),
|
|
85
|
-
...createEnvConfigPathFunctions(getConfigFn, saveConfigFn)
|
|
208
|
+
...createEnvConfigPathFunctions(getConfigFn, saveConfigFn),
|
|
209
|
+
...createRemoteConfigFunctions(getConfigFn, saveConfigFn)
|
|
86
210
|
};
|
|
87
211
|
}
|
|
88
212
|
|
|
89
213
|
module.exports = {
|
|
90
214
|
getPathConfig,
|
|
91
215
|
setPathConfig,
|
|
92
|
-
createPathConfigFunctions
|
|
216
|
+
createPathConfigFunctions,
|
|
217
|
+
SETTINGS_RESPONSE_KEYS
|
|
93
218
|
};
|
|
94
219
|
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves configuration section values for upload (variable → .env, keyvault → secrets)
|
|
3
|
+
* and re-templates configuration on download from env.template.
|
|
4
|
+
*
|
|
5
|
+
* @fileoverview Configuration env resolution for external system upload/download
|
|
6
|
+
* @author AI Fabrix Team
|
|
7
|
+
* @version 2.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const { getIntegrationPath } = require('./paths');
|
|
13
|
+
const { parseEnvToMap, resolveKvValue } = require('./credential-secrets-env');
|
|
14
|
+
const { loadSecrets, resolveKvReferences } = require('../core/secrets');
|
|
15
|
+
const { loadEnvTemplate } = require('./secrets-helpers');
|
|
16
|
+
const { getActualSecretsPath } = require('./secrets-path');
|
|
17
|
+
|
|
18
|
+
const VAR_PLACEHOLDER_REGEX = /\{\{([^}]+)\}\}/g;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Builds resolved env map and secrets for an integration app (for configuration resolution).
|
|
22
|
+
* If .env exists, parses it; otherwise resolves env.template with secrets and parses the result.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} systemKey - External system key (e.g. 'my-sharepoint')
|
|
25
|
+
* @returns {Promise<{ envMap: Object.<string, string>, secrets: Object }>} envMap for {{VAR}} substitution, secrets for kv://
|
|
26
|
+
* @throws {Error} If env.template is missing when .env is missing (only when building from template)
|
|
27
|
+
*/
|
|
28
|
+
async function buildResolvedEnvMapForIntegration(systemKey) {
|
|
29
|
+
if (!systemKey || typeof systemKey !== 'string') {
|
|
30
|
+
throw new Error('systemKey is required and must be a string');
|
|
31
|
+
}
|
|
32
|
+
const integrationPath = getIntegrationPath(systemKey);
|
|
33
|
+
const envPath = path.join(integrationPath, '.env');
|
|
34
|
+
const envTemplatePath = path.join(integrationPath, 'env.template');
|
|
35
|
+
|
|
36
|
+
let secrets = {};
|
|
37
|
+
try {
|
|
38
|
+
secrets = await loadSecrets(undefined, systemKey);
|
|
39
|
+
} catch {
|
|
40
|
+
secrets = {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let envMap = {};
|
|
44
|
+
if (fs.existsSync(envPath)) {
|
|
45
|
+
const content = fs.readFileSync(envPath, 'utf8');
|
|
46
|
+
envMap = parseEnvToMap(content);
|
|
47
|
+
} else if (fs.existsSync(envTemplatePath)) {
|
|
48
|
+
const templateContent = loadEnvTemplate(envTemplatePath);
|
|
49
|
+
const secretsPaths = await getActualSecretsPath(undefined, systemKey);
|
|
50
|
+
const resolvedContent = await resolveKvReferences(
|
|
51
|
+
templateContent,
|
|
52
|
+
secrets,
|
|
53
|
+
'local',
|
|
54
|
+
secretsPaths,
|
|
55
|
+
systemKey
|
|
56
|
+
);
|
|
57
|
+
envMap = parseEnvToMap(resolvedContent);
|
|
58
|
+
}
|
|
59
|
+
return { envMap, secrets };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resolves {{VAR}} in a string using envMap. Throws if any variable is missing.
|
|
64
|
+
*
|
|
65
|
+
* @param {string} value - Value that may contain {{VAR}}
|
|
66
|
+
* @param {Object.<string, string>} envMap - Resolved env key-value map
|
|
67
|
+
* @param {string} [systemKey] - System key for error message
|
|
68
|
+
* @returns {string} Value with {{VAR}} replaced
|
|
69
|
+
* @throws {Error} If a {{VAR}} is missing from envMap
|
|
70
|
+
*/
|
|
71
|
+
function substituteVarPlaceholders(value, envMap, systemKey) {
|
|
72
|
+
const hint = systemKey ? ` Run 'aifabrix resolve ${systemKey}' or set the variable in .env.` : '';
|
|
73
|
+
return value.replace(VAR_PLACEHOLDER_REGEX, (match, varName) => {
|
|
74
|
+
const key = varName.trim();
|
|
75
|
+
if (envMap[key] === undefined || envMap[key] === null) {
|
|
76
|
+
throw new Error(`Missing configuration env var: ${key}.${hint}`);
|
|
77
|
+
}
|
|
78
|
+
return String(envMap[key]);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Resolves configuration array values in place by location: variable → {{VAR}} from envMap;
|
|
84
|
+
* keyvault → kv:// from secrets. Does not log or expose secret values.
|
|
85
|
+
*
|
|
86
|
+
* @param {Array<{ name?: string, value?: string, location?: string }>} configArray - Configuration array (mutated)
|
|
87
|
+
* @param {Object.<string, string>} envMap - Resolved env map from buildResolvedEnvMapForIntegration
|
|
88
|
+
* @param {Object} secrets - Loaded secrets for kv:// resolution
|
|
89
|
+
* @param {string} [systemKey] - System key for error messages
|
|
90
|
+
* @throws {Error} If variable env is missing or keyvault secret unresolved (message never contains secret values)
|
|
91
|
+
*/
|
|
92
|
+
function resolveConfigurationValues(configArray, envMap, secrets, systemKey) {
|
|
93
|
+
if (!Array.isArray(configArray)) return;
|
|
94
|
+
const hint = systemKey ? ` Run 'aifabrix resolve ${systemKey}' and ensure the key exists in the secrets file.` : '';
|
|
95
|
+
for (const item of configArray) {
|
|
96
|
+
if (!item || typeof item.value !== 'string') continue;
|
|
97
|
+
const location = (item.location || '').toLowerCase();
|
|
98
|
+
if (location === 'variable') {
|
|
99
|
+
if (item.value.trim().startsWith('kv://')) {
|
|
100
|
+
throw new Error(`Configuration entry '${item.name || 'unknown'}' has location 'variable' but value is kv://. Use location 'keyvault' for secrets.`);
|
|
101
|
+
}
|
|
102
|
+
item.value = substituteVarPlaceholders(item.value, envMap, systemKey);
|
|
103
|
+
} else if (location === 'keyvault') {
|
|
104
|
+
const resolved = resolveKvValue(secrets, item.value);
|
|
105
|
+
if (resolved === null || resolved === undefined) {
|
|
106
|
+
throw new Error(`Unresolved keyvault reference for configuration '${item.name || 'unknown'}'.${hint}`);
|
|
107
|
+
}
|
|
108
|
+
item.value = resolved;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Returns the set of variable names (keys) defined in env.template content.
|
|
115
|
+
*
|
|
116
|
+
* @param {string} envTemplateContent - Raw env.template content
|
|
117
|
+
* @returns {Set<string>} Set of variable names
|
|
118
|
+
*/
|
|
119
|
+
function getEnvTemplateVariableNames(envTemplateContent) {
|
|
120
|
+
const names = new Set();
|
|
121
|
+
if (!envTemplateContent || typeof envTemplateContent !== 'string') return names;
|
|
122
|
+
const lines = envTemplateContent.split(/\r?\n/);
|
|
123
|
+
for (const line of lines) {
|
|
124
|
+
const trimmed = line.trim();
|
|
125
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
126
|
+
const eq = trimmed.indexOf('=');
|
|
127
|
+
if (eq > 0) {
|
|
128
|
+
const key = trimmed.substring(0, eq).trim();
|
|
129
|
+
if (key) names.add(key);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return names;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Re-templates configuration from env.template: for each entry with location === 'variable'
|
|
137
|
+
* whose name matches a key in env.template, sets value to {{name}}. Mutates configArray in place.
|
|
138
|
+
*
|
|
139
|
+
* @param {Array<{ name?: string, value?: string, location?: string }>} configArray - Configuration array (mutated)
|
|
140
|
+
* @param {Set<string>} envTemplateVariableNames - Variable names present in env.template
|
|
141
|
+
*/
|
|
142
|
+
function retemplateConfigurationFromEnvTemplate(configArray, envTemplateVariableNames) {
|
|
143
|
+
if (!Array.isArray(configArray) || !envTemplateVariableNames || !envTemplateVariableNames.size) return;
|
|
144
|
+
for (const item of configArray) {
|
|
145
|
+
if (!item || (item.location || '').toLowerCase() !== 'variable') continue;
|
|
146
|
+
const name = item.name && String(item.name).trim();
|
|
147
|
+
if (name && envTemplateVariableNames.has(name)) {
|
|
148
|
+
item.value = `{{${name}}}`;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Reads env.template at integration path, re-templates the given configuration array,
|
|
155
|
+
* and returns the updated array (mutates in place). If env.template is missing, does nothing.
|
|
156
|
+
*
|
|
157
|
+
* @param {string} systemKey - External system key
|
|
158
|
+
* @param {Array<{ name?: string, value?: string, location?: string }>} configArray - Configuration array (mutated)
|
|
159
|
+
* @returns {Promise<boolean>} True if re-templating was applied (env.template existed)
|
|
160
|
+
*/
|
|
161
|
+
async function retemplateConfigurationForDownload(systemKey, configArray) {
|
|
162
|
+
if (!systemKey || typeof systemKey !== 'string' || !Array.isArray(configArray)) return false;
|
|
163
|
+
const integrationPath = getIntegrationPath(systemKey);
|
|
164
|
+
const envTemplatePath = path.join(integrationPath, 'env.template');
|
|
165
|
+
if (!fs.existsSync(envTemplatePath)) return false;
|
|
166
|
+
const content = fs.readFileSync(envTemplatePath, 'utf8');
|
|
167
|
+
const names = getEnvTemplateVariableNames(content);
|
|
168
|
+
retemplateConfigurationFromEnvTemplate(configArray, names);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
module.exports = {
|
|
173
|
+
buildResolvedEnvMapForIntegration,
|
|
174
|
+
resolveConfigurationValues,
|
|
175
|
+
getEnvTemplateVariableNames,
|
|
176
|
+
retemplateConfigurationFromEnvTemplate,
|
|
177
|
+
retemplateConfigurationForDownload,
|
|
178
|
+
substituteVarPlaceholders
|
|
179
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential display utilities – status icons and formatting for CLI output
|
|
3
|
+
* Aligns with dataplane credential status lifecycle (pending, verified, failed, expired).
|
|
4
|
+
*
|
|
5
|
+
* @fileoverview Credential status formatter with icons and chalk colors
|
|
6
|
+
* @author AI Fabrix Team
|
|
7
|
+
* @version 2.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const chalk = require('chalk');
|
|
11
|
+
|
|
12
|
+
/** @type {{ verified: string, pending: string, failed: string, expired: string }} */
|
|
13
|
+
const STATUS_ICONS = {
|
|
14
|
+
verified: ' ✓',
|
|
15
|
+
pending: ' ○',
|
|
16
|
+
failed: ' ✗',
|
|
17
|
+
expired: ' ⊘'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** @type {{ verified: string, pending: string, failed: string, expired: string }} */
|
|
21
|
+
const STATUS_LABELS = {
|
|
22
|
+
verified: 'Valid',
|
|
23
|
+
pending: 'Not tested',
|
|
24
|
+
failed: 'Connection failed',
|
|
25
|
+
expired: 'Token expired'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Chalk color functions per status
|
|
30
|
+
* @type {{ verified: Function, pending: Function, failed: Function, expired: Function }}
|
|
31
|
+
*/
|
|
32
|
+
const STATUS_CHALK = {
|
|
33
|
+
verified: chalk.green,
|
|
34
|
+
pending: chalk.gray,
|
|
35
|
+
failed: chalk.red,
|
|
36
|
+
expired: chalk.yellow
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const VALID_STATUSES = ['verified', 'pending', 'failed', 'expired'];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Format credential status for display (icon + optional label)
|
|
43
|
+
* @param {string} [status] - Credential status (pending | verified | failed | expired)
|
|
44
|
+
* @returns {{ icon: string, color: Function, label: string } | null} Status info or null when missing/invalid
|
|
45
|
+
*/
|
|
46
|
+
function formatCredentialStatus(status) {
|
|
47
|
+
if (!status || typeof status !== 'string') return null;
|
|
48
|
+
const s = status.toLowerCase();
|
|
49
|
+
if (!VALID_STATUSES.includes(s)) return null;
|
|
50
|
+
return {
|
|
51
|
+
icon: STATUS_ICONS[s],
|
|
52
|
+
color: STATUS_CHALK[s],
|
|
53
|
+
label: STATUS_LABELS[s]
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Format credential with status for CLI display
|
|
59
|
+
* @param {Object} credential - Credential object from API
|
|
60
|
+
* @param {string} [credential.key]
|
|
61
|
+
* @param {string} [credential.id]
|
|
62
|
+
* @param {string} [credential.credentialKey]
|
|
63
|
+
* @param {string} [credential.displayName]
|
|
64
|
+
* @param {string} [credential.name]
|
|
65
|
+
* @param {string} [credential.status]
|
|
66
|
+
* @returns {{ key: string, name: string, statusFormatted: string, statusLabel: string }}
|
|
67
|
+
*/
|
|
68
|
+
function formatCredentialWithStatus(credential) {
|
|
69
|
+
const key = credential?.key ?? credential?.id ?? credential?.credentialKey ?? '-';
|
|
70
|
+
const name = credential?.displayName ?? credential?.name ?? key;
|
|
71
|
+
const statusInfo = formatCredentialStatus(credential?.status);
|
|
72
|
+
const statusFormatted = statusInfo ? statusInfo.color(statusInfo.icon) : '';
|
|
73
|
+
const statusLabel = statusInfo ? ` (${statusInfo.label})` : '';
|
|
74
|
+
return { key, name, statusFormatted, statusLabel };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
STATUS_ICONS,
|
|
79
|
+
STATUS_LABELS,
|
|
80
|
+
STATUS_CHALK,
|
|
81
|
+
formatCredentialStatus,
|
|
82
|
+
formatCredentialWithStatus
|
|
83
|
+
};
|