@aifabrix/builder 2.44.4 → 2.44.6
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/cli-layout.mdc +1 -1
- package/.cursor/rules/project-rules.mdc +1 -1
- package/.npmrc.token +1 -1
- package/README.md +15 -23
- package/integration/hubspot-test/README.md +2 -0
- package/integration/hubspot-test/test.js +5 -3
- package/jest.projects.js +68 -17
- package/lib/api/controller-health.api.js +49 -0
- package/lib/api/dimension-values.api.js +82 -0
- package/lib/api/dimensions.api.js +114 -0
- package/lib/api/external-systems.api.js +1 -0
- package/lib/api/integration-clients.api.js +168 -0
- package/lib/api/types/dimension-values.types.js +28 -0
- package/lib/api/types/dimensions.types.js +31 -0
- package/lib/api/types/integration-clients.types.js +45 -0
- package/lib/api/types/wizard.types.js +2 -1
- package/lib/api/validation-runner.js +46 -25
- package/lib/app/deploy-config.js +11 -1
- package/lib/app/deploy-status-display.js +3 -3
- package/lib/app/deploy.js +36 -14
- package/lib/app/display.js +15 -11
- package/lib/app/push.js +46 -23
- package/lib/app/register.js +1 -1
- package/lib/app/restart-display.js +95 -0
- package/lib/app/rotate-secret.js +1 -1
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +44 -12
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +99 -73
- package/lib/build/index.js +75 -45
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +445 -0
- package/lib/cli/setup-app.help.js +1 -1
- package/lib/cli/setup-app.js +20 -2
- package/lib/cli/setup-app.test-commands.js +9 -5
- package/lib/cli/setup-auth.js +26 -0
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra.js +138 -61
- package/lib/cli/setup-integration-client.js +182 -0
- package/lib/cli/setup-parameters.js +21 -2
- package/lib/cli/setup-platform.js +102 -0
- package/lib/cli/setup-secrets.js +18 -6
- package/lib/cli/setup-utility.js +97 -33
- package/lib/commands/datasource-capability-dimension-cli.js +128 -0
- package/lib/commands/datasource-capability-output.js +29 -0
- package/lib/commands/datasource-capability-relate-cli.js +140 -0
- package/lib/commands/datasource-capability.js +411 -0
- package/lib/commands/datasource-unified-test-cli.options.js +1 -1
- package/lib/commands/datasource.js +53 -13
- package/lib/commands/dev-down.js +3 -3
- package/lib/commands/dev-infra-gate.js +32 -0
- package/lib/commands/dev-init.js +13 -7
- package/lib/commands/dimension-value.js +179 -0
- package/lib/commands/dimension.js +330 -0
- package/lib/commands/integration-client.js +430 -0
- package/lib/commands/login-device.js +65 -30
- package/lib/commands/login.js +21 -10
- package/lib/commands/parameters-validate.js +78 -13
- package/lib/commands/repair-datasource-auto-rbac.js +166 -0
- package/lib/commands/repair-datasource-keys.js +10 -5
- package/lib/commands/repair-datasource.js +19 -7
- package/lib/commands/repair-env-template.js +4 -1
- package/lib/commands/repair-openapi-sync.js +172 -0
- package/lib/commands/repair-persist.js +102 -0
- package/lib/commands/repair-rbac-extract.js +27 -0
- package/lib/commands/repair-rbac-migrate.js +186 -0
- package/lib/commands/repair-rbac.js +225 -19
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -354
- package/lib/commands/secure.js +1 -1
- package/lib/commands/setup-modes.js +455 -0
- package/lib/commands/setup-prompts.js +388 -0
- package/lib/commands/setup.js +149 -0
- package/lib/commands/teardown.js +228 -0
- package/lib/commands/test-e2e-external.js +4 -3
- package/lib/commands/up-common.js +97 -12
- package/lib/commands/up-dataplane.js +33 -11
- package/lib/commands/up-miso.js +7 -11
- package/lib/commands/upload.js +109 -23
- package/lib/commands/wizard-core-helpers.js +14 -11
- package/lib/commands/wizard-core.js +58 -15
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +72 -14
- package/lib/commands/wizard-headless.js +7 -3
- package/lib/commands/wizard-helpers.js +13 -1
- package/lib/commands/wizard.js +210 -61
- package/lib/constants/infra-compose-service-names.js +40 -0
- package/lib/core/env-reader.js +16 -3
- package/lib/core/secrets-admin-env.js +101 -0
- package/lib/core/secrets-ensure-infra.js +34 -1
- package/lib/core/secrets-ensure.js +88 -66
- package/lib/core/secrets-env-content.js +432 -0
- package/lib/core/secrets-env-write.js +27 -1
- package/lib/core/secrets-load.js +248 -0
- package/lib/core/secrets-names.js +32 -0
- package/lib/core/secrets.js +17 -757
- package/lib/datasource/capability/basic-exposure.js +76 -0
- package/lib/datasource/capability/capability-diff-slice.js +41 -0
- package/lib/datasource/capability/capability-key.js +34 -0
- package/lib/datasource/capability/capability-resolve.js +172 -0
- package/lib/datasource/capability/capability-storage-keys.js +22 -0
- package/lib/datasource/capability/copy-operations.js +348 -0
- package/lib/datasource/capability/copy-test-payload.js +139 -0
- package/lib/datasource/capability/create-operations.js +235 -0
- package/lib/datasource/capability/dimension-operations.js +151 -0
- package/lib/datasource/capability/dimension-validate.js +219 -0
- package/lib/datasource/capability/json-pointer.js +31 -0
- package/lib/datasource/capability/reference-rewrite.js +51 -0
- package/lib/datasource/capability/relate-operations.js +325 -0
- package/lib/datasource/capability/relate-validate.js +219 -0
- package/lib/datasource/capability/remove-operations.js +275 -0
- package/lib/datasource/capability/run-capability-copy.js +152 -0
- package/lib/datasource/capability/run-capability-diff.js +135 -0
- package/lib/datasource/capability/run-capability-dimension.js +291 -0
- package/lib/datasource/capability/run-capability-edit.js +377 -0
- package/lib/datasource/capability/run-capability-relate.js +193 -0
- package/lib/datasource/capability/run-capability-remove.js +105 -0
- package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
- package/lib/datasource/capability/validate-capability-slice.js +35 -0
- package/lib/datasource/list.js +136 -23
- package/lib/datasource/log-viewer.js +2 -4
- package/lib/datasource/unified-validation-run.js +51 -16
- package/lib/datasource/validate.js +53 -1
- package/lib/deployment/deploy-poll-ui.js +60 -0
- package/lib/deployment/deployer-status.js +29 -3
- package/lib/deployment/deployer.js +48 -30
- package/lib/deployment/environment.js +7 -2
- package/lib/deployment/poll-interval.js +72 -0
- package/lib/deployment/push.js +11 -9
- package/lib/external-system/deploy.js +4 -1
- package/lib/external-system/download.js +61 -32
- package/lib/external-system/sync-deploy-manifest.js +33 -0
- package/lib/generator/wizard-prompts.js +7 -1
- package/lib/generator/wizard.js +34 -0
- package/lib/infrastructure/index.js +49 -19
- package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
- package/lib/parameters/infra-kv-discovery.js +29 -4
- package/lib/parameters/infra-parameter-catalog.js +6 -3
- package/lib/parameters/infra-parameter-validate.js +67 -19
- package/lib/resolvers/datasource-resolver.js +53 -0
- package/lib/resolvers/dimension-file.js +52 -0
- package/lib/resolvers/manifest-resolver.js +133 -0
- package/lib/schema/external-datasource.schema.json +183 -53
- package/lib/schema/external-system.schema.json +23 -10
- package/lib/schema/infra.parameter.yaml +26 -11
- package/lib/schema/wizard-config.schema.json +2 -2
- package/lib/utils/aifabrix-config-dir-walk.js +40 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
- package/lib/utils/app-run-containers.js +2 -2
- package/lib/utils/bash-secret-env.js +59 -0
- package/lib/utils/cli-secrets-error-format.js +78 -0
- package/lib/utils/cli-test-layout-chalk.js +31 -9
- package/lib/utils/cli-utils.js +4 -36
- package/lib/utils/datasource-test-run-display.js +8 -0
- package/lib/utils/dev-hosts-helper.js +3 -2
- package/lib/utils/dev-init-ssh-merge.js +2 -1
- package/lib/utils/docker-build.js +17 -9
- package/lib/utils/docker-reload-mount.js +127 -0
- package/lib/utils/external-readme.js +117 -4
- package/lib/utils/external-system-local-test-tty.js +3 -2
- package/lib/utils/external-system-readiness-core.js +45 -12
- package/lib/utils/external-system-readiness-deploy-display.js +3 -3
- package/lib/utils/external-system-readiness-display-internals.js +33 -3
- package/lib/utils/external-system-readiness-display.js +10 -1
- package/lib/utils/file-upload.js +40 -3
- package/lib/utils/health-check-db-init.js +107 -0
- package/lib/utils/health-check-public-warn.js +69 -0
- package/lib/utils/health-check-url.js +19 -4
- package/lib/utils/health-check.js +135 -105
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- package/lib/utils/integration-file-backup.js +74 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +108 -25
- package/lib/utils/postgres-wipe.js +212 -0
- package/lib/utils/register-aifabrix-shell-env.js +15 -0
- package/lib/utils/remote-dev-auth.js +21 -5
- package/lib/utils/remote-docker-env.js +9 -1
- package/lib/utils/remote-secrets-loader.js +42 -3
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- package/lib/utils/secrets-helpers.js +17 -10
- package/lib/utils/secrets-kv-refs.js +42 -0
- package/lib/utils/secrets-kv-scope.js +19 -2
- package/lib/utils/secrets-materialize-local.js +134 -0
- package/lib/utils/secrets-path.js +24 -10
- package/lib/utils/secrets-utils.js +2 -2
- package/lib/utils/system-builder-root.js +34 -0
- package/lib/utils/url-declarative-resolve-build.js +6 -1
- package/lib/utils/url-declarative-runtime-base-path.js +32 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry.js +73 -20
- package/lib/utils/validation-poll-ui.js +81 -0
- package/lib/utils/validation-run-poll.js +29 -5
- package/lib/utils/with-muted-logger.js +53 -0
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +7 -0
- package/templates/applications/miso-controller/env.template +7 -7
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +89 -102
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
- package/lib/api/service-users.api.js +0 -150
- package/lib/api/types/service-users.types.js +0 -65
- package/lib/cli/setup-service-user.js +0 -187
- package/lib/commands/service-user.js +0 -429
package/lib/commands/wizard.js
CHANGED
|
@@ -26,6 +26,7 @@ const {
|
|
|
26
26
|
validateAndCheckAppDirectory,
|
|
27
27
|
formatDataplaneRejectedTokenMessage,
|
|
28
28
|
extractSessionId,
|
|
29
|
+
handleSourceSelection,
|
|
29
30
|
handleOpenApiParsing,
|
|
30
31
|
handleCredentialSelection,
|
|
31
32
|
handleTypeDetection,
|
|
@@ -49,6 +50,150 @@ const {
|
|
|
49
50
|
ensureIntegrationDir
|
|
50
51
|
} = require('./wizard-helpers');
|
|
51
52
|
const { humanizeAppKey } = require('../generator/wizard-prompts-secondary');
|
|
53
|
+
const { formatSuccessLine } = require('../utils/cli-layout-chalk');
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Map resolved source type/data onto wizard state.source.
|
|
57
|
+
* @param {Object} state - Mutable wizard state
|
|
58
|
+
* @param {string} sourceType - Source type key
|
|
59
|
+
* @param {unknown} sourceData - Raw source payload from prompts
|
|
60
|
+
*/
|
|
61
|
+
function applySourceSelectionToState(state, sourceType, sourceData) {
|
|
62
|
+
state.source = { type: sourceType };
|
|
63
|
+
if (sourceType === 'openapi-file') state.source.filePath = sourceData;
|
|
64
|
+
else if (sourceType === 'openapi-url') state.source.url = sourceData;
|
|
65
|
+
else if (sourceType === 'mcp-server') state.source.serverUrl = JSON.parse(sourceData).serverUrl;
|
|
66
|
+
else if (sourceType === 'known-platform') state.source.platform = sourceData;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Interactive or prefill source selection (Step 2).
|
|
71
|
+
* @returns {Promise<{ sourceType: string, sourceData: unknown }>}
|
|
72
|
+
*/
|
|
73
|
+
async function resolveWizardSourcePhase(dataplaneUrl, sessionId, authConfig, platforms, prefill) {
|
|
74
|
+
if (prefill?.source?.type) {
|
|
75
|
+
logger.log(chalk.gray(
|
|
76
|
+
`Using source from wizard.yaml (${prefill.source.type}). Skipping source prompts.`
|
|
77
|
+
));
|
|
78
|
+
return handleSourceSelection(dataplaneUrl, sessionId, authConfig, prefill.source);
|
|
79
|
+
}
|
|
80
|
+
return handleInteractiveSourceSelection(dataplaneUrl, sessionId, authConfig, platforms);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Credential selection with optional prefill (Step 3).
|
|
85
|
+
* @returns {Promise<string>} credentialIdOrKey for generation step
|
|
86
|
+
*/
|
|
87
|
+
async function resolveWizardCredentialPhase(dataplaneUrl, authConfig, prefill, state) {
|
|
88
|
+
if (prefill?.credential) {
|
|
89
|
+
state.credential = prefill.credential;
|
|
90
|
+
return handleCredentialSelection(dataplaneUrl, authConfig, prefill.credential);
|
|
91
|
+
}
|
|
92
|
+
const credentialAction = await promptForCredentialAction();
|
|
93
|
+
const configCredential = await resolveCredentialConfig(dataplaneUrl, authConfig, credentialAction);
|
|
94
|
+
state.credential = configCredential;
|
|
95
|
+
return handleCredentialSelection(dataplaneUrl, authConfig, configCredential);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Collect user intent and UI preferences (Step 5), with optional wizard.yaml prefill.
|
|
100
|
+
* @returns {Promise<{ userIntent: string, preferences: Object, hasPrefillIntent: boolean }>}
|
|
101
|
+
*/
|
|
102
|
+
async function collectIntentAndPreferences(preferencesPrefill) {
|
|
103
|
+
const prefillPrefs = preferencesPrefill;
|
|
104
|
+
const hasPrefillIntent =
|
|
105
|
+
prefillPrefs &&
|
|
106
|
+
typeof prefillPrefs.intent === 'string' &&
|
|
107
|
+
prefillPrefs.intent.trim().length > 0;
|
|
108
|
+
|
|
109
|
+
if (hasPrefillIntent) {
|
|
110
|
+
logger.log(chalk.gray(
|
|
111
|
+
'Using preferences from wizard.yaml (intent and toggles). Skipping preference prompts.'
|
|
112
|
+
));
|
|
113
|
+
const level = prefillPrefs.fieldOnboardingLevel;
|
|
114
|
+
const validLevel = level === 'standard' || level === 'minimal' ? level : 'full';
|
|
115
|
+
return {
|
|
116
|
+
userIntent: prefillPrefs.intent.trim(),
|
|
117
|
+
preferences: {
|
|
118
|
+
fieldOnboardingLevel: validLevel,
|
|
119
|
+
mcp: Boolean(prefillPrefs.enableMCP),
|
|
120
|
+
abac: Boolean(prefillPrefs.enableABAC),
|
|
121
|
+
rbac: Boolean(prefillPrefs.enableRBAC)
|
|
122
|
+
},
|
|
123
|
+
hasPrefillIntent: true
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const userIntent = await promptForUserIntent();
|
|
128
|
+
const preferences = await promptForUserPreferences();
|
|
129
|
+
return { userIntent, preferences, hasPrefillIntent: false };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Run Step 5 (generate), Step 6–7 (review), and save files; updates state.preferences.
|
|
134
|
+
* @param {Object} payload
|
|
135
|
+
* @param {string} payload.appKey - Application key
|
|
136
|
+
* @param {string} payload.dataplaneUrl - Dataplane URL
|
|
137
|
+
* @param {Object} payload.authConfig - Auth configuration
|
|
138
|
+
* @param {string} payload.sessionId - Wizard session ID
|
|
139
|
+
* @param {Object} payload.state - Mutable wizard state
|
|
140
|
+
* @param {Object} payload.flowOpts - Flow options (mode, systemIdOrKey, debug, prefill, etc.)
|
|
141
|
+
* @param {Object} payload.genInput - Generation inputs (openapiSpec, detectedType, credential…)
|
|
142
|
+
* @returns {Promise<Object|null>} Updated state or null if review cancelled
|
|
143
|
+
*/
|
|
144
|
+
async function completeWizardGenerateReviewSave(payload) {
|
|
145
|
+
const {
|
|
146
|
+
appKey,
|
|
147
|
+
dataplaneUrl,
|
|
148
|
+
authConfig,
|
|
149
|
+
sessionId,
|
|
150
|
+
state,
|
|
151
|
+
flowOpts,
|
|
152
|
+
genInput
|
|
153
|
+
} = payload;
|
|
154
|
+
const { mode, systemIdOrKey, debug, prefill } = flowOpts;
|
|
155
|
+
const genResult = await handleInteractiveConfigGeneration({
|
|
156
|
+
dataplaneUrl,
|
|
157
|
+
authConfig,
|
|
158
|
+
mode,
|
|
159
|
+
openapiSpec: genInput.openapiSpec,
|
|
160
|
+
detectedType: genInput.detectedType,
|
|
161
|
+
credentialIdOrKey: genInput.credentialIdOrKey,
|
|
162
|
+
systemIdOrKey,
|
|
163
|
+
sourceType: genInput.sourceType,
|
|
164
|
+
sourceData: genInput.sourceData,
|
|
165
|
+
entityName: genInput.entityName,
|
|
166
|
+
appName: appKey,
|
|
167
|
+
debug,
|
|
168
|
+
systemDisplayName: flowOpts.systemDisplayName,
|
|
169
|
+
preferencesPrefill: prefill?.preferences
|
|
170
|
+
});
|
|
171
|
+
const { systemConfig, datasourceConfigs, systemKey, preferences: savedPrefs } = genResult;
|
|
172
|
+
state.preferences = savedPrefs || {};
|
|
173
|
+
|
|
174
|
+
const finalConfigs = await handleConfigurationReview(
|
|
175
|
+
dataplaneUrl,
|
|
176
|
+
authConfig,
|
|
177
|
+
sessionId,
|
|
178
|
+
systemConfig,
|
|
179
|
+
datasourceConfigs,
|
|
180
|
+
{ appKey, debug }
|
|
181
|
+
);
|
|
182
|
+
if (!finalConfigs) return null;
|
|
183
|
+
|
|
184
|
+
await handleFileSaving(
|
|
185
|
+
appKey,
|
|
186
|
+
finalConfigs.systemConfig,
|
|
187
|
+
finalConfigs.datasourceConfigs,
|
|
188
|
+
systemKey || appKey,
|
|
189
|
+
{
|
|
190
|
+
dataplaneUrl,
|
|
191
|
+
authConfig,
|
|
192
|
+
enableRBAC: Boolean(savedPrefs?.enableRBAC)
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
return state;
|
|
196
|
+
}
|
|
52
197
|
|
|
53
198
|
/**
|
|
54
199
|
* Create wizard session with given mode and optional systemIdOrKey (no prompts)
|
|
@@ -125,12 +270,15 @@ async function handleInteractiveSourceSelection(dataplaneUrl, sessionId, authCon
|
|
|
125
270
|
* @param {Object} options.detectedType - Detected type info
|
|
126
271
|
* @param {string} [options.credentialIdOrKey] - Credential ID or key (optional)
|
|
127
272
|
* @param {string} [options.systemIdOrKey] - System ID or key (optional)
|
|
273
|
+
* @param {Object} [options.preferencesPrefill] - From wizard.yaml `preferences` (skip Step 5 prompts when intent is set)
|
|
128
274
|
* @returns {Promise<Object>} Generated configuration and preferences { systemConfig, datasourceConfigs, systemKey, preferences }
|
|
129
275
|
*/
|
|
130
276
|
async function handleInteractiveConfigGeneration(options) {
|
|
131
277
|
logger.log(chalk.blue('\n\uD83D\uDCCB Step 5: User Preferences'));
|
|
132
|
-
|
|
133
|
-
const preferences = await
|
|
278
|
+
|
|
279
|
+
const { userIntent, preferences, hasPrefillIntent } = await collectIntentAndPreferences(
|
|
280
|
+
options.preferencesPrefill
|
|
281
|
+
);
|
|
134
282
|
|
|
135
283
|
const configPrefs = {
|
|
136
284
|
intent: userIntent,
|
|
@@ -138,6 +286,9 @@ async function handleInteractiveConfigGeneration(options) {
|
|
|
138
286
|
enableMCP: preferences.mcp,
|
|
139
287
|
enableABAC: preferences.abac,
|
|
140
288
|
enableRBAC: preferences.rbac,
|
|
289
|
+
enableOpenAPIGeneration: hasPrefillIntent
|
|
290
|
+
? options.preferencesPrefill?.enableOpenAPIGeneration !== false
|
|
291
|
+
: true,
|
|
141
292
|
debug: options.debug === true
|
|
142
293
|
};
|
|
143
294
|
|
|
@@ -222,62 +373,43 @@ async function handleConfigurationReview(dataplaneUrl, authConfig, sessionId, sy
|
|
|
222
373
|
* @returns {Promise<Object>} Collected state (source, credential, preferences) for wizard.yaml save
|
|
223
374
|
*/
|
|
224
375
|
async function doWizardSteps(appKey, dataplaneUrl, authConfig, sessionId, flowOpts, state) {
|
|
225
|
-
const {
|
|
226
|
-
|
|
227
|
-
|
|
376
|
+
const { platforms, prefill } = flowOpts;
|
|
377
|
+
|
|
378
|
+
const { sourceType, sourceData } = await resolveWizardSourcePhase(
|
|
379
|
+
dataplaneUrl,
|
|
380
|
+
sessionId,
|
|
381
|
+
authConfig,
|
|
382
|
+
platforms,
|
|
383
|
+
prefill
|
|
228
384
|
);
|
|
229
|
-
state
|
|
230
|
-
if (sourceType === 'openapi-file') state.source.filePath = sourceData;
|
|
231
|
-
else if (sourceType === 'openapi-url') state.source.url = sourceData;
|
|
232
|
-
else if (sourceType === 'mcp-server') state.source.serverUrl = JSON.parse(sourceData).serverUrl;
|
|
233
|
-
else if (sourceType === 'known-platform') state.source.platform = sourceData;
|
|
385
|
+
applySourceSelectionToState(state, sourceType, sourceData);
|
|
234
386
|
|
|
235
387
|
const openapiSpec = await handleOpenApiParsing(dataplaneUrl, authConfig, sourceType, sourceData);
|
|
236
|
-
const
|
|
237
|
-
const configCredential = await resolveCredentialConfig(dataplaneUrl, authConfig, credentialAction);
|
|
238
|
-
state.credential = configCredential;
|
|
239
|
-
const credentialIdOrKey = await handleCredentialSelection(dataplaneUrl, authConfig, configCredential);
|
|
388
|
+
const credentialIdOrKey = await resolveWizardCredentialPhase(dataplaneUrl, authConfig, prefill, state);
|
|
240
389
|
|
|
241
390
|
const detectedType = await handleTypeDetection(dataplaneUrl, authConfig, openapiSpec);
|
|
391
|
+
const prefillEntityName = prefill?.source?.entityName;
|
|
242
392
|
const entityName = openapiSpec && sourceType !== 'known-platform'
|
|
243
|
-
? await handleEntitySelection(dataplaneUrl, authConfig, openapiSpec) : null;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
detectedType,
|
|
250
|
-
credentialIdOrKey,
|
|
251
|
-
systemIdOrKey,
|
|
252
|
-
sourceType,
|
|
253
|
-
sourceData,
|
|
254
|
-
entityName: entityName || undefined,
|
|
255
|
-
appName: appKey,
|
|
256
|
-
debug,
|
|
257
|
-
systemDisplayName: flowOpts.systemDisplayName
|
|
258
|
-
});
|
|
259
|
-
const { systemConfig, datasourceConfigs, systemKey, preferences: savedPrefs } = genResult;
|
|
260
|
-
state.preferences = savedPrefs || {};
|
|
261
|
-
|
|
262
|
-
const finalConfigs = await handleConfigurationReview(
|
|
393
|
+
? await handleEntitySelection(dataplaneUrl, authConfig, openapiSpec, prefillEntityName) : null;
|
|
394
|
+
if (entityName && state.source) {
|
|
395
|
+
state.source.entityName = entityName;
|
|
396
|
+
}
|
|
397
|
+
return completeWizardGenerateReviewSave({
|
|
398
|
+
appKey,
|
|
263
399
|
dataplaneUrl,
|
|
264
400
|
authConfig,
|
|
265
401
|
sessionId,
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
{
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
dataplaneUrl,
|
|
278
|
-
authConfig
|
|
279
|
-
);
|
|
280
|
-
return state;
|
|
402
|
+
state,
|
|
403
|
+
flowOpts,
|
|
404
|
+
genInput: {
|
|
405
|
+
openapiSpec,
|
|
406
|
+
detectedType,
|
|
407
|
+
credentialIdOrKey,
|
|
408
|
+
sourceType,
|
|
409
|
+
sourceData,
|
|
410
|
+
entityName: entityName || undefined
|
|
411
|
+
}
|
|
412
|
+
});
|
|
281
413
|
}
|
|
282
414
|
|
|
283
415
|
async function runWizardStepsAfterSession(appKey, dataplaneUrl, authConfig, sessionId, flowOpts) {
|
|
@@ -317,14 +449,14 @@ async function runWizardStepsAfterSession(appKey, dataplaneUrl, authConfig, sess
|
|
|
317
449
|
* @returns {Promise<void>} Resolves when wizard flow completes
|
|
318
450
|
*/
|
|
319
451
|
async function executeWizardFlow(appKey, dataplaneUrl, authConfig, flowOpts = {}) {
|
|
320
|
-
const { mode, systemIdOrKey, configPath, debug, systemDisplayName } = flowOpts;
|
|
452
|
+
const { mode, systemIdOrKey, configPath, debug, systemDisplayName, prefill } = flowOpts;
|
|
321
453
|
|
|
322
454
|
if (debug) {
|
|
323
455
|
logger.log(chalk.gray(`[DEBUG] Wizard debug mode enabled for app: ${appKey}`));
|
|
324
456
|
}
|
|
325
457
|
logger.log(chalk.blue('\n\uD83D\uDCCB Step 1: Create Session'));
|
|
326
458
|
const sessionId = await createSessionFromParams(dataplaneUrl, authConfig, mode, systemIdOrKey, appKey);
|
|
327
|
-
logger.log(
|
|
459
|
+
logger.log(formatSuccessLine('Session created'));
|
|
328
460
|
|
|
329
461
|
const platforms = mode === 'add-datasource' ? [] : await getWizardPlatforms(dataplaneUrl, authConfig);
|
|
330
462
|
const state = await runWizardStepsAfterSession(appKey, dataplaneUrl, authConfig, sessionId, {
|
|
@@ -333,7 +465,8 @@ async function executeWizardFlow(appKey, dataplaneUrl, authConfig, flowOpts = {}
|
|
|
333
465
|
platforms,
|
|
334
466
|
configPath,
|
|
335
467
|
debug: flowOpts.debug,
|
|
336
|
-
systemDisplayName
|
|
468
|
+
systemDisplayName,
|
|
469
|
+
prefill
|
|
337
470
|
});
|
|
338
471
|
if (!state) return;
|
|
339
472
|
|
|
@@ -348,6 +481,11 @@ async function executeWizardFlow(appKey, dataplaneUrl, authConfig, flowOpts = {}
|
|
|
348
481
|
* @param {string} [appName] - App name (for log message)
|
|
349
482
|
* @returns {Promise<Object|null>} Loaded config or null
|
|
350
483
|
*/
|
|
484
|
+
/**
|
|
485
|
+
* Load wizard.yaml when present. Returns full valid config, or invalid-but-parsed config for interactive prefill.
|
|
486
|
+
*
|
|
487
|
+
* @returns {Promise<{ valid: true, config: Object } | { valid: false, config: Object, errors: string[] } | null>}
|
|
488
|
+
*/
|
|
351
489
|
async function loadWizardConfigIfExists(configPath, appName) {
|
|
352
490
|
if (!configPath) return null;
|
|
353
491
|
const displayPath = appName ? `integration/${appName}/wizard.yaml` : configPath;
|
|
@@ -360,10 +498,16 @@ async function loadWizardConfigIfExists(configPath, appName) {
|
|
|
360
498
|
const result = await validateWizardConfig(configPath, { validateFilePaths: false });
|
|
361
499
|
if (result.valid && result.config) {
|
|
362
500
|
logger.log(chalk.green(`Loaded saved state from ${displayPath}. Resuming with saved choices.`));
|
|
363
|
-
return result.config;
|
|
501
|
+
return { valid: true, config: result.config };
|
|
364
502
|
}
|
|
365
503
|
if (result.errors?.length) {
|
|
366
|
-
logger.log(chalk.yellow(
|
|
504
|
+
logger.log(chalk.yellow(
|
|
505
|
+
`Loaded ${displayPath} but it does not fully validate; prefilling from file where possible.`
|
|
506
|
+
));
|
|
507
|
+
result.errors.forEach(err => logger.log(chalk.gray(` • ${err}`)));
|
|
508
|
+
}
|
|
509
|
+
if (result.config) {
|
|
510
|
+
return { valid: false, config: result.config, errors: result.errors || [] };
|
|
367
511
|
}
|
|
368
512
|
} catch (e) {
|
|
369
513
|
logger.log(chalk.gray(`Could not load wizard config from ${displayPath}: ${e.message}`));
|
|
@@ -470,15 +614,16 @@ async function handleWizardWithSavedConfig(options, loadedConfig, displayPath) {
|
|
|
470
614
|
}
|
|
471
615
|
|
|
472
616
|
async function handleWizardInteractive(options) {
|
|
617
|
+
const prefill = options.wizardPrefill;
|
|
473
618
|
const allowAddDatasource = !options.app;
|
|
474
619
|
const mode = allowAddDatasource ? await promptForMode(undefined, true) : 'create-system';
|
|
475
620
|
const resolved = mode === 'create-system'
|
|
476
|
-
? await resolveCreateNewPath(options, null)
|
|
477
|
-
: await resolveAddDatasourcePath(options, null);
|
|
621
|
+
? await resolveCreateNewPath(options, prefill || null)
|
|
622
|
+
: await resolveAddDatasourcePath(options, prefill || null);
|
|
478
623
|
if (!resolved) return;
|
|
479
624
|
const { appKey, configPath, dataplaneUrl, authConfig } = resolved;
|
|
480
625
|
const systemIdOrKey = mode === 'add-datasource' ? resolved.systemIdOrKey : undefined;
|
|
481
|
-
const systemDisplayName = options.systemDisplayName || options.displayName ||
|
|
626
|
+
const systemDisplayName = options.systemDisplayName || options.displayName || prefill?.systemDisplayName ||
|
|
482
627
|
(mode === 'create-system' ? humanizeAppKey(appKey) : undefined);
|
|
483
628
|
try {
|
|
484
629
|
await executeWizardFlow(appKey, dataplaneUrl, authConfig, {
|
|
@@ -486,7 +631,8 @@ async function handleWizardInteractive(options) {
|
|
|
486
631
|
systemIdOrKey,
|
|
487
632
|
configPath,
|
|
488
633
|
debug: options.debug,
|
|
489
|
-
systemDisplayName
|
|
634
|
+
systemDisplayName,
|
|
635
|
+
prefill: prefill || undefined
|
|
490
636
|
});
|
|
491
637
|
logger.log(chalk.gray(`To change settings, edit integration/${appKey}/wizard.yaml and run: aifabrix wizard ${appKey}`));
|
|
492
638
|
} catch (error) {
|
|
@@ -504,9 +650,12 @@ async function handleWizard(options = {}) {
|
|
|
504
650
|
return await handleWizardSilent(options);
|
|
505
651
|
}
|
|
506
652
|
logger.log(chalk.blue('\n\uD83E\uDDD9 AI Fabrix External System Wizard\n'));
|
|
507
|
-
const
|
|
508
|
-
if (
|
|
509
|
-
return await handleWizardWithSavedConfig(options,
|
|
653
|
+
const loadResult = await loadWizardConfigIfExists(options.configPath, options.app);
|
|
654
|
+
if (loadResult?.valid && loadResult.config) {
|
|
655
|
+
return await handleWizardWithSavedConfig(options, loadResult.config, displayPath);
|
|
656
|
+
}
|
|
657
|
+
if (loadResult?.config && loadResult.valid === false) {
|
|
658
|
+
return await handleWizardInteractive({ ...options, wizardPrefill: loadResult.config });
|
|
510
659
|
}
|
|
511
660
|
return await handleWizardInteractive(options);
|
|
512
661
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infra compose service names accepted by `aifabrix restart` and {@link restartService}.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Single source of truth for CLI help + validation
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/** @type {ReadonlyArray<{ name: string, description: string }>} */
|
|
12
|
+
const RESTARTABLE_INFRA_SERVICES = Object.freeze([
|
|
13
|
+
{ name: 'postgres', description: 'PostgreSQL database' },
|
|
14
|
+
{ name: 'redis', description: 'Redis' },
|
|
15
|
+
{ name: 'pgadmin', description: 'pgAdmin 4 web UI (only if enabled when you ran up-infra)' },
|
|
16
|
+
{ name: 'redis-commander', description: 'Redis Commander web UI (only if enabled when you ran up-infra)' },
|
|
17
|
+
{ name: 'traefik', description: 'Traefik reverse proxy (only if enabled when you ran up-infra)' }
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @returns {string[]} service names in compose order
|
|
22
|
+
*/
|
|
23
|
+
function getRestartableInfraServiceNames() {
|
|
24
|
+
return RESTARTABLE_INFRA_SERVICES.map((s) => s.name);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Aligned lines for Commander `addHelpText('after', …)`.
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
function buildRestartInfraHelpLines() {
|
|
32
|
+
const col = 22;
|
|
33
|
+
return RESTARTABLE_INFRA_SERVICES.map((s) => ` ${s.name.padEnd(col)}${s.description}`).join('\n');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
RESTARTABLE_INFRA_SERVICES,
|
|
38
|
+
getRestartableInfraServiceNames,
|
|
39
|
+
buildRestartInfraHelpLines
|
|
40
|
+
};
|
package/lib/core/env-reader.js
CHANGED
|
@@ -115,6 +115,19 @@ function detectSensitiveValue(key, value) {
|
|
|
115
115
|
return false;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Path segment after `kv://` for sensitive env vars; must match infra.parameter.yaml keys.
|
|
120
|
+
* API_KEY uses the shared miso-controller/dataplane catalog entry (not a flat `api-key` slug).
|
|
121
|
+
* @param {string} key - Environment variable name
|
|
122
|
+
* @returns {string}
|
|
123
|
+
*/
|
|
124
|
+
function sensitiveKvPathSegmentFromEnvKey(key) {
|
|
125
|
+
if (key === 'API_KEY') {
|
|
126
|
+
return 'miso-controller-secrets-apiKeyVault';
|
|
127
|
+
}
|
|
128
|
+
return key.toLowerCase().replace(/[^a-z0-9]/g, '-');
|
|
129
|
+
}
|
|
130
|
+
|
|
118
131
|
/**
|
|
119
132
|
* Convert existing .env variables to env.template format
|
|
120
133
|
* @param {Object} existingEnv - Existing environment variables
|
|
@@ -128,7 +141,7 @@ function convertToEnvTemplate(existingEnv, requiredVars) {
|
|
|
128
141
|
Object.entries(existingEnv).forEach(([key, value]) => {
|
|
129
142
|
if (detectSensitiveValue(key, value)) {
|
|
130
143
|
// Convert sensitive values to kv:// references
|
|
131
|
-
convertedEnv[key] = `kv://${key
|
|
144
|
+
convertedEnv[key] = `kv://${sensitiveKvPathSegmentFromEnvKey(key)}`;
|
|
132
145
|
} else {
|
|
133
146
|
// Keep non-sensitive values as-is
|
|
134
147
|
convertedEnv[key] = value;
|
|
@@ -148,8 +161,8 @@ function generateSecretsFromEnv(envVars) {
|
|
|
148
161
|
|
|
149
162
|
Object.entries(envVars).forEach(([key, value]) => {
|
|
150
163
|
if (detectSensitiveValue(key, value)) {
|
|
151
|
-
|
|
152
|
-
|
|
164
|
+
const secretName =
|
|
165
|
+
key === 'API_KEY' ? sensitiveKvPathSegmentFromEnvKey(key) : getCanonicalSecretName(key);
|
|
153
166
|
secrets[secretName] = value;
|
|
154
167
|
}
|
|
155
168
|
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infrastructure admin-secrets.env generation (PG/Redis Commander defaults).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Split from secrets.js for module size limits
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const logger = require('../utils/logger');
|
|
14
|
+
const config = require('./config');
|
|
15
|
+
const {
|
|
16
|
+
mergeInfraParameterDefaultsForCli,
|
|
17
|
+
getInfraParameterCatalog,
|
|
18
|
+
readRelaxedCatalogDefaults
|
|
19
|
+
} = require('../parameters/infra-parameter-catalog');
|
|
20
|
+
const { createDefaultSecrets } = require('../utils/secrets-generator');
|
|
21
|
+
const pathsUtil = require('../utils/paths');
|
|
22
|
+
const { loadSecrets } = require('./secrets-load');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Writes admin env key-value pairs to content; encrypts values when encryption key is set.
|
|
26
|
+
* @async
|
|
27
|
+
* @param {Object.<string, string>} adminObj - Key-value object (e.g. POSTGRES_PASSWORD, ...)
|
|
28
|
+
* @returns {Promise<string>} .env-style content (plaintext or secure:// for secrets)
|
|
29
|
+
*/
|
|
30
|
+
async function formatAdminSecretsContent(adminObj) {
|
|
31
|
+
const encryptionKey = await config.getSecretsEncryptionKey();
|
|
32
|
+
const { encryptSecret } = require('../utils/secrets-encryption');
|
|
33
|
+
const lines = ['# Infrastructure Admin Credentials'];
|
|
34
|
+
for (const [k, v] of Object.entries(adminObj)) {
|
|
35
|
+
const value = (v === null || v === undefined) ? '' : String(v).replace(/\n/g, ' ').trim();
|
|
36
|
+
const valueToWrite = encryptionKey ? encryptSecret(value, encryptionKey) : value;
|
|
37
|
+
lines.push(`${k}=${valueToWrite}`);
|
|
38
|
+
}
|
|
39
|
+
return lines.join('\n');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function loadSecretsOrBootstrapForAdmin(secretsPath) {
|
|
43
|
+
try {
|
|
44
|
+
return await loadSecrets(secretsPath);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
const defaultSecretsPath = secretsPath || path.join(pathsUtil.getAifabrixHome(), 'secrets.yaml');
|
|
47
|
+
if (!fs.existsSync(defaultSecretsPath)) {
|
|
48
|
+
logger.log('Creating default secrets file...');
|
|
49
|
+
await createDefaultSecrets(defaultSecretsPath);
|
|
50
|
+
return await loadSecrets(secretsPath);
|
|
51
|
+
}
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getInfraDefaultsMergedForAdmin() {
|
|
57
|
+
try {
|
|
58
|
+
return mergeInfraParameterDefaultsForCli(getInfraParameterCatalog().data, {});
|
|
59
|
+
} catch {
|
|
60
|
+
return {};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function buildLocalAdminSecretsObject(secrets, infraDefaults) {
|
|
65
|
+
const raw = secrets['postgres-passwordKeyVault'];
|
|
66
|
+
const relaxed = readRelaxedCatalogDefaults();
|
|
67
|
+
const postgresPassword =
|
|
68
|
+
(raw && String(raw).trim()) ||
|
|
69
|
+
infraDefaults.adminPassword ||
|
|
70
|
+
relaxed.adminPassword ||
|
|
71
|
+
'';
|
|
72
|
+
const pgAdminEmail = infraDefaults.adminEmail || relaxed.adminEmail || '';
|
|
73
|
+
return {
|
|
74
|
+
POSTGRES_PASSWORD: postgresPassword,
|
|
75
|
+
PGADMIN_DEFAULT_EMAIL: pgAdminEmail,
|
|
76
|
+
PGADMIN_DEFAULT_PASSWORD: postgresPassword,
|
|
77
|
+
REDIS_HOST: 'local:redis:6379:0:',
|
|
78
|
+
REDIS_COMMANDER_USER: 'admin',
|
|
79
|
+
REDIS_COMMANDER_PASSWORD: postgresPassword
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Generates admin secrets for infrastructure (beside config.yaml, typically ~/.aifabrix/admin-secrets.env). Defaults from infra.parameter.yaml `defaults`. */
|
|
84
|
+
async function generateAdminSecretsEnv(secretsPath) {
|
|
85
|
+
const secrets = await loadSecretsOrBootstrapForAdmin(secretsPath);
|
|
86
|
+
const infraDefaults = getInfraDefaultsMergedForAdmin();
|
|
87
|
+
const adminObj = buildLocalAdminSecretsObject(secrets, infraDefaults);
|
|
88
|
+
const aifabrixDir = pathsUtil.getAifabrixSystemDir();
|
|
89
|
+
const adminEnvPath = path.join(aifabrixDir, 'admin-secrets.env');
|
|
90
|
+
if (!fs.existsSync(aifabrixDir)) {
|
|
91
|
+
fs.mkdirSync(aifabrixDir, { recursive: true, mode: 0o700 });
|
|
92
|
+
}
|
|
93
|
+
const adminSecrets = await formatAdminSecretsContent(adminObj);
|
|
94
|
+
fs.writeFileSync(adminEnvPath, adminSecrets, { mode: 0o600 });
|
|
95
|
+
return adminEnvPath;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
formatAdminSecretsContent,
|
|
100
|
+
generateAdminSecretsEnv
|
|
101
|
+
};
|
|
@@ -70,8 +70,41 @@ function getInfraSecretKeysForUpInfra() {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Same merge as runtime `loadSecrets()` (user local + project path + remote shared API + defaults).
|
|
75
|
+
* Ensures missing-key checks treat remote `--shared` keys as already satisfied.
|
|
76
|
+
*
|
|
77
|
+
* @returns {Promise<Object.<string, string>>}
|
|
78
|
+
*/
|
|
79
|
+
async function loadMergedSecretsForEnsureMissingCheck() {
|
|
80
|
+
const { loadSecrets } = require('./secrets-load');
|
|
81
|
+
return loadSecrets(undefined);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Default on for writes to the primary user secrets file: consult full {@link loadSecrets} merge so remote
|
|
86
|
+
* `--shared` keys are not duplicated locally. Opt out with `useMergedSecretsForMissingKeys: false`.
|
|
87
|
+
*
|
|
88
|
+
* @param {{ useMergedSecretsForMissingKeys?: boolean }} options
|
|
89
|
+
* @param {{ type?: string, filePath?: string }} target
|
|
90
|
+
* @returns {boolean}
|
|
91
|
+
*/
|
|
92
|
+
function shouldUseMergedSecretsForMissingKeys(options, target) {
|
|
93
|
+
if (options.useMergedSecretsForMissingKeys === false) return false;
|
|
94
|
+
if (options.useMergedSecretsForMissingKeys === true) return true;
|
|
95
|
+
if (!target || target.type !== 'file' || !target.filePath) return false;
|
|
96
|
+
try {
|
|
97
|
+
const primary = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
98
|
+
return path.resolve(target.filePath) === path.resolve(primary);
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
73
104
|
module.exports = {
|
|
74
105
|
buildInfraPlaceholderContext,
|
|
75
106
|
isSecretKeyAllowedEmpty,
|
|
76
|
-
getInfraSecretKeysForUpInfra
|
|
107
|
+
getInfraSecretKeysForUpInfra,
|
|
108
|
+
loadMergedSecretsForEnsureMissingCheck,
|
|
109
|
+
shouldUseMergedSecretsForMissingKeys
|
|
77
110
|
};
|