@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
@@ -94,19 +94,20 @@ async function saveLocalCredentials(responseData, apiUrl) {
94
94
  await saveLocalSecret(clientIdKey, responseData.credentials.clientId);
95
95
  await saveLocalSecret(clientSecretKey, responseData.credentials.clientSecret);
96
96
 
97
- // Update env.template
97
+ // Update env.template (kv:// refs only; no resolved values touch disk)
98
98
  await updateEnvTemplate(registeredAppKey, clientIdKey, clientSecretKey, apiUrl);
99
99
 
100
- // Regenerate .env file with updated credentials
100
+ // Resolve in-memory so any missing-secret / kv:// error surfaces here, but never
101
+ // materialize <appPath>/.env or envOutputPath — that is only done by `aifabrix resolve`.
101
102
  try {
102
- await generateEnvFile(registeredAppKey, null, 'local', true);
103
- logger.log(formatSuccessLine('.env file updated with new credentials'));
103
+ await generateEnvFile(registeredAppKey, null, 'local', true, { noWrite: true });
104
104
  } catch (error) {
105
- logger.warn(chalk.yellow(`⚠ Could not regenerate .env file: ${error.message}`));
105
+ logger.warn(chalk.yellow(`⚠ Could not validate env resolution: ${error.message}`));
106
106
  }
107
107
 
108
108
  logger.log(formatSuccessParagraph('Credentials saved to ~/.aifabrix/secrets.local.yaml'));
109
- logger.log(formatSuccessLine('env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL\n'));
109
+ logger.log(formatSuccessLine('env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL'));
110
+ logger.log(formatSuccessLine('Run "aifabrix resolve ' + registeredAppKey + '" to materialize an on-disk .env\n'));
110
111
  } catch (error) {
111
112
  logger.warn(chalk.yellow(`⚠ Could not save credentials locally: ${error.message}`));
112
113
  }
@@ -13,6 +13,7 @@ const config = require('../core/config');
13
13
  const { execWithDockerEnv } = require('../utils/docker-exec');
14
14
  const { sectionTitle, headerKeyValue, metadata } = require('../utils/cli-test-layout-chalk');
15
15
  const { isReloadBindMountOnEngineHost } = require('../utils/docker-reload-mount');
16
+ const { isApplicationsReloadDefaultOn } = require('../utils/applications-config-defaults');
16
17
 
17
18
  /**
18
19
  * @param {unknown} mounts - docker inspect .Mounts
@@ -54,42 +55,72 @@ async function fetchContainerMountsJson(containerName) {
54
55
  }
55
56
  }
56
57
 
58
+ /**
59
+ * Log saved reload default for the app (same source as `aifabrix run` dev persist / `aifabrix show`).
60
+ * @param {string|null|undefined} appName - Application key (e.g. miso-controller)
61
+ * @returns {Promise<void>}
62
+ */
63
+ async function logReloadConfigSummaryForRestart(appName) {
64
+ if (!appName || typeof appName !== 'string') {
65
+ return;
66
+ }
67
+ const userCfg = await config.getConfig();
68
+ const reloadOn = isApplicationsReloadDefaultOn(userCfg, appName);
69
+ logger.log('');
70
+ logger.log(sectionTitle('Reload (config)'));
71
+ logger.log(
72
+ headerKeyValue(
73
+ 'Next dev run:',
74
+ reloadOn ? 'reload on (applications.<app>.reload in config)' : 'reload off'
75
+ )
76
+ );
77
+ logger.log(
78
+ metadata(
79
+ 'Persisted when you last ran this app in dev with or without --reload. Use aifabrix show <app> to inspect.'
80
+ )
81
+ );
82
+ logger.log('');
83
+ }
84
+
57
85
  /**
58
86
  * Log workspace transport after a successful container restart (mounts unchanged).
59
87
  * @param {string} containerName - Docker container name
88
+ * @param {string|null} [appName] - Application key; when set, logs reload default from config after mount info
60
89
  * @returns {Promise<void>}
61
90
  */
