@aifabrix/builder 2.44.5 → 2.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/.cursor/rules/cli-layout.mdc +8 -4
  2. package/.cursor/rules/project-rules.mdc +1 -1
  3. package/README.md +15 -23
  4. package/integration/hubspot-test/README.md +2 -0
  5. package/integration/hubspot-test/test.js +5 -3
  6. package/jest.projects.js +104 -2
  7. package/lib/api/controller-health.api.js +49 -0
  8. package/lib/api/dimension-values.api.js +82 -0
  9. package/lib/api/dimensions.api.js +114 -0
  10. package/lib/api/external-systems.api.js +1 -0
  11. package/lib/api/integration-clients.api.js +168 -0
  12. package/lib/api/types/dimension-values.types.js +28 -0
  13. package/lib/api/types/dimensions.types.js +31 -0
  14. package/lib/api/types/integration-clients.types.js +45 -0
  15. package/lib/api/validation-runner.js +46 -25
  16. package/lib/app/deploy-config.js +11 -1
  17. package/lib/app/deploy-status-display.js +3 -3
  18. package/lib/app/deploy.js +36 -14
  19. package/lib/app/display.js +15 -11
  20. package/lib/app/helpers.js +3 -3
  21. package/lib/app/index.js +3 -3
  22. package/lib/app/push.js +46 -23
  23. package/lib/app/register.js +7 -6
  24. package/lib/app/restart-display.js +126 -0
  25. package/lib/app/rotate-secret.js +7 -6
  26. package/lib/app/run-container-start.js +12 -6
  27. package/lib/app/run-env-compose.js +30 -1
  28. package/lib/app/run-helpers.js +58 -19
  29. package/lib/app/run-reload-sync.js +148 -0
  30. package/lib/app/run-resolve-image.js +51 -1
  31. package/lib/app/run.js +148 -74
  32. package/lib/app/show-display.js +7 -0
  33. package/lib/app/show.js +87 -5
  34. package/lib/build/index.js +83 -49
  35. package/lib/cli/doctor-check.js +117 -0
  36. package/lib/cli/index.js +8 -2
  37. package/lib/cli/infra-guided.js +460 -0
  38. package/lib/cli/installation-log-command.js +73 -0
  39. package/lib/cli/setup-app.js +31 -3
  40. package/lib/cli/setup-auth.js +98 -27
  41. package/lib/cli/setup-dev-path-commands.js +50 -3
  42. package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
  43. package/lib/cli/setup-infra-up-platform-action.js +131 -0
  44. package/lib/cli/setup-infra.js +132 -118
  45. package/lib/cli/setup-integration-client.js +182 -0
  46. package/lib/cli/setup-parameters.js +21 -2
  47. package/lib/cli/setup-platform.js +102 -0
  48. package/lib/cli/setup-secrets.js +18 -6
  49. package/lib/cli/setup-utility-resolve.js +132 -0
  50. package/lib/cli/setup-utility.js +143 -84
  51. package/lib/commands/app-logs.js +81 -33
  52. package/lib/commands/auth-config.js +116 -18
  53. package/lib/commands/datasource-capability-dimension-cli.js +128 -0
  54. package/lib/commands/datasource-capability-output.js +29 -0
  55. package/lib/commands/datasource-capability-relate-cli.js +140 -0
  56. package/lib/commands/datasource-capability.js +411 -0
  57. package/lib/commands/datasource-unified-test-cli.options.js +1 -1
  58. package/lib/commands/datasource.js +53 -13
  59. package/lib/commands/dev-down.js +3 -3
  60. package/lib/commands/dev-infra-gate.js +32 -0
  61. package/lib/commands/dev-init.js +13 -7
  62. package/lib/commands/dimension-value.js +179 -0
  63. package/lib/commands/dimension.js +330 -0
  64. package/lib/commands/integration-client.js +430 -0
  65. package/lib/commands/login-device.js +65 -30
  66. package/lib/commands/login.js +21 -10
  67. package/lib/commands/parameters-validate.js +78 -13
  68. package/lib/commands/repair-datasource-auto-rbac.js +166 -0
  69. package/lib/commands/repair-datasource-keys.js +10 -5
  70. package/lib/commands/repair-datasource.js +19 -7
  71. package/lib/commands/repair-env-template.js +4 -1
  72. package/lib/commands/repair-openapi-sync.js +172 -0
  73. package/lib/commands/repair-persist.js +102 -0
  74. package/lib/commands/repair-rbac-extract.js +27 -0
  75. package/lib/commands/repair-rbac-migrate.js +186 -0
  76. package/lib/commands/repair-rbac.js +214 -31
  77. package/lib/commands/repair-system-alignment.js +246 -0
  78. package/lib/commands/repair-system-permissions.js +168 -0
  79. package/lib/commands/repair.js +120 -338
  80. package/lib/commands/secure.js +1 -1
  81. package/lib/commands/setup-modes.js +468 -0
  82. package/lib/commands/setup-prompts.js +421 -0
  83. package/lib/commands/setup.js +254 -0
  84. package/lib/commands/teardown.js +277 -0
  85. package/lib/commands/up-common.js +113 -19
  86. package/lib/commands/up-dataplane.js +44 -19
  87. package/lib/commands/up-miso.js +18 -18
  88. package/lib/commands/upload.js +111 -23
  89. package/lib/commands/wizard-core-helpers.js +14 -11
  90. package/lib/commands/wizard-core.js +6 -5
  91. package/lib/commands/wizard-dataplane.js +2 -2
  92. package/lib/commands/wizard-entity-selection.js +4 -3
  93. package/lib/commands/wizard-headless.js +2 -1
  94. package/lib/commands/wizard.js +2 -1
  95. package/lib/constants/infra-compose-service-names.js +40 -0
  96. package/lib/core/audit-logger.js +1 -34
  97. package/lib/core/config-admin-email.js +56 -0
  98. package/lib/core/config-normalize.js +60 -0
  99. package/lib/core/config-registered-controller-urls.js +54 -0
  100. package/lib/core/config.js +33 -50
  101. package/lib/core/env-reader.js +16 -3
  102. package/lib/core/secrets-admin-env.js +101 -0
  103. package/lib/core/secrets-ensure-infra.js +34 -1
  104. package/lib/core/secrets-ensure.js +88 -66
  105. package/lib/core/secrets-env-content.js +428 -0
  106. package/lib/core/secrets-env-declarative-expand.js +170 -0
  107. package/lib/core/secrets-env-write.js +29 -1
  108. package/lib/core/secrets-load.js +252 -0
  109. package/lib/core/secrets-names.js +32 -0
  110. package/lib/core/secrets.js +17 -757
  111. package/lib/datasource/capability/basic-exposure.js +76 -0
  112. package/lib/datasource/capability/capability-diff-slice.js +41 -0
  113. package/lib/datasource/capability/capability-key.js +34 -0
  114. package/lib/datasource/capability/capability-resolve.js +172 -0
  115. package/lib/datasource/capability/capability-storage-keys.js +22 -0
  116. package/lib/datasource/capability/copy-operations.js +348 -0
  117. package/lib/datasource/capability/copy-test-payload.js +139 -0
  118. package/lib/datasource/capability/create-operations.js +235 -0
  119. package/lib/datasource/capability/dimension-operations.js +151 -0
  120. package/lib/datasource/capability/dimension-validate.js +219 -0
  121. package/lib/datasource/capability/json-pointer.js +31 -0
  122. package/lib/datasource/capability/reference-rewrite.js +51 -0
  123. package/lib/datasource/capability/relate-operations.js +325 -0
  124. package/lib/datasource/capability/relate-validate.js +219 -0
  125. package/lib/datasource/capability/remove-operations.js +275 -0
  126. package/lib/datasource/capability/run-capability-copy.js +152 -0
  127. package/lib/datasource/capability/run-capability-diff.js +135 -0
  128. package/lib/datasource/capability/run-capability-dimension.js +291 -0
  129. package/lib/datasource/capability/run-capability-edit.js +377 -0
  130. package/lib/datasource/capability/run-capability-relate.js +193 -0
  131. package/lib/datasource/capability/run-capability-remove.js +105 -0
  132. package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
  133. package/lib/datasource/capability/validate-capability-slice.js +35 -0
  134. package/lib/datasource/list.js +136 -23
  135. package/lib/datasource/log-viewer.js +2 -4
  136. package/lib/datasource/unified-validation-run.js +51 -16
  137. package/lib/datasource/validate.js +53 -1
  138. package/lib/deployment/deploy-poll-ui.js +60 -0
  139. package/lib/deployment/deployer-status.js +29 -3
  140. package/lib/deployment/deployer.js +48 -30
  141. package/lib/deployment/environment.js +7 -2
  142. package/lib/deployment/poll-interval.js +72 -0
  143. package/lib/deployment/push.js +11 -9
  144. package/lib/external-system/deploy.js +9 -2
  145. package/lib/external-system/download.js +61 -32
  146. package/lib/external-system/sync-deploy-manifest.js +33 -0
  147. package/lib/infrastructure/index.js +49 -19
  148. package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
  149. package/lib/internal/node-fs.js +2 -0
  150. package/lib/parameters/infra-kv-discovery.js +29 -4
  151. package/lib/parameters/infra-parameter-catalog.js +6 -3
  152. package/lib/parameters/infra-parameter-validate.js +67 -19
  153. package/lib/resolvers/datasource-resolver.js +53 -0
  154. package/lib/resolvers/dimension-file.js +52 -0
  155. package/lib/resolvers/manifest-resolver.js +133 -0
  156. package/lib/schema/application-schema.json +4 -0
  157. package/lib/schema/external-datasource.schema.json +183 -53
  158. package/lib/schema/external-system.schema.json +23 -10
  159. package/lib/schema/infra.parameter.yaml +26 -1
  160. package/lib/schema/wizard-config.schema.json +1 -1
  161. package/lib/utils/aifabrix-config-dir-walk.js +40 -0
  162. package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
  163. package/lib/utils/app-config-resolver.js +24 -1
  164. package/lib/utils/app-run-containers.js +2 -2
  165. package/lib/utils/applications-config-defaults.js +206 -0
  166. package/lib/utils/auth-config-validator.js +2 -12
  167. package/lib/utils/bash-secret-env.js +59 -0
  168. package/lib/utils/cli-secrets-error-format.js +78 -0
  169. package/lib/utils/cli-test-layout-chalk.js +31 -9
  170. package/lib/utils/cli-utils.js +4 -36
  171. package/lib/utils/compose-generate-docker-compose.js +111 -6
  172. package/lib/utils/compose-generator.js +17 -8
  173. package/lib/utils/controller-url.js +50 -7
  174. package/lib/utils/datasource-test-run-display.js +8 -0
  175. package/lib/utils/dev-hosts-helper.js +3 -2
  176. package/lib/utils/dev-init-ssh-merge.js +2 -1
  177. package/lib/utils/docker-build.js +17 -9
  178. package/lib/utils/docker-reload-mount.js +127 -0
  179. package/lib/utils/env-copy.js +99 -14
  180. package/lib/utils/env-template.js +5 -1
  181. package/lib/utils/external-readme.js +71 -2
  182. package/lib/utils/external-system-local-test-tty.js +3 -2
  183. package/lib/utils/external-system-readiness-core.js +45 -12
  184. package/lib/utils/external-system-readiness-deploy-display.js +3 -3
  185. package/lib/utils/external-system-readiness-display-internals.js +33 -3
  186. package/lib/utils/external-system-readiness-display.js +10 -1
  187. package/lib/utils/file-upload.js +40 -3
  188. package/lib/utils/health-check-db-init.js +107 -0
  189. package/lib/utils/health-check-public-warn.js +69 -0
  190. package/lib/utils/health-check-url.js +28 -10
  191. package/lib/utils/health-check.js +139 -107
  192. package/lib/utils/help-builder.js +5 -1
  193. package/lib/utils/image-name.js +34 -7
  194. package/lib/utils/infra-optional-service-flags.js +69 -0
  195. package/lib/utils/installation-log-core.js +282 -0
  196. package/lib/utils/installation-log-record.js +237 -0
  197. package/lib/utils/installation-log.js +123 -0
  198. package/lib/utils/integration-file-backup.js +74 -0
  199. package/lib/utils/log-redaction.js +105 -0
  200. package/lib/utils/manifest-location.js +164 -0
  201. package/lib/utils/manifest-source-emit.js +162 -0
  202. package/lib/utils/mutagen-install.js +30 -3
  203. package/lib/utils/paths.js +308 -76
  204. package/lib/utils/postgres-wipe.js +212 -0
  205. package/lib/utils/register-aifabrix-shell-env.js +15 -0
  206. package/lib/utils/remote-dev-auth.js +21 -5
  207. package/lib/utils/remote-docker-env.js +9 -1
  208. package/lib/utils/remote-secrets-loader.js +49 -4
  209. package/lib/utils/resolve-docker-image-ref.js +9 -3
  210. package/lib/utils/run-cli-flags.js +29 -0
  211. package/lib/utils/secrets-ancestor-paths.js +47 -0
  212. package/lib/utils/secrets-canonical.js +10 -3
  213. package/lib/utils/secrets-helpers.js +17 -10
  214. package/lib/utils/secrets-kv-refs.js +42 -0
  215. package/lib/utils/secrets-kv-scope.js +19 -2
  216. package/lib/utils/secrets-materialize-local.js +134 -0
  217. package/lib/utils/secrets-path.js +26 -13
  218. package/lib/utils/secrets-utils.js +20 -10
  219. package/lib/utils/system-builder-root.js +42 -0
  220. package/lib/utils/url-declarative-public-base.js +80 -12
  221. package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
  222. package/lib/utils/url-declarative-resolve-build.js +24 -388
  223. package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
  224. package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
  225. package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
  226. package/lib/utils/url-declarative-resolve.js +47 -7
  227. package/lib/utils/url-declarative-runtime-base-path.js +52 -0
  228. package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
  229. package/lib/utils/urls-local-registry-scan.js +103 -0
  230. package/lib/utils/urls-local-registry.js +158 -76
  231. package/lib/utils/validation-poll-ui.js +81 -0
  232. package/lib/utils/validation-run-poll.js +29 -5
  233. package/lib/utils/with-muted-logger.js +53 -0
  234. package/package.json +3 -1
  235. package/templates/applications/dataplane/application.yaml +5 -1
  236. package/templates/applications/dataplane/rbac.yaml +10 -10
  237. package/templates/applications/keycloak/env.template +8 -6
  238. package/templates/applications/miso-controller/application.yaml +9 -0
  239. package/templates/applications/miso-controller/env.template +27 -29
  240. package/templates/applications/miso-controller/rbac.yaml +9 -9
  241. package/templates/external-system/README.md.hbs +83 -123
  242. package/.npmrc.token +0 -1
  243. package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  244. package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  245. package/.nyc_output/processinfo/index.json +0 -1
  246. package/lib/api/service-users.api.js +0 -150
  247. package/lib/api/types/service-users.types.js +0 -65
  248. package/lib/cli/setup-service-user.js +0 -187
  249. package/lib/commands/service-user.js +0 -429
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 });
@@ -1,4 +1,13 @@
1
- const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
+ const {
2
+ formatSuccessLine,
3
+ formatSuccessParagraph,
4
+ formatProgress,
5
+ formatWarningLine,
6
+ sectionTitle,
7
+ headerKeyValue,
8
+ metadata,
9
+ formatNextActions
10
+ } = require('../utils/cli-test-layout-chalk');
2
11
  /**
3
12
  * AI Fabrix Builder Build Functions
4
13
  *
@@ -18,7 +27,6 @@ const paths = require('../utils/paths');
18
27
  const { detectAppType, getProjectRoot } = require('../utils/paths');
19
28
  const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
20
29
  const { loadConfigFile } = require('../utils/config-format');
21
- const chalk = require('chalk');
22
30
  const secrets = require('../core/secrets');
23
31
  const config = require('../core/config');
24
32
  const logger = require('../utils/logger');
@@ -28,6 +36,7 @@ const buildCopy = require('../utils/build-copy');
28
36
  const { buildDevImageName } = require('../utils/image-name');
29
37
  const buildHelpers = require('../utils/build-helpers');
30
38
  const secretsEnvWrite = require('../core/secrets-env-write');
39
+ const { ensureRunSecretsForApp } = require('../app/run-env-compose');
31
40
 
32
41
  /**
33
42
  * Loads application config for an application
@@ -187,12 +196,13 @@ async function generateDockerfile(appNameOrPath, language, config, buildConfig =
187
196
 
188
197
  async function postBuildTasks(appName, buildConfig) {
189
198
  try {
190
- const envPath = await secrets.generateEnvFile(appName, buildConfig.secrets, 'docker');
191
- logger.log(formatSuccessLine(`Generated .env file: ${envPath}`));
192
- // Note: processEnvVariables is already called by generateEnvFile to generate local .env
193
- // 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)'));
194
204
  } catch (error) {
195
- logger.log(chalk.yellow(`⚠ Warning: Could not generate .env file: ${error.message}`));
205
+ logger.log(formatWarningLine(`Could not resolve env: ${error.message}`));
196
206
  }
197
207
  }
198
208
 
@@ -205,8 +215,11 @@ async function postBuildTasks(appName, buildConfig) {
205
215
  async function checkExternalAppType(appName) {
206
216
  const variables = await loadVariablesYaml(appName);
207
217
  if (variables.app && variables.app.type === 'external') {
208
- logger.log(chalk.blue(`External system: ${appName}`));
209
- logger.log(chalk.gray('To regenerate deployment JSON, run: aifabrix json ' + appName));
218
+ logger.log('');
219
+ logger.log(sectionTitle('External integration'));
220
+ logger.log(headerKeyValue('Application:', appName));
221
+ logger.log(metadata('No Docker image is built for external systems.'));
222
+ logger.log(formatNextActions([`aifabrix json ${appName}`]));
210
223
  return true;
211
224
  }
212
225
  return false;
@@ -269,9 +282,9 @@ async function prepareDevDirectory(appName, buildConfig, options) {
269
282
  const developerId = await config.getDeveloperId();
270
283
  const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
271
284
  const directoryName = idNum === 0 ? 'applications' : `dev-${developerId}`;
272
- logger.log(chalk.blue(`Copying files to developer-specific directory (${directoryName})...`));
285
+ logger.log(formatProgress(`Copying workspace (${directoryName})…`));
273
286
  const devDir = await buildCopy.copyBuilderToDevDirectory(appName, developerId);
274
- logger.log(formatSuccessLine(`Files copied to: ${devDir}`));
287
+ logger.log(formatSuccessLine(`Workspace ready: ${devDir}`));
275
288
 
276
289
  const { config: appConfig, imageName } = await buildHelpers.loadAndValidateConfig(appName);
277
290
  const effectiveImageName = buildDevImageName(imageName, developerId);
@@ -297,8 +310,11 @@ function prepareBuildContext(buildConfig, devDir) {
297
310
  // Check if context is using old format (../appName) - these are incompatible with dev directory structure
298
311
  if (buildConfig.context && buildConfig.context.startsWith('../') && buildConfig.context !== '../..') {
299
312
  // Old format detected - always use devDir instead
300
- logger.log(chalk.yellow(`⚠ Warning: Build context uses old format: ${buildConfig.context}`));
301
- logger.log(chalk.yellow(` Using dev directory instead: ${devDir}`));
313
+ logger.log(
314
+ formatWarningLine(
315
+ `Build context uses legacy path (${buildConfig.context}). Using dev workspace: ${devDir}`
316
+ )
317
+ );
302
318
  contextPath = devDir;
303
319
  } else if (buildConfig.context && buildConfig.context !== '../..') {
304
320
  // Resolve relative context path from dev directory
@@ -370,6 +386,56 @@ async function handleDockerfileGeneration(appName, params, options, buildHelpers
370
386
  }, generateDockerfile);
371
387
  }
372
388
 
389
+ /**
390
+ * Resolve Docker build-args from resolved env map and merged BASH_* secrets.
391
+ * @param {string} appName
392
+ * @returns {Promise<Object.<string, string>>}
393
+ */
394
+ async function resolveDockerBuildArgsForApp(appName) {
395
+ const envMap = await secretsEnvWrite.resolveAndGetEnvMap(appName, { environment: 'docker' });
396
+ const { getBashPrefixedProcessEnvOverlay } = require('../utils/bash-secret-env');
397
+ const bashOverlay = await getBashPrefixedProcessEnvOverlay(null, appName);
398
+ const buildArgs = {};
399
+ if (envMap.NPM_TOKEN) buildArgs.NPM_TOKEN = envMap.NPM_TOKEN;
400
+ if (envMap.PYPI_TOKEN) buildArgs.PYPI_TOKEN = envMap.PYPI_TOKEN;
401
+ for (const [k, v] of Object.entries(bashOverlay)) {
402
+ if (v && buildArgs[k] === undefined) buildArgs[k] = v;
403
+ }
404
+ return buildArgs;
405
+ }
406
+
407
+ /**
408
+ * Standard Docker build path after CLI header is printed (non-external apps).
409
+ * @param {string} appName
410
+ * @param {Object} options
411
+ * @returns {Promise<string>} Built image ref e.g. name:tag
412
+ */
413
+ async function runStandardDockerBuild(appName, options) {
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 });
418
+ const { devDir, effectiveImageName, imageName, appConfig } = await prepareDevDirectory(appName, buildConfig, options);
419
+ const contextPath = prepareBuildContext(buildConfig, devDir);
420
+ const dockerfilePath = await handleDockerfileGeneration(appName, {
421
+ devDir,
422
+ buildConfig,
423
+ contextPath,
424
+ appConfig
425
+ }, options, buildHelpers);
426
+ await ensureRunSecretsForApp(appName);
427
+ const buildArgs = await resolveDockerBuildArgsForApp(appName);
428
+ const buildOptions = { ...options, buildArgs };
429
+ const tag = options.tag || 'latest';
430
+ if (typeof tag === 'string' && tag.includes(',')) {
431
+ throw new Error('Use a single image tag per build (comma-separated multiple tags are not supported).');
432
+ }
433
+ await dockerBuild.executeDockerBuildWithTag(effectiveImageName, imageName, dockerfilePath, contextPath, tag, buildOptions);
434
+ await postBuildTasks(appName, buildConfig);
435
+ logger.log(formatSuccessParagraph('Build completed successfully!'));
436
+ return `${effectiveImageName}:${tag}`;
437
+ }
438
+
373
439
  /**
374
440
  * Builds a container image for the specified application
375
441
  * Auto-detects runtime and generates Dockerfile if needed
@@ -395,42 +461,10 @@ async function buildApp(appName, options = {}) {
395
461
  }
396
462
 
397
463
  try {
398
- logger.log(chalk.blue(`\n🔨 Building application: ${appName}`));
399
-
400
- // 1. Load and validate configuration
401
- const { buildConfig } = await buildHelpers.loadAndValidateConfig(appName);
402
-
403
- // 2. Prepare dev directory and copy files
404
- const { devDir, effectiveImageName, imageName, appConfig } = await prepareDevDirectory(appName, buildConfig, options);
405
-
406
- // 3. Prepare build context
407
- const contextPath = prepareBuildContext(buildConfig, devDir);
408
-
409
- // 4. Handle Dockerfile generation
410
- const dockerfilePath = await handleDockerfileGeneration(appName, {
411
- devDir,
412
- buildConfig,
413
- contextPath,
414
- appConfig
415
- }, options, buildHelpers);
416
-
417
- // 5. Resolve NPM_TOKEN/PYPI_TOKEN for private registries and pass as build-args
418
- const envMap = await secretsEnvWrite.resolveAndGetEnvMap(appName, { environment: 'docker' });
419
- const buildArgs = {};
420
- if (envMap.NPM_TOKEN) buildArgs.NPM_TOKEN = envMap.NPM_TOKEN;
421
- if (envMap.PYPI_TOKEN) buildArgs.PYPI_TOKEN = envMap.PYPI_TOKEN;
422
- const buildOptions = { ...options, buildArgs };
423
-
424
- // 6. Execute Docker build
425
- const tag = options.tag || 'latest';
426
- await dockerBuild.executeDockerBuildWithTag(effectiveImageName, imageName, dockerfilePath, contextPath, tag, buildOptions);
427
-
428
- // 7. Post-build tasks
429
- await postBuildTasks(appName, buildConfig);
430
-
431
- logger.log(formatSuccessParagraph('Build completed successfully!'));
432
- return `${imageName}:${tag}`;
433
-
464
+ logger.log('');
465
+ logger.log(sectionTitle('Build'));
466
+ logger.log(headerKeyValue('Application:', appName));
467
+ return await runStandardDockerBuild(appName, options);
434
468
  } catch (error) {
435
469
  throw new Error(`Build failed: ${error.message}`);
436
470
  }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * @fileoverview `aifabrix doctor` action: environment validation + optional infra health.
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const {
9
+ sectionTitle,
10
+ formatBulletSection,
11
+ formatDatasourceListRow,
12
+ formatWarningLine,
13
+ metadata
14
+ } = require('../utils/cli-test-layout-chalk');
15
+ const validator = require('../validation/validator');
16
+ const config = require('../core/config');
17
+ const infra = require('../infrastructure');
18
+ const logger = require('../utils/logger');
19
+
20
+ /**
21
+ * @param {string} label
22
+ * @param {'ok'|'warning'|'fail'} variant
23
+ * @param {{ ok?: string, warn?: string, fail?: string }} text
24
+ * @returns {string}
25
+ */
26
+ function formatDoctorEnvSummaryLine(label, variant, text) {
27
+ const base = chalk.gray(`${label}:`);
28
+ if (variant === 'ok') return `${base} ${chalk.green('✔')} ${chalk.white(text.ok || '')}`;
29
+ if (variant === 'warning') return `${base} ${chalk.yellow('⚠')} ${chalk.white(text.warn || '')}`;
30
+ return `${base} ${chalk.red('✖')} ${chalk.white(text.fail || '')}`;
31
+ }
32
+
33
+ /**
34
+ * @param {string} statusRaw
35
+ * @returns {'ok'|'warn'|'fail'}
36
+ */
37
+ function doctorInfraRowAggregate(statusRaw) {
38
+ const s = String(statusRaw).trim().toLowerCase();
39
+ if (s === 'healthy') return 'ok';
40
+ if (s === 'unknown') return 'warn';
41
+ return 'fail';
42
+ }
43
+
44
+ /**
45
+ * @param {Object} result - `validator.checkEnvironment()` payload
46
+ */
47
+ function logDoctorEnvironmentSection(result) {
48
+ logger.log('');
49
+ logger.log(sectionTitle('Environment check'));
50
+ logger.log('');
51
+ logger.log(
52
+ formatDoctorEnvSummaryLine(
53
+ 'Docker',
54
+ result.docker === 'ok' ? 'ok' : 'fail',
55
+ { ok: 'Running', fail: 'Not available' }
56
+ )
57
+ );
58
+ logger.log(
59
+ formatDoctorEnvSummaryLine(
60
+ 'Ports',
61
+ result.ports === 'ok' ? 'ok' : 'warning',
62
+ { ok: 'Available', warn: 'Some ports in use' }
63
+ )
64
+ );
65
+ logger.log(
66
+ formatDoctorEnvSummaryLine(
67
+ 'Secrets',
68
+ result.secrets === 'ok' ? 'ok' : 'fail',
69
+ { ok: 'Configured', fail: 'Missing' }
70
+ )
71
+ );
72
+ if (result.recommendations.length > 0) {
73
+ logger.log('');
74
+ logger.log(formatBulletSection('Recommendations:', result.recommendations));
75
+ }
76
+ }
77
+
78
+ /**
79
+ * @param {Object} result - `validator.checkEnvironment()` payload
80
+ */
81
+ async function logDoctorInfraHealthSection(result) {
82
+ if (result.docker !== 'ok') {
83
+ logger.log('');
84
+ logger.log(metadata('Infrastructure health skipped (Docker not available).'));
85
+ return;
86
+ }
87
+ try {
88
+ const cfg = await config.getConfig();
89
+ const health = await infra.checkInfraHealth(null, {
90
+ pgadmin: cfg.pgadmin !== false,
91
+ redisCommander: cfg.redisCommander !== false,
92
+ traefik: !!cfg.traefik
93
+ });
94
+ logger.log('');
95
+ logger.log(sectionTitle('Infrastructure health'));
96
+ Object.entries(health).forEach(([service, status]) => {
97
+ const agg = doctorInfraRowAggregate(status);
98
+ logger.log(formatDatasourceListRow(agg, `${service}: ${status}`, null));
99
+ });
100
+ } catch (_err) {
101
+ logger.log('');
102
+ logger.log(formatWarningLine('Infrastructure is not running or health could not be read.'));
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Runs the doctor checks and prints the TTY summary.
108
+ * @returns {Promise<void>}
109
+ */
110
+ async function runDoctorCheck() {
111
+ const result = await validator.checkEnvironment();
112
+ logDoctorEnvironmentSection(result);
113
+ await logDoctorInfraHealthSection(result);
114
+ logger.log('');
115
+ }
116
+
117
+ module.exports = { runDoctorCheck };
package/lib/cli/index.js CHANGED
@@ -21,8 +21,11 @@ const { setupParametersCommands } = require('./setup-parameters');
21
21
  const { setupExternalSystemCommands } = require('./setup-external-system');
22
22
  const { setupAppCommands: setupAppManagementCommands } = require('../commands/app');
23
23
  const { setupDatasourceCommands } = require('../commands/datasource');
24
+ const { setupDimensionCommands } = require('../commands/dimension');
25
+ const { setupDimensionValueCommands } = require('../commands/dimension-value');
24
26
  const { setupCredentialDeploymentCommands } = require('./setup-credential-deployment');
25
- const { setupServiceUserCommands } = require('./setup-service-user');
27
+ const { setupIntegrationClientCommands } = require('./setup-integration-client');
28
+ const { setupPlatformCommands } = require('./setup-platform');
26
29
 
27
30
  /**
28
31
  * Sets up all CLI commands on the Commander program instance
@@ -35,13 +38,16 @@ function setupCommands(program) {
35
38
  setupEnvironmentCommands(program);
36
39
  setupAppManagementCommands(program);
37
40
  setupDatasourceCommands(program);
41
+ setupDimensionCommands(program);
42
+ setupDimensionValueCommands(program);
38
43
  setupUtilityCommands(program);
39
44
  setupCredentialDeploymentCommands(program);
40
- setupServiceUserCommands(program);
45
+ setupIntegrationClientCommands(program);
41
46
  setupExternalSystemCommands(program);
42
47
  setupDevCommands(program);
43
48
  setupSecretsCommands(program);
44
49
  setupParametersCommands(program);
50
+ setupPlatformCommands(program);
45
51
  }
46
52
 
47
53
  module.exports = {