@aifabrix/builder 2.44.5 → 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.
Files changed (207) hide show
  1. package/.cursor/rules/cli-layout.mdc +1 -1
  2. package/.cursor/rules/project-rules.mdc +1 -1
  3. package/.npmrc.token +1 -1
  4. package/README.md +15 -23
  5. package/integration/hubspot-test/README.md +2 -0
  6. package/integration/hubspot-test/test.js +5 -3
  7. package/jest.projects.js +48 -2
  8. package/lib/api/controller-health.api.js +49 -0
  9. package/lib/api/dimension-values.api.js +82 -0
  10. package/lib/api/dimensions.api.js +114 -0
  11. package/lib/api/external-systems.api.js +1 -0
  12. package/lib/api/integration-clients.api.js +168 -0
  13. package/lib/api/types/dimension-values.types.js +28 -0
  14. package/lib/api/types/dimensions.types.js +31 -0
  15. package/lib/api/types/integration-clients.types.js +45 -0
  16. package/lib/api/validation-runner.js +46 -25
  17. package/lib/app/deploy-config.js +11 -1
  18. package/lib/app/deploy-status-display.js +3 -3
  19. package/lib/app/deploy.js +36 -14
  20. package/lib/app/display.js +15 -11
  21. package/lib/app/push.js +46 -23
  22. package/lib/app/register.js +1 -1
  23. package/lib/app/restart-display.js +95 -0
  24. package/lib/app/rotate-secret.js +1 -1
  25. package/lib/app/run-container-start.js +12 -6
  26. package/lib/app/run-env-compose.js +30 -1
  27. package/lib/app/run-helpers.js +44 -12
  28. package/lib/app/run-reload-sync.js +148 -0
  29. package/lib/app/run-resolve-image.js +51 -1
  30. package/lib/app/run.js +99 -73
  31. package/lib/build/index.js +75 -45
  32. package/lib/cli/doctor-check.js +117 -0
  33. package/lib/cli/index.js +8 -2
  34. package/lib/cli/infra-guided.js +445 -0
  35. package/lib/cli/setup-app.js +20 -2
  36. package/lib/cli/setup-auth.js +26 -0
  37. package/lib/cli/setup-dev-path-commands.js +50 -3
  38. package/lib/cli/setup-infra.js +134 -61
  39. package/lib/cli/setup-integration-client.js +182 -0
  40. package/lib/cli/setup-parameters.js +21 -2
  41. package/lib/cli/setup-platform.js +102 -0
  42. package/lib/cli/setup-secrets.js +18 -6
  43. package/lib/cli/setup-utility.js +78 -33
  44. package/lib/commands/datasource-capability-dimension-cli.js +128 -0
  45. package/lib/commands/datasource-capability-output.js +29 -0
  46. package/lib/commands/datasource-capability-relate-cli.js +140 -0
  47. package/lib/commands/datasource-capability.js +411 -0
  48. package/lib/commands/datasource-unified-test-cli.options.js +1 -1
  49. package/lib/commands/datasource.js +53 -13
  50. package/lib/commands/dev-down.js +3 -3
  51. package/lib/commands/dev-infra-gate.js +32 -0
  52. package/lib/commands/dev-init.js +13 -7
  53. package/lib/commands/dimension-value.js +179 -0
  54. package/lib/commands/dimension.js +330 -0
  55. package/lib/commands/integration-client.js +430 -0
  56. package/lib/commands/login-device.js +65 -30
  57. package/lib/commands/login.js +21 -10
  58. package/lib/commands/parameters-validate.js +78 -13
  59. package/lib/commands/repair-datasource-auto-rbac.js +166 -0
  60. package/lib/commands/repair-datasource-keys.js +10 -5
  61. package/lib/commands/repair-datasource.js +19 -7
  62. package/lib/commands/repair-env-template.js +4 -1
  63. package/lib/commands/repair-openapi-sync.js +172 -0
  64. package/lib/commands/repair-persist.js +102 -0
  65. package/lib/commands/repair-rbac-extract.js +27 -0
  66. package/lib/commands/repair-rbac-migrate.js +186 -0
  67. package/lib/commands/repair-rbac.js +214 -31
  68. package/lib/commands/repair-system-alignment.js +246 -0
  69. package/lib/commands/repair-system-permissions.js +168 -0
  70. package/lib/commands/repair.js +120 -338
  71. package/lib/commands/secure.js +1 -1
  72. package/lib/commands/setup-modes.js +455 -0
  73. package/lib/commands/setup-prompts.js +388 -0
  74. package/lib/commands/setup.js +149 -0
  75. package/lib/commands/teardown.js +228 -0
  76. package/lib/commands/up-common.js +79 -19
  77. package/lib/commands/up-dataplane.js +33 -11
  78. package/lib/commands/up-miso.js +7 -11
  79. package/lib/commands/upload.js +109 -23
  80. package/lib/commands/wizard-core-helpers.js +14 -11
  81. package/lib/commands/wizard-core.js +6 -5
  82. package/lib/commands/wizard-dataplane.js +2 -2
  83. package/lib/commands/wizard-entity-selection.js +4 -3
  84. package/lib/commands/wizard-headless.js +2 -1
  85. package/lib/commands/wizard.js +2 -1
  86. package/lib/constants/infra-compose-service-names.js +40 -0
  87. package/lib/core/env-reader.js +16 -3
  88. package/lib/core/secrets-admin-env.js +101 -0
  89. package/lib/core/secrets-ensure-infra.js +34 -1
  90. package/lib/core/secrets-ensure.js +88 -66
  91. package/lib/core/secrets-env-content.js +432 -0
  92. package/lib/core/secrets-env-write.js +27 -1
  93. package/lib/core/secrets-load.js +248 -0
  94. package/lib/core/secrets-names.js +32 -0
  95. package/lib/core/secrets.js +17 -757
  96. package/lib/datasource/capability/basic-exposure.js +76 -0
  97. package/lib/datasource/capability/capability-diff-slice.js +41 -0
  98. package/lib/datasource/capability/capability-key.js +34 -0
  99. package/lib/datasource/capability/capability-resolve.js +172 -0
  100. package/lib/datasource/capability/capability-storage-keys.js +22 -0
  101. package/lib/datasource/capability/copy-operations.js +348 -0
  102. package/lib/datasource/capability/copy-test-payload.js +139 -0
  103. package/lib/datasource/capability/create-operations.js +235 -0
  104. package/lib/datasource/capability/dimension-operations.js +151 -0
  105. package/lib/datasource/capability/dimension-validate.js +219 -0
  106. package/lib/datasource/capability/json-pointer.js +31 -0
  107. package/lib/datasource/capability/reference-rewrite.js +51 -0
  108. package/lib/datasource/capability/relate-operations.js +325 -0
  109. package/lib/datasource/capability/relate-validate.js +219 -0
  110. package/lib/datasource/capability/remove-operations.js +275 -0
  111. package/lib/datasource/capability/run-capability-copy.js +152 -0
  112. package/lib/datasource/capability/run-capability-diff.js +135 -0
  113. package/lib/datasource/capability/run-capability-dimension.js +291 -0
  114. package/lib/datasource/capability/run-capability-edit.js +377 -0
  115. package/lib/datasource/capability/run-capability-relate.js +193 -0
  116. package/lib/datasource/capability/run-capability-remove.js +105 -0
  117. package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
  118. package/lib/datasource/capability/validate-capability-slice.js +35 -0
  119. package/lib/datasource/list.js +136 -23
  120. package/lib/datasource/log-viewer.js +2 -4
  121. package/lib/datasource/unified-validation-run.js +51 -16
  122. package/lib/datasource/validate.js +53 -1
  123. package/lib/deployment/deploy-poll-ui.js +60 -0
  124. package/lib/deployment/deployer-status.js +29 -3
  125. package/lib/deployment/deployer.js +48 -30
  126. package/lib/deployment/environment.js +7 -2
  127. package/lib/deployment/poll-interval.js +72 -0
  128. package/lib/deployment/push.js +11 -9
  129. package/lib/external-system/deploy.js +4 -1
  130. package/lib/external-system/download.js +61 -32
  131. package/lib/external-system/sync-deploy-manifest.js +33 -0
  132. package/lib/infrastructure/index.js +49 -19
  133. package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
  134. package/lib/parameters/infra-kv-discovery.js +29 -4
  135. package/lib/parameters/infra-parameter-catalog.js +6 -3
  136. package/lib/parameters/infra-parameter-validate.js +67 -19
  137. package/lib/resolvers/datasource-resolver.js +53 -0
  138. package/lib/resolvers/dimension-file.js +52 -0
  139. package/lib/resolvers/manifest-resolver.js +133 -0
  140. package/lib/schema/external-datasource.schema.json +183 -53
  141. package/lib/schema/external-system.schema.json +23 -10
  142. package/lib/schema/infra.parameter.yaml +26 -11
  143. package/lib/schema/wizard-config.schema.json +1 -1
  144. package/lib/utils/aifabrix-config-dir-walk.js +40 -0
  145. package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
  146. package/lib/utils/app-run-containers.js +2 -2
  147. package/lib/utils/bash-secret-env.js +59 -0
  148. package/lib/utils/cli-secrets-error-format.js +78 -0
  149. package/lib/utils/cli-test-layout-chalk.js +31 -9
  150. package/lib/utils/cli-utils.js +4 -36
  151. package/lib/utils/datasource-test-run-display.js +8 -0
  152. package/lib/utils/dev-hosts-helper.js +3 -2
  153. package/lib/utils/dev-init-ssh-merge.js +2 -1
  154. package/lib/utils/docker-build.js +17 -9
  155. package/lib/utils/docker-reload-mount.js +127 -0
  156. package/lib/utils/external-readme.js +71 -2
  157. package/lib/utils/external-system-local-test-tty.js +3 -2
  158. package/lib/utils/external-system-readiness-core.js +45 -12
  159. package/lib/utils/external-system-readiness-deploy-display.js +3 -3
  160. package/lib/utils/external-system-readiness-display-internals.js +33 -3
  161. package/lib/utils/external-system-readiness-display.js +10 -1
  162. package/lib/utils/file-upload.js +40 -3
  163. package/lib/utils/health-check-db-init.js +107 -0
  164. package/lib/utils/health-check-public-warn.js +69 -0
  165. package/lib/utils/health-check-url.js +19 -4
  166. package/lib/utils/health-check.js +135 -105
  167. package/lib/utils/help-builder.js +5 -1
  168. package/lib/utils/image-name.js +34 -7
  169. package/lib/utils/integration-file-backup.js +74 -0
  170. package/lib/utils/mutagen-install.js +30 -3
  171. package/lib/utils/paths.js +108 -25
  172. package/lib/utils/postgres-wipe.js +212 -0
  173. package/lib/utils/register-aifabrix-shell-env.js +15 -0
  174. package/lib/utils/remote-dev-auth.js +21 -5
  175. package/lib/utils/remote-docker-env.js +9 -1
  176. package/lib/utils/remote-secrets-loader.js +42 -3
  177. package/lib/utils/resolve-docker-image-ref.js +9 -3
  178. package/lib/utils/secrets-ancestor-paths.js +47 -0
  179. package/lib/utils/secrets-helpers.js +17 -10
  180. package/lib/utils/secrets-kv-refs.js +42 -0
  181. package/lib/utils/secrets-kv-scope.js +19 -2
  182. package/lib/utils/secrets-materialize-local.js +134 -0
  183. package/lib/utils/secrets-path.js +24 -10
  184. package/lib/utils/secrets-utils.js +2 -2
  185. package/lib/utils/system-builder-root.js +34 -0
  186. package/lib/utils/url-declarative-resolve-build.js +6 -1
  187. package/lib/utils/url-declarative-runtime-base-path.js +32 -0
  188. package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
  189. package/lib/utils/urls-local-registry.js +23 -12
  190. package/lib/utils/validation-poll-ui.js +81 -0
  191. package/lib/utils/validation-run-poll.js +29 -5
  192. package/lib/utils/with-muted-logger.js +53 -0
  193. package/package.json +1 -1
  194. package/templates/applications/dataplane/application.yaml +1 -1
  195. package/templates/applications/dataplane/rbac.yaml +10 -10
  196. package/templates/applications/keycloak/env.template +8 -6
  197. package/templates/applications/miso-controller/application.yaml +7 -0
  198. package/templates/applications/miso-controller/env.template +1 -1
  199. package/templates/applications/miso-controller/rbac.yaml +9 -9
  200. package/templates/external-system/README.md.hbs +83 -123
  201. package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  202. package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  203. package/.nyc_output/processinfo/index.json +0 -1
  204. package/lib/api/service-users.api.js +0 -150
  205. package/lib/api/types/service-users.types.js +0 -65
  206. package/lib/cli/setup-service-user.js +0 -187
  207. package/lib/commands/service-user.js +0 -429
