@aifabrix/builder 2.44.5 → 2.45.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/cli-layout.mdc +8 -4
- package/.cursor/rules/project-rules.mdc +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 +104 -2
- 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/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/helpers.js +3 -3
- package/lib/app/index.js +3 -3
- package/lib/app/push.js +46 -23
- package/lib/app/register.js +7 -6
- package/lib/app/restart-display.js +126 -0
- package/lib/app/rotate-secret.js +7 -6
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +58 -19
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +148 -74
- package/lib/app/show-display.js +7 -0
- package/lib/app/show.js +87 -5
- package/lib/build/index.js +83 -49
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +460 -0
- package/lib/cli/installation-log-command.js +73 -0
- package/lib/cli/setup-app.js +31 -3
- package/lib/cli/setup-auth.js +98 -27
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
- package/lib/cli/setup-infra-up-platform-action.js +131 -0
- package/lib/cli/setup-infra.js +132 -118
- 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-resolve.js +132 -0
- package/lib/cli/setup-utility.js +143 -84
- package/lib/commands/app-logs.js +81 -33
- package/lib/commands/auth-config.js +116 -18
- 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 +214 -31
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -338
- package/lib/commands/secure.js +1 -1
- package/lib/commands/setup-modes.js +468 -0
- package/lib/commands/setup-prompts.js +421 -0
- package/lib/commands/setup.js +254 -0
- package/lib/commands/teardown.js +277 -0
- package/lib/commands/up-common.js +113 -19
- package/lib/commands/up-dataplane.js +44 -19
- package/lib/commands/up-miso.js +18 -18
- package/lib/commands/upload.js +111 -23
- package/lib/commands/wizard-core-helpers.js +14 -11
- package/lib/commands/wizard-core.js +6 -5
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +4 -3
- package/lib/commands/wizard-headless.js +2 -1
- package/lib/commands/wizard.js +2 -1
- package/lib/constants/infra-compose-service-names.js +40 -0
- package/lib/core/audit-logger.js +1 -34
- package/lib/core/config-admin-email.js +56 -0
- package/lib/core/config-normalize.js +60 -0
- package/lib/core/config-registered-controller-urls.js +54 -0
- package/lib/core/config.js +33 -50
- 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 +428 -0
- package/lib/core/secrets-env-declarative-expand.js +170 -0
- package/lib/core/secrets-env-write.js +29 -1
- package/lib/core/secrets-load.js +252 -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 +9 -2
- package/lib/external-system/download.js +61 -32
- package/lib/external-system/sync-deploy-manifest.js +33 -0
- package/lib/infrastructure/index.js +49 -19
- package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
- package/lib/internal/node-fs.js +2 -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/application-schema.json +4 -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 -1
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/aifabrix-config-dir-walk.js +40 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
- package/lib/utils/app-config-resolver.js +24 -1
- package/lib/utils/app-run-containers.js +2 -2
- package/lib/utils/applications-config-defaults.js +206 -0
- package/lib/utils/auth-config-validator.js +2 -12
- 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/compose-generate-docker-compose.js +111 -6
- package/lib/utils/compose-generator.js +17 -8
- package/lib/utils/controller-url.js +50 -7
- 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/env-copy.js +99 -14
- package/lib/utils/env-template.js +5 -1
- package/lib/utils/external-readme.js +71 -2
- 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 +28 -10
- package/lib/utils/health-check.js +139 -107
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- package/lib/utils/infra-optional-service-flags.js +69 -0
- package/lib/utils/installation-log-core.js +282 -0
- package/lib/utils/installation-log-record.js +237 -0
- package/lib/utils/installation-log.js +123 -0
- package/lib/utils/integration-file-backup.js +74 -0
- package/lib/utils/log-redaction.js +105 -0
- package/lib/utils/manifest-location.js +164 -0
- package/lib/utils/manifest-source-emit.js +162 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +308 -76
- 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 +49 -4
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/run-cli-flags.js +29 -0
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- package/lib/utils/secrets-canonical.js +10 -3
- 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 +26 -13
- package/lib/utils/secrets-utils.js +20 -10
- package/lib/utils/system-builder-root.js +42 -0
- package/lib/utils/url-declarative-public-base.js +80 -12
- package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
- package/lib/utils/url-declarative-resolve-build.js +24 -388
- package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
- package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
- package/lib/utils/url-declarative-resolve.js +47 -7
- package/lib/utils/url-declarative-runtime-base-path.js +52 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry-scan.js +103 -0
- package/lib/utils/urls-local-registry.js +158 -76
- 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 +3 -1
- package/templates/applications/dataplane/application.yaml +5 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +9 -0
- package/templates/applications/miso-controller/env.template +27 -29
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +83 -123
- package/.npmrc.token +0 -1
- 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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive controller URLs stored in config (default + device token keys).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Registered controller URL list for auth --set-controller pick mode
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Build sorted unique controller URLs from a config object.
|
|
13
|
+
* @param {Object} cfg - Parsed config.yaml object
|
|
14
|
+
* @param {(url: string) => string} normalizeControllerUrl - normalizer from config module
|
|
15
|
+
* @returns {string[]}
|
|
16
|
+
*/
|
|
17
|
+
function buildRegisteredControllerUrlList(cfg, normalizeControllerUrl) {
|
|
18
|
+
const seen = new Set();
|
|
19
|
+
const add = (raw) => {
|
|
20
|
+
if (!raw || typeof raw !== 'string') return;
|
|
21
|
+
const trimmed = raw.trim();
|
|
22
|
+
if (!/^https?:\/\//i.test(trimmed)) return;
|
|
23
|
+
let parsed;
|
|
24
|
+
try {
|
|
25
|
+
parsed = new URL(trimmed);
|
|
26
|
+
} catch {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return;
|
|
30
|
+
seen.add(normalizeControllerUrl(trimmed));
|
|
31
|
+
};
|
|
32
|
+
if (cfg.controller) {
|
|
33
|
+
add(cfg.controller);
|
|
34
|
+
}
|
|
35
|
+
for (const key of Object.keys(cfg.device || {})) {
|
|
36
|
+
add(key);
|
|
37
|
+
}
|
|
38
|
+
return [...seen].sort((a, b) => a.localeCompare(b));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {() => Promise<Object>} getConfig - async config loader
|
|
43
|
+
* @param {(url: string) => string} normalizeControllerUrl
|
|
44
|
+
* @returns {Promise<string[]>}
|
|
45
|
+
*/
|
|
46
|
+
async function getRegisteredControllerUrlsWithLoader(getConfig, normalizeControllerUrl) {
|
|
47
|
+
const cfg = await getConfig();
|
|
48
|
+
return buildRegisteredControllerUrlList(cfg, normalizeControllerUrl);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
buildRegisteredControllerUrlList,
|
|
53
|
+
getRegisteredControllerUrlsWithLoader
|
|
54
|
+
};
|
package/lib/core/config.js
CHANGED
|
@@ -15,62 +15,15 @@ const os = require('os');
|
|
|
15
15
|
const { encryptToken, decryptToken, isTokenEncrypted } = require('../utils/token-encryption');
|
|
16
16
|
const { ensureSecureFilePermissions, ensureSecureDirPermissions } = require('../utils/secure-file-permissions');
|
|
17
17
|
const { getRuntimeConfigDir, getRuntimeConfigFile } = require('./config-runtime-paths');
|
|
18
|
+
const { getAdminEmailFromConfig, setAdminEmailInConfig } = require('./config-admin-email');
|
|
19
|
+
const { getRegisteredControllerUrlsWithLoader } = require('./config-registered-controller-urls');
|
|
20
|
+
const { normalizeControllerUrl, validateAndNormalizeDeveloperId } = require('./config-normalize');
|
|
18
21
|
// Avoid importing paths.js here to prevent circular dependency; use shared runtime config dir helper.
|
|
19
22
|
// Config location: AIFABRIX_CONFIG dirname → AIFABRIX_HOME (with ~/.aifabrix fallback when config lives there) → ~/.aifabrix
|
|
20
23
|
|
|
21
24
|
// Cache for developer ID - loaded when getConfig() is first called
|
|
22
25
|
let cachedDeveloperId = null;
|
|
23
26
|
|
|
24
|
-
/**
|
|
25
|
-
* Normalize controller URL for consistent storage and lookup
|
|
26
|
-
* Removes trailing slashes and normalizes the URL format
|
|
27
|
-
* @param {string} url - Controller URL to normalize
|
|
28
|
-
* @returns {string} Normalized controller URL
|
|
29
|
-
*/
|
|
30
|
-
function normalizeControllerUrl(url) {
|
|
31
|
-
if (!url || typeof url !== 'string') {
|
|
32
|
-
return url;
|
|
33
|
-
}
|
|
34
|
-
// Remove trailing slashes
|
|
35
|
-
let normalized = url.trim().replace(/\/+$/, '');
|
|
36
|
-
// Ensure it starts with http:// or https://
|
|
37
|
-
if (!normalized.match(/^https?:\/\//)) {
|
|
38
|
-
// If it doesn't start with protocol, assume http://
|
|
39
|
-
normalized = `http://${normalized}`;
|
|
40
|
-
}
|
|
41
|
-
return normalized;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Validate and normalize developer ID
|
|
46
|
-
* @param {*} developerId - Developer ID value (can be string, number, undefined, or null)
|
|
47
|
-
* @returns {string} Normalized developer ID as string
|
|
48
|
-
* @throws {Error} If developer ID is invalid
|
|
49
|
-
*/
|
|
50
|
-
function validateAndNormalizeDeveloperId(developerId) {
|
|
51
|
-
const DEV_ID_DIGITS_REGEX = /^[0-9]+$/;
|
|
52
|
-
|
|
53
|
-
if (typeof developerId === 'undefined' || developerId === null) {
|
|
54
|
-
return '0';
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (typeof developerId === 'number') {
|
|
58
|
-
if (developerId < 0 || !Number.isFinite(developerId)) {
|
|
59
|
-
throw new Error('Developer ID must be a non-negative digit string or number');
|
|
60
|
-
}
|
|
61
|
-
return String(developerId);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (typeof developerId === 'string') {
|
|
65
|
-
if (!DEV_ID_DIGITS_REGEX.test(developerId)) {
|
|
66
|
-
throw new Error('Developer ID must be a non-negative digit string or number');
|
|
67
|
-
}
|
|
68
|
-
return developerId;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
throw new Error('Developer ID must be a non-negative digit string or number');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
27
|
/**
|
|
75
28
|
* Ensure default config values exist
|
|
76
29
|
* @param {Object} config - Configuration object
|
|
@@ -258,6 +211,25 @@ async function getTlsEnabled() {
|
|
|
258
211
|
return cfg.tlsEnabled === true;
|
|
259
212
|
}
|
|
260
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Admin email stored in `config.yaml` by `aifabrix setup` (Keycloak / pgAdmin). Empty when unset.
|
|
216
|
+
*
|
|
217
|
+
* @returns {Promise<string>}
|
|
218
|
+
*/
|
|
219
|
+
async function getAdminEmail() {
|
|
220
|
+
return getAdminEmailFromConfig(getConfig);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Persist admin email to `config.yaml` (used on subsequent setup runs as the prompt default).
|
|
225
|
+
*
|
|
226
|
+
* @param {string} email
|
|
227
|
+
* @returns {Promise<void>}
|
|
228
|
+
*/
|
|
229
|
+
async function setAdminEmail(email) {
|
|
230
|
+
return setAdminEmailInConfig(getConfig, saveConfig, email);
|
|
231
|
+
}
|
|
232
|
+
|
|
261
233
|
/**
|
|
262
234
|
* Whether Traefik is enabled (`traefik: true` in config; infra compose includes the proxy).
|
|
263
235
|
* @returns {Promise<boolean>}
|
|
@@ -316,6 +288,14 @@ async function getControllerUrl() {
|
|
|
316
288
|
return config.controller || null;
|
|
317
289
|
}
|
|
318
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Controller URLs from config: `controller` plus `device` token keys.
|
|
293
|
+
* @returns {Promise<string[]>}
|
|
294
|
+
*/
|
|
295
|
+
async function getRegisteredControllerUrls() {
|
|
296
|
+
return getRegisteredControllerUrlsWithLoader(getConfig, normalizeControllerUrl);
|
|
297
|
+
}
|
|
298
|
+
|
|
319
299
|
function isTokenExpired(expiresAt) {
|
|
320
300
|
if (!expiresAt) return true;
|
|
321
301
|
const expirationTime = new Date(expiresAt).getTime();
|
|
@@ -454,6 +434,8 @@ const exportsObj = {
|
|
|
454
434
|
loadDeveloperId,
|
|
455
435
|
getCurrentEnvironment,
|
|
456
436
|
getTlsEnabled,
|
|
437
|
+
getAdminEmail,
|
|
438
|
+
setAdminEmail,
|
|
457
439
|
getTraefikEnabled,
|
|
458
440
|
setCurrentEnvironment,
|
|
459
441
|
resolveEnvironment,
|
|
@@ -469,6 +451,7 @@ const exportsObj = {
|
|
|
469
451
|
normalizeControllerUrl,
|
|
470
452
|
setControllerUrl,
|
|
471
453
|
getControllerUrl,
|
|
454
|
+
getRegisteredControllerUrls,
|
|
472
455
|
get CONFIG_DIR() {
|
|
473
456
|
return getRuntimeConfigDir();
|
|
474
457
|
},
|
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()` (primary `~/.aifabrix/secrets.local.yaml` plus `aifabrix-secrets` file or remote API).
|
|
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
|
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AI Fabrix Builder – Ensure secrets in
|
|
2
|
+
* AI Fabrix Builder – Ensure secrets in the primary user store
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Automated flows (resolve, run, wizard, up-infra backfill) **only** write to
|
|
5
|
+
* `~/.aifabrix/secrets.local.yaml` (or the path from `getPrimaryUserSecretsLocalPath`).
|
|
6
|
+
* The shared store (`aifabrix-secrets` file or remote API) is **read** via `loadSecrets()`;
|
|
7
|
+
* writing there is **human-only** through `aifabrix secret set --shared` (see `commands/secrets-set.js`).
|
|
8
|
+
* New values are encrypted when secrets-encryption is set.
|
|
8
9
|
*
|
|
9
10
|
* @fileoverview Central ensure-secrets service for zero-touch install
|
|
10
11
|
* @author AI Fabrix Team
|
|
@@ -17,6 +18,7 @@ const os = require('os');
|
|
|
17
18
|
const config = require('./config');
|
|
18
19
|
const pathsUtil = require('../utils/paths');
|
|
19
20
|
const logger = require('../utils/logger');
|
|
21
|
+
const { formatSuccessLine, metadata } = require('../utils/cli-test-layout-chalk');
|
|
20
22
|
const remoteDevAuth = require('../utils/remote-dev-auth');
|
|
21
23
|
const devApi = require('../api/dev.api');
|
|
22
24
|
const {
|
|
@@ -31,7 +33,9 @@ const { loadEnvTemplate } = require('../utils/secrets-helpers');
|
|
|
31
33
|
const {
|
|
32
34
|
buildInfraPlaceholderContext,
|
|
33
35
|
isSecretKeyAllowedEmpty,
|
|
34
|
-
getInfraSecretKeysForUpInfra
|
|
36
|
+
getInfraSecretKeysForUpInfra,
|
|
37
|
+
loadMergedSecretsForEnsureMissingCheck,
|
|
38
|
+
shouldUseMergedSecretsForMissingKeys
|
|
35
39
|
} = require('./secrets-ensure-infra');
|
|
36
40
|
const { syncLiteralKvSecretsFromCliOverrides } = require('./secrets-infra-placeholder-sync');
|
|
37
41
|
|
|
@@ -59,10 +63,12 @@ function expandTilde(filePath) {
|
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
/**
|
|
62
|
-
* Resolve
|
|
66
|
+
* Resolve the **configured** shared store for inspection/validation (e.g. `secret validate` without a path).
|
|
67
|
+
* This is the `aifabrix-secrets` target (file or remote API), not the default for automated writes.
|
|
68
|
+
*
|
|
63
69
|
* - File path → that path (expand ~)
|
|
64
|
-
* - http(s) URL → remote
|
|
65
|
-
* - No config → user file
|
|
70
|
+
* - http(s) URL → remote
|
|
71
|
+
* - No config → primary user secrets file
|
|
66
72
|
*
|
|
67
73
|
* @returns {Promise<{ type: 'file'|'remote', filePath?: string, serverUrl?: string|null, secretsEndpointUrl?: string, clientCertPem?: string|null, serverCaPem?: string|null }>}
|
|
68
74
|
*/
|
|
@@ -91,6 +97,16 @@ async function resolveWriteTarget() {
|
|
|
91
97
|
return { type: 'file', filePath };
|
|
92
98
|
}
|
|
93
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Default write target for **automated** ensure / `setSecretInStore` (resolve, run, wizard, infra).
|
|
102
|
+
* Never the shared store; use `aifabrix secret set --shared` to write shared.
|
|
103
|
+
*
|
|
104
|
+
* @returns {Promise<{ type: 'file', filePath: string }>}
|
|
105
|
+
*/
|
|
106
|
+
async function resolvePrimaryUserWriteTarget() {
|
|
107
|
+
return { type: 'file', filePath: pathsUtil.getPrimaryUserSecretsLocalPath() };
|
|
108
|
+
}
|
|
109
|
+
|
|
94
110
|
/**
|
|
95
111
|
* Load existing secrets from the resolved target (file or remote).
|
|
96
112
|
*
|
|
@@ -166,34 +182,36 @@ function valueForKey(key, suggested, emptyForCredentials, placeholderContext) {
|
|
|
166
182
|
}
|
|
167
183
|
|
|
168
184
|
/**
|
|
169
|
-
*
|
|
170
|
-
* @param {
|
|
171
|
-
* @
|
|
172
|
-
* @param {Object} suggested - Suggested values
|
|
173
|
-
* @param {string[]} added - Array to push added keys to
|
|
174
|
-
* @param {string|null} encryptionKey - Encryption key for file fallback or null
|
|
175
|
-
* @returns {Promise<string[]>}
|
|
185
|
+
* Short label for logs (file path or remote secrets URL).
|
|
186
|
+
* @param {{ type?: string, filePath?: string, secretsEndpointUrl?: string }|null} target
|
|
187
|
+
* @returns {string}
|
|
176
188
|
*/
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
try {
|
|
182
|
-
await devApi.addSecret(
|
|
183
|
-
target.serverUrl,
|
|
184
|
-
target.clientCertPem,
|
|
185
|
-
{ key, value },
|
|
186
|
-
target.serverCaPem || undefined,
|
|
187
|
-
target.secretsEndpointUrl
|
|
188
|
-
);
|
|
189
|
-
added.push(key);
|
|
190
|
-
} catch (err) {
|
|
191
|
-
logger.warn(`Remote secret "${key}" failed (${err.message}); writing to local file.`);
|
|
192
|
-
await writeSecretToFile(target.filePath, key, value, encryptionKey);
|
|
193
|
-
added.push(key);
|
|
194
|
-
}
|
|
189
|
+
function summarizeSecretsStoreForLog(target) {
|
|
190
|
+
if (!target || typeof target !== 'object') return 'secrets store';
|
|
191
|
+
if (target.type === 'remote' && typeof target.secretsEndpointUrl === 'string' && target.secretsEndpointUrl.trim()) {
|
|
192
|
+
return target.secretsEndpointUrl.trim();
|
|
195
193
|
}
|
|
196
|
-
|
|
194
|
+
if (typeof target.filePath === 'string' && target.filePath.trim()) {
|
|
195
|
+
return target.filePath.trim();
|
|
196
|
+
}
|
|
197
|
+
return 'secrets store';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Log that new values were written for keys that were missing or empty.
|
|
202
|
+
* @param {string[]} added
|
|
203
|
+
* @param {{ type?: string, filePath?: string, secretsEndpointUrl?: string }|null} target
|
|
204
|
+
*/
|
|
205
|
+
function logNewSecretValuesWritten(added, target) {
|
|
206
|
+
if (!Array.isArray(added) || added.length === 0) return;
|
|
207
|
+
const where = summarizeSecretsStoreForLog(target);
|
|
208
|
+
logger.log(
|
|
209
|
+
formatSuccessLine(
|
|
210
|
+
`Wrote ${added.length} new secret value(s) to ${where}. ` +
|
|
211
|
+
'These keys were missing or empty; values are now stored.'
|
|
212
|
+
)
|
|
213
|
+
);
|
|
214
|
+
logger.log(metadata(` Keys: ${added.join(', ')}`));
|
|
197
215
|
}
|
|
198
216
|
|
|
199
217
|
/**
|
|
@@ -223,9 +241,7 @@ async function addSecretsToFile(batch) {
|
|
|
223
241
|
await writeSecretToFile(filePath, key, value, encryptionKey);
|
|
224
242
|
added.push(key);
|
|
225
243
|
}
|
|
226
|
-
|
|
227
|
-
logger.log(`✔ Ensured ${added.length} secret key(s): ${added.join(', ')}`);
|
|
228
|
-
}
|
|
244
|
+
logNewSecretValuesWritten(added, { type: 'file', filePath });
|
|
229
245
|
return added;
|
|
230
246
|
}
|
|
231
247
|
|
|
@@ -240,6 +256,9 @@ async function addSecretsToFile(batch) {
|
|
|
240
256
|
* @param {Object} [options] - Options
|
|
241
257
|
* @param {boolean} [options.emptyValuesForCredentials=false] - Use empty string for new values
|
|
242
258
|
* @param {Object} [options.suggestedValues] - Optional map key -> value for specific keys
|
|
259
|
+
* @param {boolean} [options.useMergedSecretsForMissingKeys] - When true, treat keys present in the full
|
|
260
|
+
* `loadSecrets()` merge (including remote `--shared`) as satisfied. When false, only the target store file
|
|
261
|
+
* is consulted. When omitted and the target file is the primary user secrets path, merged secrets are used (default).
|
|
243
262
|
* @returns {Promise<string[]>} Keys that were added (new or backfilled)
|
|
244
263
|
* @throws {Error} If config or file write fails
|
|
245
264
|
*/
|
|
@@ -253,8 +272,11 @@ async function ensureSecretsForKeys(keys, options = {}) {
|
|
|
253
272
|
: {};
|
|
254
273
|
const placeholderContext = options.placeholderContext;
|
|
255
274
|
|
|
256
|
-
const target = options._targetOverride || await
|
|
257
|
-
|
|
275
|
+
const target = options._targetOverride || (await resolvePrimaryUserWriteTarget());
|
|
276
|
+
let existing = await loadExistingFromTarget(target);
|
|
277
|
+
if (shouldUseMergedSecretsForMissingKeys(options, target)) {
|
|
278
|
+
existing = await loadMergedSecretsForEnsureMissingCheck();
|
|
279
|
+
}
|
|
258
280
|
const toAdd = keys.filter((k) => {
|
|
259
281
|
const v = existing[k];
|
|
260
282
|
const missingOrEmpty = v === undefined || v === null || (typeof v === 'string' && v.trim() === '');
|
|
@@ -265,9 +287,6 @@ async function ensureSecretsForKeys(keys, options = {}) {
|
|
|
265
287
|
const encryptionKey = await config.getSecretsEncryptionKey();
|
|
266
288
|
const added = [];
|
|
267
289
|
|
|
268
|
-
if (target.type === 'remote' && target.serverUrl && target.clientCertPem) {
|
|
269
|
-
return addSecretsRemote(target, toAdd, suggested, added, encryptionKey, placeholderContext);
|
|
270
|
-
}
|
|
271
290
|
return addSecretsToFile({
|
|
272
291
|
filePath: target.filePath,
|
|
273
292
|
toAdd,
|
|
@@ -288,6 +307,8 @@ async function ensureSecretsForKeys(keys, options = {}) {
|
|
|
288
307
|
* @param {string} envTemplatePathOrContent - Path to env.template or template content
|
|
289
308
|
* @param {Object} [options] - Options
|
|
290
309
|
* @param {boolean} [options.emptyValuesForCredentials=false] - Use empty string for new values
|
|
310
|
+
* @param {boolean} [options.useMergedSecretsForMissingKeys] - Same semantics as {@link ensureSecretsForKeys};
|
|
311
|
+
* defaults to merged lookup when the write target is the primary user secrets file
|
|
291
312
|
* @returns {Promise<string[]>} Keys that were added
|
|
292
313
|
* @throws {Error} If template cannot be read or ensure fails
|
|
293
314
|
*/
|
|
@@ -313,9 +334,12 @@ async function ensureSecretsFromEnvTemplate(envTemplatePathOrContent, options =
|
|
|
313
334
|
: path.resolve(process.cwd(), options.preferredFilePath);
|
|
314
335
|
target = { type: 'file', filePath };
|
|
315
336
|
} else {
|
|
316
|
-
target = await
|
|
337
|
+
target = await resolvePrimaryUserWriteTarget();
|
|
338
|
+
}
|
|
339
|
+
let existing = await loadExistingFromTarget(target);
|
|
340
|
+
if (shouldUseMergedSecretsForMissingKeys(options, target)) {
|
|
341
|
+
existing = await loadMergedSecretsForEnsureMissingCheck();
|
|
317
342
|
}
|
|
318
|
-
const existing = await loadExistingFromTarget(target);
|
|
319
343
|
const missingKeys = findMissingSecretKeys(template, existing);
|
|
320
344
|
return ensureSecretsForKeys(missingKeys, { ...options, _targetOverride: target });
|
|
321
345
|
}
|
|
@@ -358,29 +382,16 @@ async function writeSecretToStoreFile(filePath, key, strValue) {
|
|
|
358
382
|
*/
|
|
359
383
|
async function setSecretInStore(key, value) {
|
|
360
384
|
if (!key || typeof key !== 'string' || value === undefined) return;
|
|
361
|
-
const target = await resolveWriteTarget();
|
|
362
385
|
const strValue = typeof value === 'string' ? value : String(value);
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
await devApi.addSecret(
|
|
366
|
-
target.serverUrl,
|
|
367
|
-
target.clientCertPem,
|
|
368
|
-
{ key, value: strValue },
|
|
369
|
-
target.serverCaPem || undefined,
|
|
370
|
-
target.secretsEndpointUrl
|
|
371
|
-
);
|
|
372
|
-
} catch (err) {
|
|
373
|
-
logger.warn(`Could not sync secret "${key}" to remote store: ${err.message}`);
|
|
374
|
-
const encryptionKey = await config.getSecretsEncryptionKey();
|
|
375
|
-
await writeSecretToFile(target.filePath, key, strValue, encryptionKey);
|
|
376
|
-
}
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
await writeSecretToStoreFile(target.filePath, key, strValue);
|
|
386
|
+
const userPath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
387
|
+
await writeSecretToStoreFile(userPath, key, strValue);
|
|
380
388
|
}
|
|
381
389
|
|
|
382
390
|
/**
|
|
383
|
-
* Ensure infra secrets exist
|
|
391
|
+
* Ensure infra secrets exist before ensureAdminSecrets or startInfra.
|
|
392
|
+
* Writes go to the primary user secrets file only (same as all **`ensureSecretsForKeys`** / **`setSecretInStore`** paths).
|
|
393
|
+
* **`kv://` resolution** merges the shared store via **`loadSecrets()`**. To publish a key to shared, use
|
|
394
|
+
* **`aifabrix secret set --shared`**.
|
|
384
395
|
*
|
|
385
396
|
* @async
|
|
386
397
|
* @function ensureInfraSecrets
|
|
@@ -398,11 +409,21 @@ async function setSecretInStore(key, value) {
|
|
|
398
409
|
async function ensureInfraSecrets(options = {}) {
|
|
399
410
|
const keys = getInfraSecretKeysForUpInfra();
|
|
400
411
|
const placeholderContext = buildInfraPlaceholderContext(options);
|
|
401
|
-
const
|
|
412
|
+
const userSecretsPath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
413
|
+
const added = await ensureSecretsForKeys(keys, {
|
|
414
|
+
placeholderContext,
|
|
415
|
+
_targetOverride: { type: 'file', filePath: userSecretsPath }
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
async function writeInfraPlaceholderLiteralToUserLocal(key, value) {
|
|
419
|
+
const strValue = typeof value === 'string' ? value : String(value);
|
|
420
|
+
await writeSecretToStoreFile(userSecretsPath, key, strValue);
|
|
421
|
+
}
|
|
422
|
+
|
|
402
423
|
await syncLiteralKvSecretsFromCliOverrides(
|
|
403
424
|
options,
|
|
404
425
|
placeholderContext,
|
|
405
|
-
|
|
426
|
+
writeInfraPlaceholderLiteralToUserLocal,
|
|
406
427
|
infraParameterCatalogModule
|
|
407
428
|
);
|
|
408
429
|
return added;
|
|
@@ -414,6 +435,7 @@ module.exports = {
|
|
|
414
435
|
ensureInfraSecrets,
|
|
415
436
|
setSecretInStore,
|
|
416
437
|
resolveWriteTarget,
|
|
438
|
+
resolvePrimaryUserWriteTarget,
|
|
417
439
|
loadExistingFromTarget,
|
|
418
440
|
getInfraSecretKeysForUpInfra,
|
|
419
441
|
isSecretKeyAllowedEmpty,
|