@aifabrix/builder 2.44.6 → 2.45.5

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 (257) hide show
  1. package/.cursor/rules/cli-layout.mdc +7 -3
  2. package/.cursor/rules/docs-rules.mdc +3 -1
  3. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  4. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  5. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
  6. package/jest.projects.js +100 -1
  7. package/lib/api/agent-metadata-validation.api.js +93 -0
  8. package/lib/api/audit-api-helpers.js +68 -0
  9. package/lib/api/audit.api.js +119 -0
  10. package/lib/api/auth-cache.api.js +44 -0
  11. package/lib/api/certificates.api.js +4 -4
  12. package/lib/api/credential.api.js +2 -2
  13. package/lib/api/dataplane-health.api.js +137 -0
  14. package/lib/api/dataplane-sync.api.js +27 -0
  15. package/lib/api/datasources-core.api.js +21 -18
  16. package/lib/api/datasources-extended.api.js +22 -22
  17. package/lib/api/dimensions.api.js +3 -0
  18. package/lib/api/external-systems.api.js +13 -13
  19. package/lib/api/governance-scenarios.api.js +39 -0
  20. package/lib/api/groups.api.js +79 -0
  21. package/lib/api/index.js +45 -2
  22. package/lib/api/pipeline.api.js +4 -4
  23. package/lib/api/protection.api.js +157 -0
  24. package/lib/api/records-bulk.api.js +30 -0
  25. package/lib/api/records-search-parse.js +49 -0
  26. package/lib/api/records-search.api.js +29 -0
  27. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  28. package/lib/api/types/audit.types.js +72 -0
  29. package/lib/api/types/dataplane-health.types.js +27 -0
  30. package/lib/api/types/dimensions.types.js +2 -0
  31. package/lib/api/types/governance-scenarios.types.js +39 -0
  32. package/lib/api/types/protection.types.js +69 -0
  33. package/lib/api/types/records-bulk.types.js +35 -0
  34. package/lib/api/types/records-search.types.js +28 -0
  35. package/lib/api/user-groups.api.js +50 -0
  36. package/lib/api/users.api.js +95 -0
  37. package/lib/api/validation-run.api.js +3 -3
  38. package/lib/api/validation-runner.js +3 -3
  39. package/lib/api/wizard-platform.api.js +3 -3
  40. package/lib/api/wizard.api.js +21 -21
  41. package/lib/app/deploy.js +34 -19
  42. package/lib/app/helpers.js +3 -3
  43. package/lib/app/index.js +3 -3
  44. package/lib/app/register.js +7 -6
  45. package/lib/app/restart-display.js +52 -21
  46. package/lib/app/rotate-secret.js +7 -6
  47. package/lib/app/run-helpers.js +15 -8
  48. package/lib/app/run.js +57 -9
  49. package/lib/app/show-display.js +7 -0
  50. package/lib/app/show.js +87 -5
  51. package/lib/build/index.js +9 -5
  52. package/lib/cli/index.js +4 -2
  53. package/lib/cli/infra-guided.js +62 -28
  54. package/lib/cli/installation-log-command.js +73 -0
  55. package/lib/cli/setup-app.help.js +50 -2
  56. package/lib/cli/setup-app.js +44 -25
  57. package/lib/cli/setup-app.test-commands.js +80 -21
  58. package/lib/cli/setup-auth.js +124 -52
  59. package/lib/cli/setup-external-system.js +37 -3
  60. package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
  61. package/lib/cli/setup-infra-up-platform-action.js +131 -0
  62. package/lib/cli/setup-infra.js +60 -119
  63. package/lib/cli/setup-platform.js +1 -1
  64. package/lib/cli/setup-utility-help-after.js +45 -0
  65. package/lib/cli/setup-utility-resolve.js +132 -0
  66. package/lib/cli/setup-utility.js +148 -116
  67. package/lib/commands/app-logs.js +81 -33
  68. package/lib/commands/auth-config.js +116 -18
  69. package/lib/commands/auth-status-dataplane-version.js +231 -0
  70. package/lib/commands/auth-status-display.js +94 -0
  71. package/lib/commands/auth-status.js +70 -6
  72. package/lib/commands/credential-push.js +10 -5
  73. package/lib/commands/datasource-load-export-cli.js +178 -0
  74. package/lib/commands/datasource-test-trust-cli.js +114 -0
  75. package/lib/commands/datasource-unified-test-cli.js +25 -17
  76. package/lib/commands/datasource-unified-test-cli.options.js +18 -3
  77. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  78. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  79. package/lib/commands/datasource.js +68 -1
  80. package/lib/commands/dimension-value.js +43 -13
  81. package/lib/commands/dimension.js +38 -7
  82. package/lib/commands/identity-apply-sync.js +131 -0
  83. package/lib/commands/identity-group-cli.js +146 -0
  84. package/lib/commands/identity-membership-role-cache.js +175 -0
  85. package/lib/commands/identity-shared.js +94 -0
  86. package/lib/commands/identity-user-cli.js +158 -0
  87. package/lib/commands/identity.js +43 -0
  88. package/lib/commands/login.js +46 -16
  89. package/lib/commands/protection-cli-leaves.js +286 -0
  90. package/lib/commands/protection.js +50 -0
  91. package/lib/commands/repair.js +13 -3
  92. package/lib/commands/setup-modes.js +19 -6
  93. package/lib/commands/setup-prompts.js +41 -8
  94. package/lib/commands/setup.js +114 -9
  95. package/lib/commands/teardown.js +54 -5
  96. package/lib/commands/test-e2e-external.js +40 -9
  97. package/lib/commands/test-governance-cli-options.js +47 -0
  98. package/lib/commands/test-governance-command-action.js +55 -0
  99. package/lib/commands/test-governance-external.js +88 -0
  100. package/lib/commands/test-trust-cli-options.js +32 -0
  101. package/lib/commands/test-trust-command-action.js +118 -0
  102. package/lib/commands/test-trust-external.js +127 -0
  103. package/lib/commands/up-common.js +48 -14
  104. package/lib/commands/up-dataplane.js +21 -18
  105. package/lib/commands/up-miso.js +12 -8
  106. package/lib/commands/upload.js +38 -15
  107. package/lib/commands/wizard-core.js +3 -0
  108. package/lib/core/audit-logger.js +1 -34
  109. package/lib/core/config-admin-email.js +56 -0
  110. package/lib/core/config-device-dataplane.js +185 -0
  111. package/lib/core/config-normalize.js +60 -0
  112. package/lib/core/config-registered-controller-urls.js +54 -0
  113. package/lib/core/config.js +33 -50
  114. package/lib/core/secrets-ensure-infra.js +1 -1
  115. package/lib/core/secrets-env-content.js +86 -90
  116. package/lib/core/secrets-env-declarative-expand.js +170 -0
  117. package/lib/core/secrets-env-write.js +2 -0
  118. package/lib/core/secrets-load.js +106 -102
  119. package/lib/datasource/agent-trust-debug-log.js +45 -0
  120. package/lib/datasource/agent-trust-fetch.js +115 -0
  121. package/lib/datasource/agent-trust-map.js +101 -0
  122. package/lib/datasource/agent-trust-mock.js +44 -0
  123. package/lib/datasource/agent-trust-run.js +93 -0
  124. package/lib/datasource/agent-trust-timeout.js +25 -0
  125. package/lib/datasource/audit-evidence-display.js +80 -0
  126. package/lib/datasource/audit-evidence-extract-debug.js +103 -0
  127. package/lib/datasource/audit-evidence-extract.js +177 -0
  128. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  129. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  130. package/lib/datasource/audit-evidence-poll.js +109 -0
  131. package/lib/datasource/audit-evidence-run.js +177 -0
  132. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  133. package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
  134. package/lib/datasource/audit-evidence-verify.js +108 -0
  135. package/lib/datasource/bulk-loader-run-batches.js +88 -0
  136. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  137. package/lib/datasource/bulk-loader-service.js +127 -0
  138. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  139. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  140. package/lib/datasource/datasource-exporter-service.js +144 -0
  141. package/lib/datasource/datasource-identifier.js +148 -0
  142. package/lib/datasource/datasource-load-export-context.js +92 -0
  143. package/lib/datasource/integration-context.js +6 -4
  144. package/lib/datasource/local-data-paths.js +145 -0
  145. package/lib/datasource/log-cleaner.js +241 -0
  146. package/lib/datasource/log-viewer.js +111 -6
  147. package/lib/datasource/record-file-parser.js +85 -0
  148. package/lib/datasource/record-mapper.js +129 -0
  149. package/lib/datasource/resolve-app.js +74 -55
  150. package/lib/datasource/test-e2e.js +26 -2
  151. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  152. package/lib/datasource/unified-validation-run.js +43 -17
  153. package/lib/datasource/validate.js +37 -20
  154. package/lib/external-system/deploy.js +5 -1
  155. package/lib/external-system/test-system-level.js +25 -4
  156. package/lib/external-system/test.js +3 -1
  157. package/lib/governance/governance-pack-loader.js +70 -0
  158. package/lib/governance/governance-run-display.js +119 -0
  159. package/lib/identity/identity-apply-core.js +108 -0
  160. package/lib/identity/identity-apply-phases.js +244 -0
  161. package/lib/identity/identity-apply-service.js +77 -0
  162. package/lib/identity/identity-csv-parser.js +114 -0
  163. package/lib/internal/fs-real-sync.js +29 -1
  164. package/lib/internal/node-fs.js +2 -0
  165. package/lib/protection/auth-context.js +45 -0
  166. package/lib/protection/convert-batch.js +90 -0
  167. package/lib/protection/load.js +41 -0
  168. package/lib/protection/paths.js +158 -0
  169. package/lib/protection/preflight-datasource-ready.js +46 -0
  170. package/lib/protection/protection-create-probe-lines.js +142 -0
  171. package/lib/protection/protection-create-probes.js +95 -0
  172. package/lib/protection/protection-create-scaffold.js +281 -0
  173. package/lib/protection/protection-display-helpers.js +445 -0
  174. package/lib/protection/protection-display.js +215 -0
  175. package/lib/protection/protection-preset-registry.js +97 -0
  176. package/lib/protection/report-exit.js +62 -0
  177. package/lib/protection/resolve.js +202 -0
  178. package/lib/protection/run-commands-validate.js +130 -0
  179. package/lib/protection/run-commands.js +216 -0
  180. package/lib/protection/run-protection-create-helpers.js +134 -0
  181. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  182. package/lib/protection/run-protection-create.js +191 -0
  183. package/lib/protection/scope.js +22 -0
  184. package/lib/protection/sync-after-upload.js +64 -0
  185. package/lib/protection/upload-batch.js +127 -0
  186. package/lib/protection/validate-batch.js +113 -0
  187. package/lib/protection/validate-local.js +65 -0
  188. package/lib/resolvers/dimension-file.js +29 -4
  189. package/lib/schema/application-schema.json +4 -0
  190. package/lib/schema/external-datasource.schema.json +47 -13
  191. package/lib/schema/flag-map-validation-run.json +2 -2
  192. package/lib/schema/infra.parameter.yaml +10 -0
  193. package/lib/schema/protection.schema.json +374 -0
  194. package/lib/schema/type/credential-field-registry.json +636 -0
  195. package/lib/schema/wizard-config.schema.json +1 -1
  196. package/lib/utils/agent-trust-run-display.js +231 -0
  197. package/lib/utils/agent-trust-run-exit.js +59 -0
  198. package/lib/utils/agent-trust-wait-ui.js +94 -0
  199. package/lib/utils/app-config-resolver.js +24 -1
  200. package/lib/utils/applications-config-defaults.js +206 -0
  201. package/lib/utils/auth-config-validator.js +2 -12
  202. package/lib/utils/bash-secret-env.js +1 -1
  203. package/lib/utils/cli-exit-codes.js +30 -0
  204. package/lib/utils/cli-sync-options.js +33 -0
  205. package/lib/utils/compose-generate-docker-compose.js +111 -6
  206. package/lib/utils/compose-generator.js +17 -8
  207. package/lib/utils/controller-url.js +50 -7
  208. package/lib/utils/credential-push-ui.js +90 -0
  209. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  210. package/lib/utils/dataplane-cli-version-help.js +74 -0
  211. package/lib/utils/datasource-export-display.js +137 -0
  212. package/lib/utils/datasource-load-display.js +174 -0
  213. package/lib/utils/datasource-load-export-ui.js +120 -0
  214. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  215. package/lib/utils/datasource-test-run-display.js +3 -3
  216. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  217. package/lib/utils/env-copy.js +99 -14
  218. package/lib/utils/env-template.js +5 -1
  219. package/lib/utils/health-check-url.js +18 -15
  220. package/lib/utils/health-check.js +7 -5
  221. package/lib/utils/help-builder.js +30 -24
  222. package/lib/utils/infra-optional-service-flags.js +69 -0
  223. package/lib/utils/installation-log-core.js +282 -0
  224. package/lib/utils/installation-log-record.js +237 -0
  225. package/lib/utils/installation-log.js +123 -0
  226. package/lib/utils/log-redaction.js +105 -0
  227. package/lib/utils/manifest-location.js +164 -0
  228. package/lib/utils/manifest-source-emit.js +184 -0
  229. package/lib/utils/paths.js +238 -89
  230. package/lib/utils/remote-secrets-loader.js +7 -1
  231. package/lib/utils/run-cli-flags.js +29 -0
  232. package/lib/utils/secrets-canonical.js +10 -3
  233. package/lib/utils/secrets-path.js +3 -4
  234. package/lib/utils/secrets-utils.js +20 -10
  235. package/lib/utils/semver-compare.js +127 -0
  236. package/lib/utils/system-builder-root.js +10 -2
  237. package/lib/utils/upload-pipeline-ui.js +91 -0
  238. package/lib/utils/url-declarative-public-base.js +80 -12
  239. package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
  240. package/lib/utils/url-declarative-resolve-build.js +24 -393
  241. package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
  242. package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
  243. package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
  244. package/lib/utils/url-declarative-resolve.js +47 -7
  245. package/lib/utils/url-declarative-runtime-base-path.js +21 -1
  246. package/lib/utils/urls-local-registry-scan.js +103 -0
  247. package/lib/utils/urls-local-registry.js +161 -90
  248. package/lib/utils/validation-poll-ui.js +14 -7
  249. package/lib/validation/wizard-config-validator.js +58 -19
  250. package/package.json +3 -1
  251. package/templates/applications/dataplane/application.yaml +4 -0
  252. package/templates/applications/dataplane/env.template +13 -1
  253. package/templates/applications/dataplane/rbac.yaml +4 -0
  254. package/templates/applications/miso-controller/application.yaml +2 -0
  255. package/templates/applications/miso-controller/env.template +27 -29
  256. package/templates/external-system/README.md.hbs +56 -8
  257. package/.npmrc.token +0 -1