@@ -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
  };
@@ -1,10 +1,11 @@
1
1
  /**
2
- * AI Fabrix Builder – Ensure secrets in configured store
2
+ * AI Fabrix Builder – Ensure secrets in the primary user store
3
3
  *
4
- * Ensures missing secret keys exist in the correct store (file path, remote API, or
5
- * user secrets file). New values are encrypted when writing to file and
6
- * secrets-encryption is set. Remote write tries API first; on failure falls back
7
- * to user file with a warning.
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 write target from config.
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 (fallback: user file)
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
- * Add secrets via remote API; on failure write to local file (with encryption if configured).
170
- * @param {Object} target - Resolved target (remote)
171
- * @param {string[]} toAdd - Keys to add
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
- async function addSecretsRemote(target, toAdd, suggested, added, encryptionKey, placeholderContext) {
178
- const emptyForCredentials = false;
179
- for (const key of toAdd) {
180
- const value = valueForKey(key, suggested, emptyForCredentials, placeholderContext);
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
- return added;
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
- if (added.length > 0) {
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 resolveWriteTarget();
257
- const existing = await loadExistingFromTarget(target);
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 resolveWriteTarget();
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
- if (target.type === 'remote' && target.serverUrl && target.clientCertPem) {
364
- try {
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 in the configured store. Call before ensureAdminSecrets or startInfra.
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 added = await ensureSecretsForKeys(keys, { placeholderContext });
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
- setSecretInStore,
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,