62
- async function logRestartDevMountSummary(containerName) {
91
+ async function logRestartDevMountSummary(containerName, appName = null) {
63
92
  if (!containerName || typeof containerName !== 'string') {
93
+ await logReloadConfigSummaryForRestart(appName);
64
94
  return;
65
95
  }
66
96
  const mounts = await fetchContainerMountsJson(containerName);
67
97
  const appBind = findAppBindMount(mounts);
68
- if (!appBind) {
69
- return;
70
- }
71
- const endpoint = await config.getDockerEndpoint();
72
- const localEngine = isReloadBindMountOnEngineHost(endpoint);
98
+ if (appBind) {
99
+ const endpoint = await config.getDockerEndpoint();
100
+ const localEngine = isReloadBindMountOnEngineHost(endpoint);
73
101
 
74
- logger.log('');
75
- logger.log(sectionTitle('Dev workspace (unchanged by restart)'));
76
- if (localEngine) {
77
- logger.log(headerKeyValue('Transport:', 'Direct bind mount on the Docker host (no Mutagen).'));
78
- logger.log(headerKeyValue('Host path → container:', `${appBind.Source} → /app`));
79
- logger.log(metadata('Edits under the host path are visible inside the container immediately.'));
80
- } else {
81
- logger.log(
82
- headerKeyValue(
83
- 'Transport:',
84
- 'Bind mount on the Docker engine (see path below; Mutagen may sync to this path when using --reload).'
85
- )
86
- );
87
- logger.log(headerKeyValue('Engine path → container:', `${appBind.Source} → /app`));
102
+ logger.log('');
103
+ logger.log(sectionTitle('Dev workspace (unchanged by restart)'));
104
+ if (localEngine) {
105
+ logger.log(headerKeyValue('Transport:', 'Direct bind mount on the Docker host (no Mutagen).'));
106
+ logger.log(headerKeyValue('Host path → container:', `${appBind.Source} → /app`));
107
+ logger.log(metadata('Edits under the host path are visible inside the container immediately.'));
108
+ } else {
109
+ logger.log(
110
+ headerKeyValue(
111
+ 'Transport:',
112
+ 'Bind mount on the Docker engine (see path below; Mutagen may sync to this path when using --reload).'
113
+ )
114
+ );
115
+ logger.log(headerKeyValue('Engine path → container:', `${appBind.Source} → /app`));
116
+ }
117
+ logger.log('');
88
118
  }
89
- logger.log('');
119
+ await logReloadConfigSummaryForRestart(appName);
90
120
  }
91
121
 
92
122
  module.exports = {
93
123
  findAppBindMount,
124
+ logReloadConfigSummaryForRestart,
94
125
  logRestartDevMountSummary
95
126
  };
@@ -281,20 +281,21 @@ async function saveCredentialsLocally(appKey, credentials, actualControllerUrl)
281
281
  await saveLocalSecret(clientIdKey, credentials.clientId);
282
282
  await saveLocalSecret(clientSecretKey, credentials.clientSecret);
283
283
 
284
- // Update env.template if localhost
284
+ // Update env.template if localhost (kv:// refs only; no resolved values touch disk)
285
285
  if (isLocalhost(actualControllerUrl)) {
286
286
  await updateEnvTemplate(appKey, clientIdKey, clientSecretKey, actualControllerUrl);
287
287
 
288
- // Regenerate .env file with updated credentials
288
+ // Resolve in-memory so any missing-secret / kv:// error surfaces here, but never
289
+ // materialize <appPath>/.env or envOutputPath — that is only done by `aifabrix resolve`.
289
290
  try {
290
- await generateEnvFile(appKey, null, 'local', true);
291
- logger.log(formatSuccessLine('.env file updated with new credentials'));
291
+ await generateEnvFile(appKey, null, 'local', true, { noWrite: true });
292
292
  } catch (error) {
293
- logger.warn(chalk.yellow(`⚠ Could not regenerate .env file: ${error.message}`));
293
+ logger.warn(chalk.yellow(`⚠ Could not validate env resolution: ${error.message}`));
294
294
  }
295
295
 
296
296
  logger.log(formatSuccessParagraph('Credentials saved to ~/.aifabrix/secrets.local.yaml'));
297
- logger.log(formatSuccessLine('env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL\n'));
297
+ logger.log(formatSuccessLine('env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL'));
298
+ logger.log(formatSuccessLine('Run "aifabrix resolve ' + appKey + '" to materialize an on-disk .env\n'));
298
299
  } else {
299
300
  logger.log(formatSuccessParagraph('Credentials saved to ~/.aifabrix/secrets.local.yaml\n'));
300
301
  }
@@ -38,6 +38,7 @@ const { resolveEnvOutputPath, writeEnvOutputForReload, writeEnvOutputForLocal }
38
38
  const { resolveVersionForApp } = require('../utils/image-version');
39
39
  const healthCheckUtil = require('../utils/health-check');
40
40
  const { computeTraefikPublicAppUrl } = require('../utils/health-check-url');
41
+ const { isFrontDoorRoutingEnabledInDoc } = require('../utils/url-declarative-vdir-inactive-env');
41
42
 
42
43
  /** Template apps (keycloak, miso-controller, dataplane) - never update application config when running */
43
44
  const TEMPLATE_APP_KEYS = ['keycloak', 'miso-controller', 'dataplane'];
@@ -72,7 +73,7 @@ function checkBuilderDirectory(appName) {
72
73
 
73
74
  /**
74
75
  * Load and validate config file exists
75
- * Uses paths.getBuilderPath so AIFABRIX_BUILDER_DIR (e.g. from up-miso) is respected.
76
+ * Uses paths.getBuilderPath (cwd / material `builder/` only; no `AIFABRIX_BUILDER_DIR` override).
76
77
  * @param {string} appName - Application name
77
78
  * @returns {Object} Application configuration
78
79
  * @throws {Error} If config file not found
@@ -299,12 +300,15 @@ async function generateComposeFile(appName, appConfig, composeOptions, devDir) {
299
300
  }
300
301
 
301
302
  /**
302
- * Writes .env to envOutputPath when application.yaml build.envOutputPath is set.
303
+ * Writes `build.envOutputPath` after each `aifabrix run`: **local**-flavored env for the host/IDE
304
+ * when not using `--reload`; with **`--reload`**, merges container `.env.run` into the existing file
305
+ * (preserves resolve comments) without appending run-only keys missing from the template.
306
+ *
303
307
  * @async
304
308
  * @param {string} appName - Application name
305
309
  * @param {Object} appConfig - Application configuration
306
- * @param {string} runEnvPath - Path to .env.run
307
- * @param {Object} options - Run options (reload flag)
310
+ * @param {string} runEnvPath - Path to `.env.run`
311
+ * @param {Object} options - Run options (`reload`, `skipEnvOutputPath`)
308
312
  */
309
313
  async function writeEnvOutputIfConfigured(appName, appConfig, runEnvPath, options) {
310
314
  if (options && options.skipEnvOutputPath === true) return;
@@ -318,8 +322,8 @@ async function writeEnvOutputIfConfigured(appName, appConfig, runEnvPath, option
318
322
  if (!fsSync.existsSync(outputDir)) {
319
323
  await fs.mkdir(outputDir, { recursive: true });
320
324
  }
321
- if (options.reload) {
322
- await writeEnvOutputForReload(outputPath, runEnvPath);
325
+ if (options && options.reload === true) {
326
+ await writeEnvOutputForReload(outputPath, runEnvPath, appName);
323
327
  } else {
324
328
  await writeEnvOutputForLocal(appName, outputPath);
325
329
  }
@@ -362,7 +366,8 @@ async function prepareEnvironment(appName, appConfig, options) {
362
366
  envFilePath: runEnvPath,
363
367
  dbInitEnvFilePath: runEnvAdminPath,
364
368
  effectiveEnvironmentScopedResources,
365
- env: runEnvKey
369
+ env: runEnvKey,
370
+ omitAppTraefikLabels: userCfg.traefik === false
366
371
  };
367
372
  composeOptions.port = calculateComposePort(composeOptions, appConfig, developerId);
368
373
  const composePath = await generateComposeFile(appName, appConfig, composeOptions, devDir);
@@ -383,7 +388,9 @@ async function prepareEnvironment(appName, appConfig, options) {
383
388
  async function displayRunStatus(appName, port, appConfig, runScopeOpts = null, runOptions = {}) {
384
389
  const containerName = containerHelpers.getContainerName(appName, appConfig.developerId, runScopeOpts);
385
390
  const ro = runOptions && typeof runOptions === 'object' ? runOptions : {};
386
- const wantsPublic = Boolean(ro.probeViaTraefik === true || ro.traefikEnabled === true);
391
+ const frontDoorOn = isFrontDoorRoutingEnabledInDoc(appConfig);
392
+ const wantsPublic =
393
+ frontDoorOn && Boolean(ro.probeViaTraefik === true || ro.traefikEnabled === true);
387
394
 
388
395
  let primaryAppUrl = `http://localhost:${port}`;
389
396
  if (wantsPublic) {
package/lib/app/run.js CHANGED
@@ -28,6 +28,7 @@ const helpers = require('./run-helpers');
28
28
  const { ensureReloadSync, logReloadDevSummary } = require('./run-reload-sync');
29
29
  const { logRestartDevMountSummary } = require('./restart-display');
30
30
  const { execWithDockerEnv } = require('../utils/docker-exec');
31
+ const { isRunCliNoProxy } = require('../utils/run-cli-flags');
31
32
 
32
33
  /**
33
34
  * True if host is localhost or 127.0.0.1 (case-insensitive).
@@ -68,7 +69,7 @@ function isLocalhostEndpoint(urlOrEndpoint) {
68
69
  * @async
69
70
  * @param {string} appName - Application name
70
71
  * @param {boolean} _debug - Debug flag (unused)
71
- * @returns {Promise<boolean>} True if should continue, false if external system
72
+ * @returns {Promise<boolean>} True if run should continue, false if app is integration-scoped or external (not a runnable container app)
72
73
  * @throws {Error} If app name is invalid
73
74
  */
74
75
  async function validateAppForRun(appName, _debug) {
@@ -88,9 +89,16 @@ async function validateAppForRun(appName, _debug) {
88
89
  formatWarningLine('External integrations are not started as local Docker containers.')
89
90
  );
90
91
  logger.log(
91
- metadata('Build and deploy the integration, then use its APIs from your environment.')
92
+ metadata(
93
+ 'Publish config to the dataplane (upload) or through the controller (deploy), then use the integration APIs from your environment.'
94
+ )
95
+ );
96
+ logger.log(
97
+ formatNextActions([
98
+ `aifabrix upload ${appName}`,
99
+ `aifabrix deploy ${appName}`
100
+ ])
92
101
  );
93
- logger.log(formatNextActions([`aifabrix build ${appName}`]));
94
102
  return false;
95
103
  }
96
104
  } catch (error) {
@@ -263,12 +271,12 @@ async function resolveRunOptions(appName, appConfig, options, envKey, debug, eff
263
271
  }
264
272
 
265
273
  /**
266
- * When Traefik is enabled in user config, pass through for health checks (DNS + localhost order).
274
+ * When Traefik is enabled in user config and this run should use proxy hints (`runOptions.noProxy` is false), enable Traefik/DNS URL hints.
267
275
  * @param {Object} runOptions
268
276
  * @param {Object} userCfg
269
277
  */
270
278
  function applyTraefikFlagToRunOptions(runOptions, userCfg) {
271
- if (userCfg && userCfg.traefik === true) {
279
+ if (userCfg && userCfg.traefik === true && !runOptions.noProxy) {
272
280
  runOptions.traefikEnabled = true;
273
281
  }
274
282
  }
@@ -335,10 +343,28 @@ async function computeRunPreparationCore(appName, appConfig, options, envKey, us
335
343
  return result;
336
344
  }
337
345
 
346
+ /**
347
+ * Gray **Manifest:** line before container start (skipped when orchestration already emitted).
348
+ * @param {string} appName
349
+ * @param {Object} options
350
+ * @returns {void}
351
+ */
352
+ function emitRunManifestMetadataIfRequested(appName, options) {
353
+ if (options.skipManifestMetadataLine === true) return;
354
+ const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
355
+ emitManifestMetadataLineIfTTY(logger, {
356
+ appKey: appName,
357
+ appPath: pathsUtil.getBuilderPath(appName),
358
+ envOnly: false,
359
+ json: false
360
+ });
361
+ }
362
+
338
363
  /**
339
364
  * Prepare run: validate app, load config, check prereqs, stop existing container, resolve port and reload, prepare env.
340
365
  * @param {string} appName - Application name
341
366
  * @param {Object} options - Run options
367
+ * @param {boolean} [options.skipManifestMetadataLine] - When true, skip gray **Manifest:** line (caller already emitted for platform orchestration)
342
368
  * @param {boolean} debug - Debug flag
343
369
  * @returns {Promise<{ appConfig: Object, tempComposePath: string, hostPort: number }|null>} Prepared run context or null if should not continue
344
370
  */
@@ -349,12 +375,25 @@ async function prepareAppRun(appName, options, debug) {
349
375
  if (!shouldContinue) {
350
376
  return null;
351
377
  }
378
+ let userCfg = await config.getConfig();
379
+ const appsDefaults = require('../utils/applications-config-defaults');
380
+ await appsDefaults.persistApplicationRunProxyFlag(appName, !isRunCliNoProxy(options));
381
+ if (envKey === 'dev') {
382
+ await appsDefaults.persistApplicationReloadFlag(appName, options.reload === true);
383
+ }
384
+ userCfg = await config.getConfig();
385
+ const savedProxyHint = appsDefaults.getApplicationsRunProxyHint(userCfg, appName);
386
+ const mergedOpts = {
387
+ ...options,
388
+ reload: options.reload === true,
389
+ noProxy: isRunCliNoProxy(options) || !savedProxyHint
390
+ };
352
391
  logger.log('');
353
392
  logger.log(sectionTitle('Run'));
354
393
  logger.log(headerKeyValue('Application:', appName));
355
394
  const appConfig = await loadAndConfigureApp(appName, debug);
356
- const userCfg = await config.getConfig();
357
- return computeRunPreparationCore(appName, appConfig, options, envKey, userCfg, debug);
395
+ emitRunManifestMetadataIfRequested(appName, options);
396
+ return computeRunPreparationCore(appName, appConfig, mergedOpts, envKey, userCfg, debug);
358
397
  }
359
398
 
360
399
  /**
@@ -367,6 +406,7 @@ async function prepareAppRun(appName, options, debug) {
367
406
  * @param {Object} options - Run options
368
407
  * @param {number} [options.port] - Override local port
369
408
  * @param {boolean} [options.debug] - Enable debug output
409
+ * @param {boolean} [options.skipManifestMetadataLine] - When true, skip gray **Manifest:** line in prepare (orchestration already emitted)
370
410
  * @returns {Promise<void>} Resolves when app is running
371
411
  * @throws {Error} If run fails or app is not built
372
412
  *
@@ -388,7 +428,7 @@ async function runApp(appName, options = {}) {
388
428
  if (debug) {
389
429
  logger.log(chalk.gray(`[DEBUG] Compose file generated: ${prepared.tempComposePath}`));
390
430
  }
391
- logReloadDevSummary(Boolean(options.reload), prepared.mergedRunOptions.reloadSyncSummary);
431
+ logReloadDevSummary(Boolean(prepared.mergedRunOptions.reload), prepared.mergedRunOptions.reloadSyncSummary);
392
432
  await startAppContainer(appName, prepared.tempComposePath, prepared.hostPort, prepared.appConfig, {
393
433
  debug,
394
434
  runEnvPath: prepared.runEnvPath,
@@ -422,11 +462,18 @@ async function restartApp(appName) {
422
462
  if (!appName || typeof appName !== 'string') {
423
463
  throw new Error('Application name is required and must be a string');
424
464
  }
465
+ const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
466
+ emitManifestMetadataLineIfTTY(logger, {
467
+ appKey: appName,
468
+ appPath: pathsUtil.getBuilderPath(appName),
469
+ envOnly: false,
470
+ json: false
471
+ });
425
472
  const developerId = await config.getDeveloperId();
426
473
  const containerName = containerHelpers.getContainerName(appName, developerId);
427
474
  try {
428
475
  await execWithDockerEnv(`docker restart ${containerName}`);
429
- await logRestartDevMountSummary(containerName);
476
+ await logRestartDevMountSummary(containerName, appName);
430
477
  } catch (error) {
431
478
  const msg = (error.stderr || error.stdout || error.message || '').toLowerCase();
432
479
  if (msg.includes('no such container') || msg.includes('is not running')) {
@@ -439,6 +486,7 @@ async function restartApp(appName) {
439
486
  module.exports = {
440
487
  runApp,
441
488
  restartApp,
489
+ validateAppForRun,
442
490
  ensureReloadSync,
443
491
  isLocalhostHost,
444
492
  isLocalhostEndpoint,
@@ -96,6 +96,13 @@ function logApplicationSection(a, summary) {
96
96
  } else {
97
97
  logApplicationFields(a);
98
98
  }
99
+ if (summary.runReloadDefault && !summary.isExternal) {
100
+ logger.log(` ${'Run default:'.padEnd(16)} reload on (config)`);
101
+ }
102
+ if (!summary.isExternal) {
103
+ const proxyOn = summary.runProxyDefault === true;
104
+ logger.log(` ${'Run default:'.padEnd(16)} proxy ${proxyOn ? 'on' : 'off'} (config)`);
105
+ }
99
106
  /* External integration is logged after Dataplane block in display() when external */
100
107
  }
101
108
 
package/lib/app/show.js CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  const path = require('path');
16
16
  const logger = require('../utils/logger');
17
- const { detectAppType, resolveApplicationConfigPath } = require('../utils/paths');
17
+ const { detectAppType, resolveApplicationConfigPath, getBuilderPath } = require('../utils/paths');
18
18
  const { loadConfigFile } = require('../utils/config-format');
19
19
  const generator = require('../generator');
20
20
  const { getConfig, normalizeControllerUrl } = require('../core/config');
@@ -38,6 +38,58 @@ const {
38
38
  sanitizeCertificationForJson
39
39
  } = require('./certification-show-enrich');
40
40
 
41
+ /**
42
+ * Attach `runReloadDefault` / `runProxyDefault` from ~/.aifabrix `applications.<appKey>`.
43
+ * @param {Object} summary
44
+ * @param {string} appKey
45
+ * @param {Object} userCfg
46
+ */
47
+ function attachRunDefaultsFromUserConfig(summary, appKey, userCfg) {
48
+ const { isApplicationsReloadDefaultOn, getApplicationsRunProxyHint } = require('../utils/applications-config-defaults');
49
+ if (isApplicationsReloadDefaultOn(userCfg, appKey)) {
50
+ summary.runReloadDefault = true;
51
+ }
52
+ summary.runProxyDefault = getApplicationsRunProxyHint(userCfg, appKey);
53
+ }
54
+
55
+ /**
56
+ * Overlay `application.url` / `application.internalUrl` from declarative url:// rules (same profile as run `.env`, default `docker`).
57
+ * Used for offline and **online** show so controller metadata does not stale local proxy/TLS hints.
58
+ * @param {Object} summary
59
+ * @param {string} appKey
60
+ */
61
+ async function attachDeclarativeUrlsToShowApplication(summary, appKey) {
62
+ if (!summary || summary.isExternal) return;
63
+ const t = summary.application && summary.application.type;
64
+ if (String(t || 'webapp').toLowerCase() === 'external') return;
65
+ try {
66
+ const { resolveDeclarativeShowUrlsForApp } = require('../core/secrets-env-declarative-expand');
67
+ const appPath = getBuilderPath(appKey);
68
+ const variablesPath = resolveApplicationConfigPath(appPath);
69
+ const urls = await resolveDeclarativeShowUrlsForApp(appKey, appPath, variablesPath, 'docker');
70
+ if (!urls) return;
71
+ summary.application.url = urls.publicUrl;
72
+ summary.application.internalUrl = urls.internalUrl;
73
+ } catch {
74
+ /* display-only */
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Local manifest provenance for `--json` (plan 141); null when the app is not on disk locally.
80
+ * @param {string} appKey
81
+ * @returns {Promise<{ tier: string, tierLabel: string, configPath: string }|null>}
82
+ */
83
+ async function tryGetLocalManifestSourceForJson(appKey) {
84
+ try {
85
+ const { getManifestSourcePayload } = require('../utils/manifest-source-emit');
86
+ const { appPath } = await detectAppType(appKey);
87
+ return getManifestSourcePayload(appKey, appPath);
88
+ } catch {
89
+ return { tier: 'unknown', tierLabel: 'unknown', configPath: '' };
90
+ }
91
+ }
92
+
41
93
  /** Truncate deployment key for display */
42
94
  const DEPLOYMENT_KEY_TRUNCATE_LEN = 12;
43
95
 
@@ -651,16 +703,34 @@ async function loadOfflineShowSummary(appKey) {
651
703
  }
652
704
  }
653
705
 
706
+ async function emitShowOfflineManifestLine(appKey, json) {
707
+ if (json) return;
708
+ try {
709
+ const { detectAppType } = require('../utils/paths');
710
+ const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
711
+ const { appPath } = await detectAppType(appKey);
712
+ emitManifestMetadataLineIfTTY(logger, { appKey, appPath, envOnly: false, json: false });
713
+ } catch {
714
+ /* ignore */
715
+ }
716
+ }
717
+
654
718
  async function runOffline(appKey, json, permissionsOnly, verifyCert = false) {
655
719
  const summary = await loadOfflineShowSummary(appKey);
720
+ await emitShowOfflineManifestLine(appKey, json);
721
+ const userCfg = await getConfig();
722
+ attachRunDefaultsFromUserConfig(summary, appKey, userCfg);
723
+ await attachDeclarativeUrlsToShowApplication(summary, appKey);
656
724
 
657
725
  if (json) {
726
+ const manifestSource = await tryGetLocalManifestSourceForJson(appKey);
658
727
  if (permissionsOnly) {
659
728
  const out = {
660
729
  source: summary.source,
661
730
  path: summary.path,
662
731
  appKey: summary.appKey,
663
- permissions: summary.permissions || []
732
+ permissions: summary.permissions || [],
733
+ manifestSource
664
734
  };
665
735
  logger.log(JSON.stringify(out, null, 2));
666
736
  return;
@@ -669,6 +739,9 @@ async function runOffline(appKey, json, permissionsOnly, verifyCert = false) {
669
739
  source: summary.source,
670
740
  path: summary.path,
671
741
  appKey: summary.appKey,
742
+ runReloadDefault: Boolean(summary.runReloadDefault),
743
+ runProxyDefault: Boolean(summary.runProxyDefault),
744
+ manifestSource,
672
745
  application: {
673
746
  ...summary.application,
674
747
  roles: summary.roles,
@@ -717,13 +790,14 @@ async function fetchExternalSystemForOnline(controllerUrl, appKey, authConfig) {
717
790
  }
718
791
  }
719
792
 
720
- function outputOnlineJson(summary, permissionsOnly) {
793
+ function outputOnlineJson(summary, permissionsOnly, manifestSource) {
721
794
  if (permissionsOnly) {
722
795
  const out = {
723
796
  source: summary.source,
724
797
  controllerUrl: summary.controllerUrl,
725
798
  appKey: summary.appKey,
726
- permissions: summary.permissions || []
799
+ permissions: summary.permissions || [],
800
+ manifestSource
727
801
  };
728
802
  logger.log(JSON.stringify(out, null, 2));
729
803
  return;
@@ -733,6 +807,7 @@ function outputOnlineJson(summary, permissionsOnly) {
733
807
  source: summary.source,
734
808
  controllerUrl: summary.controllerUrl,
735
809
  appKey: summary.appKey,
810
+ manifestSource,
736
811
  application: {
737
812
  key: app.key,
738
813
  displayName: app.displayName,
@@ -751,6 +826,10 @@ function outputOnlineJson(summary, permissionsOnly) {
751
826
  }
752
827
  };
753
828
  if (app.version !== undefined && app.version !== null) out.application.version = app.version;
829
+ if (summary.runReloadDefault) {
830
+ out.runReloadDefault = true;
831
+ }
832
+ out.runProxyDefault = Boolean(summary.runProxyDefault);
754
833
  if (summary.externalSystem !== undefined && summary.externalSystem !== null) {
755
834
  out.externalSystem = summary.externalSystem && summary.externalSystem.error
756
835
  ? { error: summary.externalSystem.error }
@@ -787,8 +866,11 @@ async function runOnline(appKey, json, permissionsOnly, verifyCert = false) {
787
866
  token: authConfig.token,
788
867
  controllerUrl: authResult.actualControllerUrl
789
868
  });
869
+ attachRunDefaultsFromUserConfig(summary, appKey, await getConfig());
870
+ await attachDeclarativeUrlsToShowApplication(summary, appKey);
790
871
  if (json) {
791
- outputOnlineJson(summary, permissionsOnly);
872
+ const manifestSource = await tryGetLocalManifestSourceForJson(appKey);
873
+ outputOnlineJson(summary, permissionsOnly, manifestSource);
792
874
  return;
793
875
  }
794
876
  displayShow(summary, { permissionsOnly: !!permissionsOnly });
@@ -196,12 +196,13 @@ async function generateDockerfile(appNameOrPath, language, config, buildConfig =
196
196
 
197
197
  async function postBuildTasks(appName, buildConfig) {
198
198
  try {
199
- const envPath = await secrets.generateEnvFile(appName, buildConfig.secrets, 'docker');
200
- logger.log(formatSuccessLine(`Generated .env file: ${envPath}`));
201
- // Note: processEnvVariables is already called by generateEnvFile to generate local .env
202
- // at the envOutputPath, so we don't need to manually copy the docker .env file
199
+ // Validate that env.template + secrets resolve cleanly, but never write <appPath>/.env or
200
+ // envOutputPath. Build args still flow through resolveAndGetEnvMap (in-memory). Run
201
+ // `aifabrix resolve <app>` to materialize an on-disk .env.
202
+ await secrets.generateEnvFile(appName, buildConfig.secrets, 'docker', false, { noWrite: true });
203
+ logger.log(formatSuccessLine('Env resolution validated (in-memory only; run "aifabrix resolve ' + appName + '" for on-disk .env)'));
203
204
  } catch (error) {
204
- logger.log(formatWarningLine(`Could not generate .env file: ${error.message}`));
205
+ logger.log(formatWarningLine(`Could not resolve env: ${error.message}`));
205
206
  }
206
207
  }
207
208
 
@@ -411,6 +412,9 @@ async function resolveDockerBuildArgsForApp(appName) {
411
412
  */
412
413
  async function runStandardDockerBuild(appName, options) {
413
414
  const { buildConfig } = await buildHelpers.loadAndValidateConfig(appName);
415
+ const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
416
+ const { appPath } = await detectAppType(appName);
417
+ emitManifestMetadataLineIfTTY(logger, { appKey: appName, appPath, envOnly: false, json: false });
414
418
  const { devDir, effectiveImageName, imageName, appConfig } = await prepareDevDirectory(appName, buildConfig, options);
415
419
  const contextPath = prepareBuildContext(buildConfig, devDir);
416
420
  const dockerfilePath = await handleDockerfileGeneration(appName, {
package/lib/cli/index.js CHANGED
@@ -22,7 +22,8 @@ const { setupExternalSystemCommands } = require('./setup-external-system');
22
22
  const { setupAppCommands: setupAppManagementCommands } = require('../commands/app');
23
23
  const { setupDatasourceCommands } = require('../commands/datasource');
24
24
  const { setupDimensionCommands } = require('../commands/dimension');
25
- const { setupDimensionValueCommands } = require('../commands/dimension-value');
25
+ const { setupIdentityCommands } = require('../commands/identity');
26
+ const { setupProtectionCommands } = require('../commands/protection');
26
27
  const { setupCredentialDeploymentCommands } = require('./setup-credential-deployment');
27
28
  const { setupIntegrationClientCommands } = require('./setup-integration-client');
28
29
  const { setupPlatformCommands } = require('./setup-platform');
@@ -39,7 +40,8 @@ function setupCommands(program) {
39
40
  setupAppManagementCommands(program);
40
41
  setupDatasourceCommands(program);
41
42
  setupDimensionCommands(program);
42
- setupDimensionValueCommands(program);
43
+ setupIdentityCommands(program);
44
+ setupProtectionCommands(program);
43
45
  setupUtilityCommands(program);
44
46
  setupCredentialDeploymentCommands(program);
45
47
  setupIntegrationClientCommands(program);