@@ -11,6 +11,7 @@ const { formatSuccessLine } = require('./cli-test-layout-chalk');
11
11
 
12
12
  const fs = require('fs');
13
13
  const fsp = require('fs').promises;
14
+ const fsRealSync = require('../internal/fs-real-sync');
14
15
  const path = require('path');
15
16
  const yaml = require('js-yaml');
16
17
  const logger = require('./logger');
@@ -98,22 +99,37 @@ function resolveEnvOutputPath(rawOutputPath, variablesPath) {
98
99
  }
99
100
 
100
101
  /**
101
- * Writes .env to envOutputPath for reload path: merge run .env into existing file.
102
+ * Writes .env to envOutputPath for `--reload`: merge container `.env.run` into the host file.
103
+ * Preserves comments when a file already exists (e.g. after `aifabrix resolve`). Keys present only
104
+ * in `.env.run` (compose-only such as DB_0_NAME) are **not** appended unless they already exist as
105
+ * `KEY=` lines in the base file. If the output file is missing, seeds from `generateEnvContent`
106
+ * (`local`) so the host file keeps template comments and localhost-oriented URLs.
107
+ *
102
108
  * @async
103
109
  * @param {string} outputPath - Resolved output path
104
110
  * @param {string} runEnvPath - Path to .env.run
111
+ * @param {string} appName - Application name (for template seed when output is absent)
105
112
  */
