@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.
Files changed (249) hide show
  1. package/.cursor/rules/cli-layout.mdc +8 -4
  2. package/.cursor/rules/project-rules.mdc +1 -1
  3. package/README.md +15 -23
  4. package/integration/hubspot-test/README.md +2 -0
  5. package/integration/hubspot-test/test.js +5 -3
  6. package/jest.projects.js +104 -2
  7. package/lib/api/controller-health.api.js +49 -0
  8. package/lib/api/dimension-values.api.js +82 -0
  9. package/lib/api/dimensions.api.js +114 -0
  10. package/lib/api/external-systems.api.js +1 -0
  11. package/lib/api/integration-clients.api.js +168 -0
  12. package/lib/api/types/dimension-values.types.js +28 -0
  13. package/lib/api/types/dimensions.types.js +31 -0
  14. package/lib/api/types/integration-clients.types.js +45 -0
  15. package/lib/api/validation-runner.js +46 -25
  16. package/lib/app/deploy-config.js +11 -1
  17. package/lib/app/deploy-status-display.js +3 -3
  18. package/lib/app/deploy.js +36 -14
  19. package/lib/app/display.js +15 -11
  20. package/lib/app/helpers.js +3 -3
  21. package/lib/app/index.js +3 -3
  22. package/lib/app/push.js +46 -23
  23. package/lib/app/register.js +7 -6
  24. package/lib/app/restart-display.js +126 -0
  25. package/lib/app/rotate-secret.js +7 -6
  26. package/lib/app/run-container-start.js +12 -6
  27. package/lib/app/run-env-compose.js +30 -1
  28. package/lib/app/run-helpers.js +58 -19
  29. package/lib/app/run-reload-sync.js +148 -0
  30. package/lib/app/run-resolve-image.js +51 -1
  31. package/lib/app/run.js +148 -74
  32. package/lib/app/show-display.js +7 -0
  33. package/lib/app/show.js +87 -5
  34. package/lib/build/index.js +83 -49
  35. package/lib/cli/doctor-check.js +117 -0
  36. package/lib/cli/index.js +8 -2
  37. package/lib/cli/infra-guided.js +460 -0
  38. package/lib/cli/installation-log-command.js +73 -0
  39. package/lib/cli/setup-app.js +31 -3
  40. package/lib/cli/setup-auth.js +98 -27
  41. package/lib/cli/setup-dev-path-commands.js +50 -3
  42. package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
  43. package/lib/cli/setup-infra-up-platform-action.js +131 -0
  44. package/lib/cli/setup-infra.js +132 -118
  45. package/lib/cli/setup-integration-client.js +182 -0
  46. package/lib/cli/setup-parameters.js +21 -2
  47. package/lib/cli/setup-platform.js +102 -0
  48. package/lib/cli/setup-secrets.js +18 -6
  49. package/lib/cli/setup-utility-resolve.js +132 -0
  50. package/lib/cli/setup-utility.js +143 -84
  51. package/lib/commands/app-logs.js +81 -33
  52. package/lib/commands/auth-config.js +116 -18
  53. package/lib/commands/datasource-capability-dimension-cli.js +128 -0
  54. package/lib/commands/datasource-capability-output.js +29 -0
  55. package/lib/commands/datasource-capability-relate-cli.js +140 -0
  56. package/lib/commands/datasource-capability.js +411 -0
  57. package/lib/commands/datasource-unified-test-cli.options.js +1 -1
  58. package/lib/commands/datasource.js +53 -13
  59. package/lib/commands/dev-down.js +3 -3
  60. package/lib/commands/dev-infra-gate.js +32 -0
  61. package/lib/commands/dev-init.js +13 -7
  62. package/lib/commands/dimension-value.js +179 -0
  63. package/lib/commands/dimension.js +330 -0
  64. package/lib/commands/integration-client.js +430 -0
  65. package/lib/commands/login-device.js +65 -30
  66. package/lib/commands/login.js +21 -10
  67. package/lib/commands/parameters-validate.js +78 -13
  68. package/lib/commands/repair-datasource-auto-rbac.js +166 -0
  69. package/lib/commands/repair-datasource-keys.js +10 -5
  70. package/lib/commands/repair-datasource.js +19 -7
  71. package/lib/commands/repair-env-template.js +4 -1
  72. package/lib/commands/repair-openapi-sync.js +172 -0
  73. package/lib/commands/repair-persist.js +102 -0
  74. package/lib/commands/repair-rbac-extract.js +27 -0
  75. package/lib/commands/repair-rbac-migrate.js +186 -0
  76. package/lib/commands/repair-rbac.js +214 -31
  77. package/lib/commands/repair-system-alignment.js +246 -0
  78. package/lib/commands/repair-system-permissions.js +168 -0
  79. package/lib/commands/repair.js +120 -338
  80. package/lib/commands/secure.js +1 -1
  81. package/lib/commands/setup-modes.js +468 -0
  82. package/lib/commands/setup-prompts.js +421 -0
  83. package/lib/commands/setup.js +254 -0
  84. package/lib/commands/teardown.js +277 -0
  85. package/lib/commands/up-common.js +113 -19
  86. package/lib/commands/up-dataplane.js +44 -19
  87. package/lib/commands/up-miso.js +18 -18
  88. package/lib/commands/upload.js +111 -23
  89. package/lib/commands/wizard-core-helpers.js +14 -11
  90. package/lib/commands/wizard-core.js +6 -5
  91. package/lib/commands/wizard-dataplane.js +2 -2
  92. package/lib/commands/wizard-entity-selection.js +4 -3
  93. package/lib/commands/wizard-headless.js +2 -1
  94. package/lib/commands/wizard.js +2 -1
  95. package/lib/constants/infra-compose-service-names.js +40 -0
  96. package/lib/core/audit-logger.js +1 -34
  97. package/lib/core/config-admin-email.js +56 -0
  98. package/lib/core/config-normalize.js +60 -0
  99. package/lib/core/config-registered-controller-urls.js +54 -0
  100. package/lib/core/config.js +33 -50
  101. package/lib/core/env-reader.js +16 -3
  102. package/lib/core/secrets-admin-env.js +101 -0
  103. package/lib/core/secrets-ensure-infra.js +34 -1
  104. package/lib/core/secrets-ensure.js +88 -66
  105. package/lib/core/secrets-env-content.js +428 -0
  106. package/lib/core/secrets-env-declarative-expand.js +170 -0
  107. package/lib/core/secrets-env-write.js +29 -1
  108. package/lib/core/secrets-load.js +252 -0
  109. package/lib/core/secrets-names.js +32 -0
  110. package/lib/core/secrets.js +17 -757
  111. package/lib/datasource/capability/basic-exposure.js +76 -0
  112. package/lib/datasource/capability/capability-diff-slice.js +41 -0
  113. package/lib/datasource/capability/capability-key.js +34 -0
  114. package/lib/datasource/capability/capability-resolve.js +172 -0
  115. package/lib/datasource/capability/capability-storage-keys.js +22 -0
  116. package/lib/datasource/capability/copy-operations.js +348 -0
  117. package/lib/datasource/capability/copy-test-payload.js +139 -0
  118. package/lib/datasource/capability/create-operations.js +235 -0
  119. package/lib/datasource/capability/dimension-operations.js +151 -0
  120. package/lib/datasource/capability/dimension-validate.js +219 -0
  121. package/lib/datasource/capability/json-pointer.js +31 -0
  122. package/lib/datasource/capability/reference-rewrite.js +51 -0
  123. package/lib/datasource/capability/relate-operations.js +325 -0
  124. package/lib/datasource/capability/relate-validate.js +219 -0
  125. package/lib/datasource/capability/remove-operations.js +275 -0
  126. package/lib/datasource/capability/run-capability-copy.js +152 -0
  127. package/lib/datasource/capability/run-capability-diff.js +135 -0
  128. package/lib/datasource/capability/run-capability-dimension.js +291 -0
  129. package/lib/datasource/capability/run-capability-edit.js +377 -0
  130. package/lib/datasource/capability/run-capability-relate.js +193 -0
  131. package/lib/datasource/capability/run-capability-remove.js +105 -0
  132. package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
  133. package/lib/datasource/capability/validate-capability-slice.js +35 -0
  134. package/lib/datasource/list.js +136 -23
  135. package/lib/datasource/log-viewer.js +2 -4
  136. package/lib/datasource/unified-validation-run.js +51 -16
  137. package/lib/datasource/validate.js +53 -1
  138. package/lib/deployment/deploy-poll-ui.js +60 -0
  139. package/lib/deployment/deployer-status.js +29 -3
  140. package/lib/deployment/deployer.js +48 -30
  141. package/lib/deployment/environment.js +7 -2
  142. package/lib/deployment/poll-interval.js +72 -0
  143. package/lib/deployment/push.js +11 -9
  144. package/lib/external-system/deploy.js +9 -2
  145. package/lib/external-system/download.js +61 -32
  146. package/lib/external-system/sync-deploy-manifest.js +33 -0
  147. package/lib/infrastructure/index.js +49 -19
  148. package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
  149. package/lib/internal/node-fs.js +2 -0
  150. package/lib/parameters/infra-kv-discovery.js +29 -4
  151. package/lib/parameters/infra-parameter-catalog.js +6 -3
  152. package/lib/parameters/infra-parameter-validate.js +67 -19
  153. package/lib/resolvers/datasource-resolver.js +53 -0
  154. package/lib/resolvers/dimension-file.js +52 -0
  155. package/lib/resolvers/manifest-resolver.js +133 -0
  156. package/lib/schema/application-schema.json +4 -0
  157. package/lib/schema/external-datasource.schema.json +183 -53
  158. package/lib/schema/external-system.schema.json +23 -10
  159. package/lib/schema/infra.parameter.yaml +26 -1
  160. package/lib/schema/wizard-config.schema.json +1 -1
  161. package/lib/utils/aifabrix-config-dir-walk.js +40 -0
  162. package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
  163. package/lib/utils/app-config-resolver.js +24 -1
  164. package/lib/utils/app-run-containers.js +2 -2
  165. package/lib/utils/applications-config-defaults.js +206 -0
  166. package/lib/utils/auth-config-validator.js +2 -12
  167. package/lib/utils/bash-secret-env.js +59 -0
  168. package/lib/utils/cli-secrets-error-format.js +78 -0
  169. package/lib/utils/cli-test-layout-chalk.js +31 -9
  170. package/lib/utils/cli-utils.js +4 -36
  171. package/lib/utils/compose-generate-docker-compose.js +111 -6
  172. package/lib/utils/compose-generator.js +17 -8
  173. package/lib/utils/controller-url.js +50 -7
  174. package/lib/utils/datasource-test-run-display.js +8 -0
  175. package/lib/utils/dev-hosts-helper.js +3 -2
  176. package/lib/utils/dev-init-ssh-merge.js +2 -1
  177. package/lib/utils/docker-build.js +17 -9
  178. package/lib/utils/docker-reload-mount.js +127 -0
  179. package/lib/utils/env-copy.js +99 -14
  180. package/lib/utils/env-template.js +5 -1
  181. package/lib/utils/external-readme.js +71 -2
  182. package/lib/utils/external-system-local-test-tty.js +3 -2
  183. package/lib/utils/external-system-readiness-core.js +45 -12
  184. package/lib/utils/external-system-readiness-deploy-display.js +3 -3
  185. package/lib/utils/external-system-readiness-display-internals.js +33 -3
  186. package/lib/utils/external-system-readiness-display.js +10 -1
  187. package/lib/utils/file-upload.js +40 -3
  188. package/lib/utils/health-check-db-init.js +107 -0
  189. package/lib/utils/health-check-public-warn.js +69 -0
  190. package/lib/utils/health-check-url.js +28 -10
  191. package/lib/utils/health-check.js +139 -107
  192. package/lib/utils/help-builder.js +5 -1
  193. package/lib/utils/image-name.js +34 -7
  194. package/lib/utils/infra-optional-service-flags.js +69 -0
  195. package/lib/utils/installation-log-core.js +282 -0
  196. package/lib/utils/installation-log-record.js +237 -0
  197. package/lib/utils/installation-log.js +123 -0
  198. package/lib/utils/integration-file-backup.js +74 -0
  199. package/lib/utils/log-redaction.js +105 -0
  200. package/lib/utils/manifest-location.js +164 -0
  201. package/lib/utils/manifest-source-emit.js +162 -0
  202. package/lib/utils/mutagen-install.js +30 -3
  203. package/lib/utils/paths.js +308 -76
  204. package/lib/utils/postgres-wipe.js +212 -0
  205. package/lib/utils/register-aifabrix-shell-env.js +15 -0
  206. package/lib/utils/remote-dev-auth.js +21 -5
  207. package/lib/utils/remote-docker-env.js +9 -1
  208. package/lib/utils/remote-secrets-loader.js +49 -4
  209. package/lib/utils/resolve-docker-image-ref.js +9 -3
  210. package/lib/utils/run-cli-flags.js +29 -0
  211. package/lib/utils/secrets-ancestor-paths.js +47 -0
  212. package/lib/utils/secrets-canonical.js +10 -3
  213. package/lib/utils/secrets-helpers.js +17 -10
  214. package/lib/utils/secrets-kv-refs.js +42 -0
  215. package/lib/utils/secrets-kv-scope.js +19 -2
  216. package/lib/utils/secrets-materialize-local.js +134 -0
  217. package/lib/utils/secrets-path.js +26 -13
  218. package/lib/utils/secrets-utils.js +20 -10
  219. package/lib/utils/system-builder-root.js +42 -0
  220. package/lib/utils/url-declarative-public-base.js +80 -12
  221. package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
  222. package/lib/utils/url-declarative-resolve-build.js +24 -388
  223. package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
  224. package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
  225. package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
  226. package/lib/utils/url-declarative-resolve.js +47 -7
  227. package/lib/utils/url-declarative-runtime-base-path.js +52 -0
  228. package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
  229. package/lib/utils/urls-local-registry-scan.js +103 -0
  230. package/lib/utils/urls-local-registry.js +158 -76
  231. package/lib/utils/validation-poll-ui.js +81 -0
  232. package/lib/utils/validation-run-poll.js +29 -5
  233. package/lib/utils/with-muted-logger.js +53 -0
  234. package/package.json +3 -1
  235. package/templates/applications/dataplane/application.yaml +5 -1
  236. package/templates/applications/dataplane/rbac.yaml +10 -10
  237. package/templates/applications/keycloak/env.template +8 -6
  238. package/templates/applications/miso-controller/application.yaml +9 -0
  239. package/templates/applications/miso-controller/env.template +27 -29
  240. package/templates/applications/miso-controller/rbac.yaml +9 -9
  241. package/templates/external-system/README.md.hbs +83 -123
  242. package/.npmrc.token +0 -1
  243. package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  244. package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  245. package/.nyc_output/processinfo/index.json +0 -1
  246. package/lib/api/service-users.api.js +0 -150
  247. package/lib/api/types/service-users.types.js +0 -65
  248. package/lib/cli/setup-service-user.js +0 -187
  249. package/lib/commands/service-user.js +0 -429
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Secrets loading: primary user file plus configured `aifabrix-secrets` (shared YAML or remote API).
3
+ *
4
+ * @fileoverview Default `kv://` resolution uses only `~/.aifabrix/secrets.local.yaml` and `aifabrix-secrets`
5
+ * from config — not cwd-ancestor `.aifabrix/secrets.local.yaml`, not `builder/secrets.local.yaml`,
6
+ * and not `~/.aifabrix/secrets.yaml`.
7
+ * @author AI Fabrix Team
8
+ * @version 1.0.0
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const config = require('./config');
16
+ const { readYamlAtPath, applyCanonicalSecretsOverride } = require('../utils/secrets-canonical');
17
+ const { ensureSecureFilePermissions } = require('../utils/secure-file-permissions');
18
+ const { resolveSecretsPath } = require('../utils/secrets-path');
19
+ const {
20
+ loadPrimaryUserSecrets,
21
+ loadDefaultSecrets,
22
+ ensurePrimaryUserSecretsFileExists
23
+ } = require('../utils/secrets-utils');
24
+ const pathsUtil = require('../utils/paths');
25
+ const { decryptSecret, isEncrypted } = require('../utils/secrets-encryption');
26
+ const logger = require('../utils/logger');
27
+ const { isSecretKeyAllowedEmpty } = require('./secrets-ensure-infra');
28
+
29
+ /** Dedupe optional decrypt warnings across repeated `loadSecrets` (and duplicate module instances). */
30
+ function optionalDecryptWarnSeen(key) {
31
+ const g = globalThis;
32
+ if (!g.__aifabrixOptionalDecryptWarnOnce) {
33
+ g.__aifabrixOptionalDecryptWarnOnce = new Set();
34
+ }
35
+ const set = g.__aifabrixOptionalDecryptWarnOnce;
36
+ if (set.has(key)) return true;
37
+ set.add(key);
38
+ return false;
39
+ }
40
+
41
+ /**
42
+ * @param {string} key
43
+ * @param {unknown} value
44
+ * @param {string} encryptionKey
45
+ * @param {Record<string, string>} decryptedSecrets
46
+ * @param {string} [sourceHint] - File path or API label for decrypt errors / warnings
47
+ */
48
+ function mergeDecryptedEntry(key, value, encryptionKey, decryptedSecrets, sourceHint) {
49
+ if (!isEncrypted(value)) {
50
+ decryptedSecrets[key] = value;
51
+ return;
52
+ }
53
+ try {
54
+ decryptedSecrets[key] = decryptSecret(value, encryptionKey);
55
+ } catch (error) {
56
+ const msg = error && error.message ? error.message : String(error);
57
+ if (!isSecretKeyAllowedEmpty(key)) {
58
+ const where =
59
+ sourceHint && String(sourceHint).trim().length > 0
60
+ ? ` (encrypted value loaded from: ${sourceHint})`
61
+ : ' (encrypted value source not recorded; check ~/.aifabrix/secrets.local.yaml and aifabrix-secrets)';
62
+ throw new Error(`Failed to decrypt secret '${key}'${where}: ${msg}`);
63
+ }
64
+ if (!optionalDecryptWarnSeen(key)) {
65
+ const where =
66
+ sourceHint && String(sourceHint).trim().length > 0
67
+ ? ` Encrypted value loaded from: ${sourceHint}.`
68
+ : '';
69
+ logger.warn(
70
+ `Optional secret '${key}' could not be decrypted (${msg}). Treating as empty.${where} ` +
71
+ 'Remove the stale key from shared or local secrets, or fix secrets-encryption alignment.'
72
+ );
73
+ }
74
+ decryptedSecrets[key] = '';
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Decrypts encrypted values in secrets object
80
+ *
81
+ * @async
82
+ * @param {Object} secrets - Secrets object with potentially encrypted values
83
+ * @param {{ keySources?: Record<string, string>, defaultSourceLabel?: string }} [options] - Per-key file/API path for errors
84
+ * @returns {Promise<Object>} Secrets object with decrypted values
85
+ */
86
+ async function decryptSecretsObject(secrets, options) {
87
+ if (!secrets || typeof secrets !== 'object') {
88
+ return secrets;
89
+ }
90
+
91
+ const encryptionKey = await config.getSecretsEncryptionKey();
92
+ if (!encryptionKey) {
93
+ const hasEncrypted = Object.values(secrets).some((value) => isEncrypted(value));
94
+ if (hasEncrypted) {
95
+ throw new Error(
96
+ 'Encrypted secrets found but no encryption key configured. Run "aifabrix secure --secrets-encryption <key>" to set encryption key.'
97
+ );
98
+ }
99
+ return secrets;
100
+ }
101
+
102
+ const keySources = options && options.keySources ? options.keySources : null;
103
+ const defaultLabel = options && options.defaultSourceLabel ? options.defaultSourceLabel : '';
104
+
105
+ const decryptedSecrets = {};
106
+ for (const [key, value] of Object.entries(secrets)) {
107
+ const sourceHint = (keySources && keySources[key]) || defaultLabel || '';
108
+ mergeDecryptedEntry(key, value, encryptionKey, decryptedSecrets, sourceHint);
109
+ }
110
+
111
+ return decryptedSecrets;
112
+ }
113
+
114
+ /**
115
+ * Merges config file secrets into user secrets (user wins). Returns null if path missing or config empty.
116
+ * @param {Record<string, string>} [keySources] - Mutated: path for keys filled from this file
117
+ */
118
+ function mergeUserWithConfigFile(userSecrets, resolvedConfigPath, keySources) {
119
+ if (!fs.existsSync(resolvedConfigPath)) {
120
+ return null;
121
+ }
122
+ ensureSecureFilePermissions(resolvedConfigPath);
123
+ let configSecrets;
124
+ try {
125
+ configSecrets = readYamlAtPath(resolvedConfigPath);
126
+ } catch (loadError) {
127
+ throw new Error(`Failed to load secrets file ${resolvedConfigPath}: ${loadError.message}`);
128
+ }
129
+ if (!configSecrets || typeof configSecrets !== 'object') {
130
+ return null;
131
+ }
132
+ const merged = { ...userSecrets };
133
+ for (const key of Object.keys(configSecrets)) {
134
+ if (!(key in merged) || merged[key] === undefined || merged[key] === null || merged[key] === '') {
135
+ merged[key] = configSecrets[key];
136
+ if (keySources) {
137
+ keySources[key] = resolvedConfigPath;
138
+ }
139
+ }
140
+ }
141
+ return merged;
142
+ }
143
+
144
+ function createMergeHelpers(userSecrets) {
145
+ const hasKeys = (obj) => obj && Object.keys(obj).length > 0;
146
+ return {
147
+ hasKeys,
148
+ userOrNull: () => (hasKeys(userSecrets) ? userSecrets : null)
149
+ };
150
+ }
151
+
152
+ async function mergeFromConfiguredSecretsPath(configSecretsPath, userSecrets, helpers, keySources) {
153
+ const remoteDevAuth = require('../utils/remote-dev-auth');
154
+ const effectiveShared = await remoteDevAuth.resolveSharedSecretsEndpoint(configSecretsPath);
155
+
156
+ if (remoteDevAuth.isRemoteSecretsUrl(effectiveShared)) {
157
+ const { loadRemoteSharedSecrets, mergeUserWithRemoteSecrets } = require('../utils/remote-secrets-loader');
158
+ const remoteSecrets = await loadRemoteSharedSecrets();
159
+ const remoteLabel = `shared secrets API (${effectiveShared})`;
160
+ const merged = mergeUserWithRemoteSecrets(userSecrets, remoteSecrets, keySources, remoteLabel);
161
+ return helpers.hasKeys(merged) ? merged : helpers.userOrNull();
162
+ }
163
+
164
+ const resolvedConfigPath = path.isAbsolute(configSecretsPath)
165
+ ? configSecretsPath
166
+ : path.resolve(process.cwd(), configSecretsPath);
167
+ const merged = mergeUserWithConfigFile(userSecrets, resolvedConfigPath, keySources);
168
+ return merged !== null ? merged : helpers.userOrNull();
169
+ }
170
+
171
+ async function loadMergedConfigAndUserSecrets() {
172
+ const userSecrets = loadPrimaryUserSecrets();
173
+ const helpers = createMergeHelpers(userSecrets);
174
+ const userPath = pathsUtil.getPrimaryUserSecretsLocalPath();
175
+ /** @type {Record<string, string>} */
176
+ const keySources = {};
177
+ for (const k of Object.keys(userSecrets || {})) {
178
+ keySources[k] = userPath;
179
+ }
180
+
181
+ try {
182
+ const configSecretsPath = await config.getSecretsPath();
183
+ if (!configSecretsPath) {
184
+ return { merged: helpers.userOrNull(), keySources };
185
+ }
186
+ const merged = await mergeFromConfiguredSecretsPath(configSecretsPath, userSecrets, helpers, keySources);
187
+ return { merged, keySources };
188
+ } catch (error) {
189
+ if (error.message && error.message.startsWith('Failed to load secrets file')) {
190
+ throw error;
191
+ }
192
+ return { merged: null, keySources };
193
+ }
194
+ }
195
+
196
+ async function loadSecretsWithFallbacks() {
197
+ const mergedResult = await loadMergedConfigAndUserSecrets();
198
+ let merged = mergedResult.merged;
199
+ const keySources = mergedResult.keySources;
200
+ if (!merged || Object.keys(merged).length === 0) {
201
+ merged = loadPrimaryUserSecrets();
202
+ const userPath = pathsUtil.getPrimaryUserSecretsLocalPath();
203
+ for (const k of Object.keys(merged || {})) {
204
+ keySources[k] = userPath;
205
+ }
206
+ merged = await applyCanonicalSecretsOverride(merged || {}, keySources);
207
+ }
208
+ const configuredShared = await config.getSecretsPath();
209
+ if ((!merged || Object.keys(merged).length === 0) && !configuredShared) {
210
+ merged = loadDefaultSecrets();
211
+ const defaultPath = path.join(pathsUtil.getAifabrixHome(), 'secrets.yaml');
212
+ for (const k of Object.keys(merged || {})) {
213
+ keySources[k] = defaultPath;
214
+ }
215
+ }
216
+ if (!merged || Object.keys(merged).length === 0) {
217
+ return { merged: {}, keySources };
218
+ }
219
+ return { merged, keySources };
220
+ }
221
+
222
+ /**
223
+ * @param {string} [secretsPath]
224
+ * @param {string} [_appName]
225
+ */
226
+ async function loadSecrets(secretsPath, _appName) {
227
+ if (secretsPath) {
228
+ const resolvedPath = resolveSecretsPath(secretsPath);
229
+ if (!fs.existsSync(resolvedPath)) {
230
+ throw new Error(`Secrets file not found: ${resolvedPath}`);
231
+ }
232
+ ensureSecureFilePermissions(resolvedPath);
233
+ const explicitSecrets = readYamlAtPath(resolvedPath);
234
+ if (!explicitSecrets || typeof explicitSecrets !== 'object') {
235
+ throw new Error(`Invalid secrets file format: ${resolvedPath}`);
236
+ }
237
+ return await decryptSecretsObject(explicitSecrets, { defaultSourceLabel: resolvedPath });
238
+ }
239
+ let { merged: mergedSecrets, keySources } = await loadSecretsWithFallbacks();
240
+ if (!mergedSecrets || Object.keys(mergedSecrets).length === 0) {
241
+ ensurePrimaryUserSecretsFileExists();
242
+ const again = await loadSecretsWithFallbacks();
243
+ mergedSecrets = again.merged;
244
+ keySources = again.keySources;
245
+ }
246
+ return await decryptSecretsObject(mergedSecrets || {}, { keySources });
247
+ }
248
+
249
+ module.exports = {
250
+ loadSecrets,
251
+ decryptSecretsObject
252
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Canonical secret name normalization for env keys.
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
+ /**
12
+ * Generates a canonical secret name from an environment variable key.
13
+ * Converts to lowercase, replaces non-alphanumeric characters with hyphens,
14
+ * collapses consecutive hyphens, and trims leading/trailing hyphens.
15
+ *
16
+ * @param {string} key - Environment variable key (e.g., JWT_SECRET)
17
+ * @returns {string} Canonical secret name (e.g., jwt-secret)
18
+ */
19
+ function getCanonicalSecretName(key) {
20
+ if (!key || typeof key !== 'string') {
21
+ return '';
22
+ }
23
+ const withHyphens = key.replace(/([a-z0-9])([A-Z])/g, '$1-$2');
24
+ const lower = withHyphens.toLowerCase();
25
+ const hyphenated = lower.replace(/[^a-z0-9]/g, '-');
26
+ const collapsed = hyphenated.replace(/-+/g, '-');
27
+ return collapsed.replace(/^-+|-+$/g, '');
28
+ }
29
+
30
+ module.exports = {
31
+ getCanonicalSecretName
32
+ };