106
- async function writeEnvOutputForReload(outputPath, runEnvPath) {
107
- const { parseEnvContentToMap, mergeEnvMapIntoContent } = require('../core/secrets');
113
+ async function writeEnvOutputForReload(outputPath, runEnvPath, appName) {
114
+ const { parseEnvContentToMap, mergeEnvMapIntoContent, generateEnvContent } = require('../core/secrets');
108
115
  const runContent = await fsp.readFile(runEnvPath, 'utf8');
109
116
  const runMap = parseEnvContentToMap(runContent);
110
- let toWrite = runContent;
111
- if (fs.existsSync(outputPath)) {
112
- const existingContent = await fsp.readFile(outputPath, 'utf8');
113
- toWrite = mergeEnvMapIntoContent(existingContent, runMap);
117
+ let baseContent;
118
+ if (fsRealSync.existsSync(outputPath)) {
119
+ baseContent = await fsp.readFile(outputPath, 'utf8');
120
+ } else if (appName) {
121
+ baseContent = await generateEnvContent(appName, null, 'local', false);
122
+ baseContent = substituteMntDataForLocal(baseContent, outputPath);
123
+ } else {
124
+ baseContent = runContent;
114
125
  }
126
+ const toWrite = mergeEnvMapIntoContent(baseContent, runMap, { appendMissingFromNewMap: false });
115
127
  await fsp.writeFile(outputPath, toWrite, { mode: 0o600 });
116
- logger.log(formatSuccessLine(`Wrote .env to envOutputPath (same as container, for --reload): ${outputPath}`));
128
+ logger.log(
129
+ formatSuccessLine(
130
+ `Wrote .env to envOutputPath (--reload: merged container env; no extra keys): ${outputPath}`
131
+ )
132
+ );
117
133
  }
118
134
 
119
135
  /**
@@ -245,6 +261,30 @@ async function patchEnvContentForLocal(envContent, variables) {
245
261
  return envContent;
246
262
  }
247
263
 
264
+ /**
265
+ * Copy the just-written builder `<appPath>/.env` to `build.envOutputPath` so both files
266
+ * share the same resolution pass (no second `generateEnvContent` with a different environment).
267
+ * Applies {@link substituteMntDataForLocal} for host paths; merges into an existing output file
268
+ * when present (preserves comments).
269
+ *
270
+ * @async
271
+ * @param {string} envPath - Path to `<appPath>/.env` (already written)
272
+ * @param {string} outputPath - Resolved `build.envOutputPath`
273
+ * @param {string} envOutputPathLabel - Label for log (e.g. raw `build.envOutputPath` value)
274
+ */
275
+ async function syncWrittenBuilderEnvToOutputPath(envPath, outputPath, envOutputPathLabel) {
276
+ const { parseEnvContentToMap, mergeEnvMapIntoContent } = require('../core/secrets');
277
+ const raw = fs.readFileSync(envPath, 'utf8');
278
+ const synced = substituteMntDataForLocal(raw, outputPath);
279
+ let toWrite = synced;
280
+ if (fs.existsSync(outputPath)) {
281
+ const existingContent = fs.readFileSync(outputPath, 'utf8');
282
+ toWrite = mergeEnvMapIntoContent(existingContent, parseEnvContentToMap(synced));
283
+ }
284
+ fs.writeFileSync(outputPath, toWrite, { mode: 0o600 });
285
+ logger.log(formatSuccessLine(`Copied resolved .env to envOutputPath (${envOutputPathLabel})`));
286
+ }
287
+
248
288
  /**
249
289
  * Write regenerated local .env to output path (merge with existing if present).
250
290
  * @async
@@ -252,10 +292,12 @@ async function patchEnvContentForLocal(envContent, variables) {
252
292
  * @param {string} appName - Application name
253
293
  * @param {string} [secretsPath] - Path to secrets file (optional)
254
294
  * @param {string} envOutputPathLabel - Label for log message (e.g. variables.build.envOutputPath)
295
+ * @param {Object} [extraOpts] - Optional: `appPath` for {@link module:lib/core/secrets-env-content.generateEnvContent}
255
296
  */
256
- async function writeLocalEnvToOutputPath(outputPath, appName, secretsPath, envOutputPathLabel) {
297
+ async function writeLocalEnvToOutputPath(outputPath, appName, secretsPath, envOutputPathLabel, extraOpts = {}) {
257
298
  const { generateEnvContent, parseEnvContentToMap, mergeEnvMapIntoContent } = require('../core/secrets');
258
- let localEnvContent = await generateEnvContent(appName, secretsPath, 'local', false);
299
+ const genOpts = extraOpts.appPath ? { appPath: extraOpts.appPath } : {};
300
+ let localEnvContent = await generateEnvContent(appName, secretsPath, 'local', false, genOpts);
259
301
  localEnvContent = substituteMntDataForLocal(localEnvContent, outputPath);
260
302
  let toWrite = localEnvContent;
261
303
  if (fs.existsSync(outputPath)) {
@@ -284,16 +326,45 @@ async function writePatchedEnvToOutputPath(envPath, outputPath, variables, envOu
284
326
  }
285
327
 
286
328
  /**
287
- * Process and optionally copy env file to envOutputPath if configured
288
- * Regenerates .env file with env=local for local development (apps/.env)
329
+ * Process and optionally copy env file to envOutputPath if configured.
330
+ * When `appName` is set and `preferLocalEnvOutputPath` is true, regenerates **local**-flavored env at
331
+ * `build.envOutputPath` (IDE/host). **False** only when `remote-server` and `applications.<app>.reload` are both set
332
+ * (docker flavor at env output). Otherwise, when `appName`
333
+ * is set and `envPath` exists, copies the same resolved `<appPath>/.env` content to the output path.
334
+ * Falls back to local regeneration when `envPath` is missing, or patched copy when `appName` is omitted.
335
+ *
289
336
  * @async
290
337
  * @function processEnvVariables
291
338
  * @param {string} envPath - Path to generated .env file
292
339
  * @param {string} variablesPath - Path to application config
293
340
  * @param {string} appName - Application name (for regenerating with local env)
294
341
  * @param {string} [secretsPath] - Path to secrets file (optional, for regenerating)
342
+ * @param {Object} [copyOptions] - Optional: `preferLocalEnvOutputPath` (IDE/host file gets **local** flavor
343
+ * while `<appPath>/.env` may be docker); `appPath` overrides app root for local regeneration
295
344
  */
296
- async function processEnvVariables(envPath, variablesPath, appName, secretsPath) {
345
+ async function copyOrRegenerateEnvForNamedApp(opts) {
346
+ const {
347
+ envPath,
348
+ outputPath,
349
+ appName,
350
+ secretsPath,
351
+ label,
352
+ preferLocal,
353
+ appPathForLocal
354
+ } = opts;
355
+ const localOpts = { appPath: appPathForLocal || undefined };
356
+ if (preferLocal) {
357
+ await writeLocalEnvToOutputPath(outputPath, appName, secretsPath, label, localOpts);
358
+ return;
359
+ }
360
+ if (fs.existsSync(envPath)) {
361
+ await syncWrittenBuilderEnvToOutputPath(envPath, outputPath, label);
362
+ return;
363
+ }
364
+ await writeLocalEnvToOutputPath(outputPath, appName, secretsPath, label, localOpts);
365
+ }
366
+
367
+ async function processEnvVariables(envPath, variablesPath, appName, secretsPath, copyOptions = {}) {
297
368
  if (!variablesPath || !fs.existsSync(variablesPath)) {
298
369
  return;
299
370
  }
@@ -310,8 +381,21 @@ async function processEnvVariables(envPath, variablesPath, appName, secretsPath)
310
381
  }
311
382
 
312
383
  const label = variables.build.envOutputPath;
384
+ const appPathForLocal =
385
+ (copyOptions && copyOptions.appPath) ||
386
+ (variablesPath ? path.dirname(variablesPath) : null);
387
+ const preferLocal =
388
+ copyOptions && typeof copyOptions === 'object' && copyOptions.preferLocalEnvOutputPath === true;
313
389
  if (appName) {
314
- await writeLocalEnvToOutputPath(outputPath, appName, secretsPath, label);
390
+ await copyOrRegenerateEnvForNamedApp({
391
+ envPath,
392
+ outputPath,
393
+ appName,
394
+ secretsPath,
395
+ label,
396
+ preferLocal,
397
+ appPathForLocal
398
+ });
315
399
  } else {
316
400
  await writePatchedEnvToOutputPath(envPath, outputPath, variables, label);
317
401
  }
@@ -321,6 +405,7 @@ module.exports = {
321
405
  processEnvVariables,
322
406
  resolveEnvOutputPath,
323
407
  substituteMntDataForLocal,
408
+ syncWrittenBuilderEnvToOutputPath,
324
409
  writeEnvOutputForReload,
325
410
  writeEnvOutputForLocal
326
411
  };
@@ -13,6 +13,7 @@ const fsSync = require('fs');
13
13
  const path = require('path');
14
14
  const chalk = require('chalk');
15
15
  const logger = require('../utils/logger');
16
+ const pathsUtil = require('./paths');
16
17
 
17
18
  /**
18
19
  * Updates env.template to add MISO_CLIENTID, MISO_CLIENTSECRET, and optionally MISO_CONTROLLER_URL.
@@ -105,7 +106,10 @@ ${missingEntries.join('\n')}
105
106
  }
106
107
 
107
108
  async function updateEnvTemplate(appKey, clientIdKey, clientSecretKey, _controllerUrl) {
108
- const envTemplatePath = path.join(process.cwd(), 'builder', appKey, 'env.template');
109
+ // Use paths.getBuilderPath so the system builder root (e.g. ~/.aifabrix/builder/<app>) and
110
+ // AIFABRIX_BUILDER_DIR are respected. Falling back to process.cwd() previously made the binary
111
+ // CLI skip env.template updates whenever cwd was a non-builder repo (e.g. aifabrix-training).
112
+ const envTemplatePath = path.join(pathsUtil.getBuilderPath(appKey), 'env.template');
109
113
 
110
114
  if (!fsSync.existsSync(envTemplatePath)) {
111
115
  logger.warn(chalk.yellow(`⚠ env.template not found for ${appKey}, skipping update`));
@@ -62,19 +62,9 @@ function frontDoorPattern(appConfig) {
62
62
  return (typeof p === 'string' && p.trim()) ? p.trim() : null;
63
63
  }
64
64
 
65
- /**
66
- * Compute the Traefik front-door health check URL when applicable.
67
- *
68
- * Returns null when Traefik/frontDoorRouting isn't active or cannot be resolved.
69
- *
70
- * @async
71
- * @param {string} appName
72
- * @param {number} healthCheckPort
73
- * @param {Object|null} appConfig
74
- * @returns {Promise<string|null>}
75
- */
76
65
  /**
77
66
  * Public app URL (Traefik + frontDoor mount path), without the health path — for CLI summaries.
67
+ * Requires infra Traefik on, `frontDoorRouting.enabled`, host, and pattern.
78
68
  *
79
69
  * @async
80
70
  * @param {string} appName
@@ -83,7 +73,7 @@ function frontDoorPattern(appConfig) {
83
73
  * @returns {Promise<string|null>}
84
74
  */
85
75
  async function computeTraefikPublicAppUrl(_appName, _healthCheckPort, appConfig) {
86
- if (!frontDoorEnabled(appConfig)) return null;
76
+ if (!appConfig || !frontDoorEnabled(appConfig)) return null;
87
77
  const pattern = frontDoorPattern(appConfig);
88
78
  if (!pattern) return null;
89
79
 
@@ -91,6 +81,13 @@ async function computeTraefikPublicAppUrl(_appName, _healthCheckPort, appConfig)
91
81
  const userCfg = await coreConfig.getConfig();
92
82
  if (!(userCfg && userCfg.traefik)) return null;
93
83
 
84
+ const fd = appConfig.frontDoorRouting;
85
+ if (!fd || typeof fd !== 'object') return null;
86
+ const hostTemplate = typeof fd.host === 'string' ? fd.host.trim() : '';
87
+ if (!hostTemplate) return null;
88
+
89
+ const mountPath = normalizeFrontDoorPatternForHealth(pattern);
90
+
94
91
  const infraTlsEnabled = Boolean(userCfg && userCfg.tlsEnabled);
95
92
  const remoteServer = await coreConfig.getRemoteServer();
96
93
  const developerIdRaw = await coreConfig.getDeveloperId();
@@ -98,7 +95,6 @@ async function computeTraefikPublicAppUrl(_appName, _healthCheckPort, appConfig)
98
95
 
99
96
  // URLs are resolved for the CLI on the host, not from inside a container.
100
97
  const profile = 'local';
101
- const fd = appConfig.frontDoorRouting;
102
98
  const listenPort = Number(appConfig?.port || 3000);
103
99
 
104
100
  const publicBase = computePublicUrlBaseString({
@@ -114,10 +110,18 @@ async function computeTraefikPublicAppUrl(_appName, _healthCheckPort, appConfig)
114
110
  infraTlsEnabled
115
111
  });
116
112
 
117
- const mountPath = normalizeFrontDoorPatternForHealth(pattern);
118
113
  return joinUrlPath(publicBase, mountPath);
119
114
  }
120
115
 
116
+ /**
117
+ * Traefik public URL including health path.
118
+ *
119
+ * @async
120
+ * @param {string} appName
121
+ * @param {number} healthCheckPort
122
+ * @param {Object|null} appConfig
123
+ * @returns {Promise<string|null>}
124
+ */
121
125
  async function computeTraefikHealthCheckUrl(appName, healthCheckPort, appConfig) {
122
126
  const baseWithFrontDoor = await computeTraefikPublicAppUrl(appName, healthCheckPort, appConfig);
123
127
  if (!baseWithFrontDoor) return null;
@@ -131,4 +135,3 @@ module.exports = {
131
135
  computeTraefikPublicAppUrl,
132
136
  computeTraefikHealthCheckUrl
133
137
  };
134
-
@@ -16,7 +16,8 @@ const dns = require('dns');
16
16
  const chalk = require('chalk');
17
17
  const logger = require('./logger');
18
18
  const { execWithDockerEnv } = require('./docker-exec');
19
- const { computeTraefikHealthCheckUrl } = require('./health-check-url');
19
+ /** Namespace require so tests can `jest.spyOn` on `./health-check-url` exports. */
20
+ const healthCheckUrl = require('./health-check-url');
20
21
  const { computePathActive } = require('./url-declarative-url-flags');
21
22
  const { isFrontDoorRoutingEnabledInDoc } = require('./url-declarative-vdir-inactive-env');
22
23
  const { waitForDbInit } = require('./health-check-db-init');
@@ -69,10 +70,11 @@ async function computeHealthCheckUrl(appName, healthCheckPort, appConfig, _opts
69
70
  const runOptions = (_opts && typeof _opts === 'object') ? _opts.runOptions : null;
70
71
  const wantsTraefik =
71
72
  Boolean(runOptions) &&
72
- (runOptions.probeViaTraefik === true || runOptions.traefikEnabled === true);
73
+ (runOptions.probeViaTraefik === true || runOptions.traefikEnabled === true) &&
74
+ isFrontDoorRoutingEnabledInDoc(appConfig);
73
75
  if (!wantsTraefik) return '';
74
76
  try {
75
- return await computeTraefikHealthCheckUrl(appName, healthCheckPort, appConfig);
77
+ return await healthCheckUrl.computeTraefikHealthCheckUrl(appName, healthCheckPort, appConfig);
76
78
  } catch {
77
79
  return '';
78
80
  }
@@ -380,10 +382,10 @@ async function computePreferredHealthCheckUrls(appName, healthCheckPort, config,
380
382
  let skippedPublicHealthUrl = '';
381
383
  const wantsTraefikFirst = Boolean(
382
384
  runOptions && (runOptions.probeViaTraefik === true || runOptions.traefikEnabled === true)
383
- );
385
+ ) && isFrontDoorRoutingEnabledInDoc(config);
384
386
  if (wantsTraefikFirst) {
385
387
  try {
386
- traefikUrl = await computeTraefikHealthCheckUrl(appName, healthCheckPort, config);
388
+ traefikUrl = await healthCheckUrl.computeTraefikHealthCheckUrl(appName, healthCheckPort, config);
387
389
  } catch {
388
390
  traefikUrl = '';
389
391
  }
@@ -26,10 +26,10 @@ const CATEGORIES = [
26
26
  { name: 'up-platform' },
27
27
  { name: 'up-miso' },
28
28
  { name: 'up-dataplane' },
29
- { name: 'down-infra', term: 'down-infra [service|app]' },
29
+ { name: 'down-infra', term: 'down-infra [service|appKey]' },
30
30
  { name: 'doctor' },
31
31
  { name: 'status' },
32
- { name: 'restart', term: 'restart <service|app>' }
32
+ { name: 'restart', term: 'restart <service|appKey>' }
33
33
  ]
34
34
  },
35
35
  {
@@ -43,23 +43,22 @@ const CATEGORIES = [
43
43
  {
44
44
  name: 'Applications (Create & Develop)',
45
45
  commands: [
46
- { name: 'create', term: 'create <app>' },
47
- { name: 'wizard' },
48
- { name: 'build', term: 'build <app>' },
49
- { name: 'run', term: 'run <app>' },
50
- { name: 'shell', term: 'shell <app>' },
51
- { name: 'install', term: 'install <app>' },
52
- { name: 'lint', term: 'lint <app>' },
53
- { name: 'logs', term: 'logs <app>' },
54
- { name: 'stop', term: 'stop <app>' },
55
- { name: 'dockerfile', term: 'dockerfile <app>' }
46
+ { name: 'create', term: 'create <appKey|systemKey>' },
47
+ { name: 'build', term: 'build <appKey>' },
48
+ { name: 'run', term: 'run <appKey>' },
49
+ { name: 'shell', term: 'shell <appKey>' },
50
+ { name: 'install', term: 'install <appKey>' },
51
+ { name: 'lint', term: 'lint <appKey>' },
52
+ { name: 'logs', term: 'logs <appKey>' },
53
+ { name: 'stop', term: 'stop <appKey>' },
54
+ { name: 'dockerfile', term: 'dockerfile <appKey>' }
56
55
  ]
57
56
  },
58
57
  {
59
58
  name: 'Deployment',
60
59
  commands: [
61
- { name: 'push', term: 'push <app>' },
62
- { name: 'deploy', term: 'deploy <app>' }
60
+ { name: 'push', term: 'push <appKey>' },
61
+ { name: 'deploy', term: 'deploy <appKey|systemKey>' }
63
62
  ]
64
63
  },
65
64
  {
@@ -80,12 +79,12 @@ const CATEGORIES = [
80
79
  {
81
80
  name: 'Configuration & Validation',
82
81
  commands: [
83
- { name: 'resolve', term: 'resolve <app>' },
84
- { name: 'json', term: 'json <app>' },
85
- { name: 'split-json', term: 'split-json <app>' },
86
- { name: 'convert', term: 'convert <app>' },
87
- { name: 'show', term: 'show <app>' },
88
- { name: 'validate', term: 'validate <appOrFile>' },
82
+ { name: 'resolve', term: 'resolve <appKey|systemKey>' },
83
+ { name: 'json', term: 'json <appKey|systemKey>' },
84
+ { name: 'split-json', term: 'split-json <appKey|systemKey>' },
85
+ { name: 'convert', term: 'convert <appKey|systemKey>' },
86
+ { name: 'show', term: 'show <appKey|systemKey>' },
87
+ { name: 'validate', term: 'validate [appKey|systemKey|file]' },
89
88
  { name: 'parameters', term: 'parameters validate' },
90
89
  { name: 'diff', term: 'diff <file1> <file2>' }
91
90
  ]
@@ -93,16 +92,23 @@ const CATEGORIES = [
93
92
  {
94
93
  name: 'External Systems',
95
94
  commands: [
95
+ { name: 'wizard', term: 'wizard [systemKey]' },
96
96
  { name: 'download', term: 'download <systemKey>' },
97
97
  { name: 'upload', term: 'upload <systemKey>' },
98
98
  { name: 'delete', term: 'delete <systemKey>' },
99
99
  { name: 'repair', term: 'repair <systemKey>' },
100
100
  { name: 'datasource' },
101
+ { name: 'datasource load', term: 'datasource load <datasourceKey>' },
102
+ { name: 'datasource export', term: 'datasource export <datasourceKey>' },
101
103
  { name: 'dimension' },
102
- { name: 'dimension-value' },
103
- { name: 'test', term: 'test <app>' },
104
- { name: 'test-e2e', term: 'test-e2e <app>' },
105
- { name: 'test-integration', term: 'test-integration <app>' }
104
+ { name: 'dimension value', term: 'dimension value <command>' },
105
+ { name: 'identity' },
106
+ { name: 'protection' },
107
+ { name: 'test', term: 'test <appKey|systemKey>' },
108
+ { name: 'test-e2e', term: 'test-e2e <appKey|systemKey>' },
109
+ { name: 'test-governance', term: 'test-governance <appKey|systemKey>' },
110
+ { name: 'test-trust', term: 'test-trust <appKey|systemKey>' },
111
+ { name: 'test-integration', term: 'test-integration <appKey|systemKey>' }
106
112
  ]
107
113
  },
108
114
  {
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Optional infra compose flags (Traefik, pgAdmin, Redis Commander) for config.yaml ↔ startInfra.
3
+ *
4
+ * @fileoverview Effective flag resolution + backfill missing keys after up-infra / setup
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const config = require('../core/config');
12
+
13
+ /**
14
+ * Resolves effective boolean from CLI option vs config.
15
+ * @param {*} optValue - options.traefik | options.pgAdmin | options.redisAdmin
16
+ * @param {*} cfgValue - cfg.traefik | cfg.pgadmin | cfg.redisCommander
17
+ * @param {boolean} defaultWhenUndef - Default when config value is undefined
18
+ * @returns {boolean}
19
+ */
20
+ function resolveInfraOptionalFlag(optValue, cfgValue, defaultWhenUndef = true) {
21
+ if (optValue === true) return true;
22
+ if (optValue === false) return false;
23
+ return cfgValue !== false && (cfgValue === true || defaultWhenUndef);
24
+ }
25
+
26
+ /**
27
+ * Effective optional infra service flags (same resolution as up-infra → startInfra).
28
+ *
29
+ * @param {Object} cfg - Config from {@link config.getConfig}
30
+ * @param {Object} [options] - Commander options (omit for setup-modes)
31
+ * @returns {{ traefik: boolean, pgadmin: boolean, redisCommander: boolean }}
32
+ */
33
+ function computeEffectiveInfraOptionalFlags(cfg, options = {}) {
34
+ return {
35
+ traefik: resolveInfraOptionalFlag(options.traefik, cfg.traefik, false),
36
+ pgadmin: resolveInfraOptionalFlag(options.pgAdmin, cfg.pgadmin, true),
37
+ redisCommander: resolveInfraOptionalFlag(options.redisAdmin, cfg.redisCommander, true)
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Writes `traefik` / `pgadmin` / `redisCommander` when missing so config matches compose defaults.
43
+ *
44
+ * @param {Object} cfg - Mutable config (updated when save runs)
45
+ * @param {{ traefik: boolean, pgadmin: boolean, redisCommander: boolean }} effective
46
+ * @returns {Promise<void>}
47
+ */
48
+ async function persistMissingInfraOptionalServiceFlags(cfg, effective) {
49
+ const keys = ['traefik', 'pgadmin', 'redisCommander'];
50
+ const merged = { ...cfg };
51
+ let dirty = false;
52
+ for (const k of keys) {
53
+ if (typeof merged[k] === 'undefined') {
54
+ merged[k] = effective[k];
55
+ dirty = true;
56
+ }
57
+ }
58
+ if (!dirty) {
59
+ return;
60
+ }
61
+ await config.saveConfig(merged);
62
+ Object.assign(cfg, merged);
63
+ }
64
+
65
+ module.exports = {
66
+ resolveInfraOptionalFlag,
67
+ computeEffectiveInfraOptionalFlags,
68
+ persistMissingInfraOptionalServiceFlags
69
+ };