@aifabrix/builder 2.45.0 → 2.46.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 (352) hide show
  1. package/.cursor/rules/cli-layout.mdc +2 -0
  2. package/.cursor/rules/cli-product-neutral.mdc +43 -0
  3. package/.cursor/rules/docs-rules.mdc +3 -1
  4. package/.cursor/rules/project-rules.mdc +1 -0
  5. package/README.md +43 -17
  6. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
  7. package/jest.config.default.js +2 -1
  8. package/jest.config.isolated.js +2 -1
  9. package/jest.global-live-fabrix-hooks.js +36 -0
  10. package/jest.isolated-projects.js +130 -0
  11. package/jest.projects.js +69 -121
  12. package/lib/api/agent-metadata-validation.api.js +93 -0
  13. package/lib/api/api-response-errors.js +124 -0
  14. package/lib/api/audit-api-helpers.js +68 -0
  15. package/lib/api/audit.api.js +119 -0
  16. package/lib/api/auth-cache.api.js +44 -0
  17. package/lib/api/certificates.api.js +4 -4
  18. package/lib/api/credential.api.js +2 -2
  19. package/lib/api/dataplane-health.api.js +137 -0
  20. package/lib/api/dataplane-sync.api.js +27 -0
  21. package/lib/api/datasources-core.api.js +21 -18
  22. package/lib/api/datasources-extended.api.js +22 -22
  23. package/lib/api/dimensions.api.js +3 -0
  24. package/lib/api/external-systems.api.js +13 -13
  25. package/lib/api/governance-scenario-pack.api.js +159 -0
  26. package/lib/api/governance-scenarios.api.js +39 -0
  27. package/lib/api/groups.api.js +79 -0
  28. package/lib/api/index.js +88 -5
  29. package/lib/api/lifecycle.api.js +77 -0
  30. package/lib/api/pipeline.api.js +7 -5
  31. package/lib/api/protection.api.js +157 -0
  32. package/lib/api/records-bulk.api.js +43 -0
  33. package/lib/api/records-search-parse.js +49 -0
  34. package/lib/api/records-search.api.js +29 -0
  35. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  36. package/lib/api/types/audit.types.js +72 -0
  37. package/lib/api/types/dataplane-health.types.js +27 -0
  38. package/lib/api/types/dimensions.types.js +2 -0
  39. package/lib/api/types/governance-scenarios.types.js +39 -0
  40. package/lib/api/types/protection.types.js +69 -0
  41. package/lib/api/types/records-bulk.types.js +35 -0
  42. package/lib/api/types/records-search.types.js +28 -0
  43. package/lib/api/user-groups.api.js +50 -0
  44. package/lib/api/users.api.js +95 -0
  45. package/lib/api/validation-run.api.js +3 -3
  46. package/lib/api/validation-runner.js +5 -3
  47. package/lib/api/wizard-platform.api.js +3 -3
  48. package/lib/api/wizard.api.js +21 -21
  49. package/lib/app/deploy-config.js +4 -1
  50. package/lib/app/deploy.js +34 -19
  51. package/lib/app/down.js +20 -1
  52. package/lib/app/push.js +4 -5
  53. package/lib/app/register.js +7 -4
  54. package/lib/app/run-env-compose.js +84 -30
  55. package/lib/build/index.js +5 -0
  56. package/lib/certification/sync-system-certification.js +13 -6
  57. package/lib/cli/index.js +8 -2
  58. package/lib/cli/infra-guided-auth.js +109 -0
  59. package/lib/cli/infra-guided.js +91 -69
  60. package/lib/cli/setup-app.help.js +19 -2
  61. package/lib/cli/setup-app.js +58 -23
  62. package/lib/cli/setup-app.test-commands.js +28 -21
  63. package/lib/cli/setup-auth.js +55 -3
  64. package/lib/cli/setup-certification-commands.js +147 -0
  65. package/lib/cli/setup-certification.help.js +141 -0
  66. package/lib/cli/setup-environment.js +19 -2
  67. package/lib/cli/setup-external-system-help.js +62 -0
  68. package/lib/cli/setup-external-system.js +62 -6
  69. package/lib/cli/setup-infra.js +9 -1
  70. package/lib/cli/setup-platform.js +6 -1
  71. package/lib/cli/setup-secrets.js +33 -1
  72. package/lib/cli/setup-utility-help-after.js +65 -0
  73. package/lib/cli/setup-utility-repair.js +198 -0
  74. package/lib/cli/setup-utility-resolve.js +42 -6
  75. package/lib/cli/setup-utility-validate.js +197 -0
  76. package/lib/cli/setup-utility.js +41 -258
  77. package/lib/commands/auth-status-client-tokens.js +214 -0
  78. package/lib/commands/auth-status-dataplane-version.js +269 -0
  79. package/lib/commands/auth-status-display.js +94 -0
  80. package/lib/commands/auth-status.js +111 -53
  81. package/lib/commands/credential-push.js +10 -5
  82. package/lib/commands/datasource-load-export-cli.js +178 -0
  83. package/lib/commands/datasource-test-trust-cli.js +118 -0
  84. package/lib/commands/datasource-unified-test-cli.js +31 -20
  85. package/lib/commands/datasource-unified-test-cli.options.js +22 -3
  86. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  87. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  88. package/lib/commands/datasource.js +68 -1
  89. package/lib/commands/dev-show-display.js +8 -1
  90. package/lib/commands/dimension-value.js +43 -13
  91. package/lib/commands/dimension.js +38 -7
  92. package/lib/commands/governance-cli-shared.js +94 -0
  93. package/lib/commands/governance-command-action.js +132 -0
  94. package/lib/commands/governance-download-external.js +34 -0
  95. package/lib/commands/governance-generate-external.js +71 -0
  96. package/lib/commands/governance-upload-external.js +36 -0
  97. package/lib/commands/governance-verify-external.js +64 -0
  98. package/lib/commands/governance.js +144 -0
  99. package/lib/commands/identity-apply-sync.js +131 -0
  100. package/lib/commands/identity-group-cli.js +146 -0
  101. package/lib/commands/identity-membership-role-cache.js +175 -0
  102. package/lib/commands/identity-shared.js +94 -0
  103. package/lib/commands/identity-user-cli.js +158 -0
  104. package/lib/commands/identity.js +43 -0
  105. package/lib/commands/lifecycle-command-action.js +123 -0
  106. package/lib/commands/lifecycle-external.js +268 -0
  107. package/lib/commands/login.js +56 -17
  108. package/lib/commands/protection-cli-leaves.js +286 -0
  109. package/lib/commands/protection.js +50 -0
  110. package/lib/commands/repair-auth-apply.js +127 -0
  111. package/lib/commands/repair-changed-files.js +41 -0
  112. package/lib/commands/repair-datasource-keys.js +4 -1
  113. package/lib/commands/repair-datasource-run.js +59 -0
  114. package/lib/commands/repair-datasource-test-payload.js +259 -0
  115. package/lib/commands/repair-datasource.js +1 -51
  116. package/lib/commands/repair-env-template.js +64 -13
  117. package/lib/commands/repair-external-run.js +216 -0
  118. package/lib/commands/repair-openapi-sync.js +23 -0
  119. package/lib/commands/repair-persist.js +4 -0
  120. package/lib/commands/repair-rbac.js +52 -1
  121. package/lib/commands/repair-steps.js +145 -0
  122. package/lib/commands/repair.js +29 -286
  123. package/lib/commands/secrets-get.js +184 -0
  124. package/lib/commands/secrets-remove-all.js +3 -1
  125. package/lib/commands/setup-image-refresh.js +146 -0
  126. package/lib/commands/setup-modes.js +158 -162
  127. package/lib/commands/setup-platform-auth.js +76 -0
  128. package/lib/commands/setup-prompts-admin.js +244 -0
  129. package/lib/commands/setup-prompts-platform-mode.js +67 -0
  130. package/lib/commands/setup-prompts-validators.js +36 -0
  131. package/lib/commands/setup-prompts.js +24 -76
  132. package/lib/commands/setup-run-context.js +45 -0
  133. package/lib/commands/setup-spinners.js +39 -0
  134. package/lib/commands/setup.js +27 -5
  135. package/lib/commands/teardown.js +39 -0
  136. package/lib/commands/test-e2e-external.js +61 -14
  137. package/lib/commands/test-governance-cli-options.js +47 -0
  138. package/lib/commands/test-governance-external.js +67 -0
  139. package/lib/commands/test-trust-cli-options.js +32 -0
  140. package/lib/commands/test-trust-external.js +126 -0
  141. package/lib/commands/up-common.js +25 -13
  142. package/lib/commands/up-dataplane.js +50 -13
  143. package/lib/commands/upload-publish-context.js +75 -0
  144. package/lib/commands/upload-sidecar-followups.js +89 -0
  145. package/lib/commands/upload.js +108 -105
  146. package/lib/commands/verify-governance-combined-external.js +61 -0
  147. package/lib/commands/verify-governance-command-action.js +64 -0
  148. package/lib/commands/verify-governance-db.js +102 -0
  149. package/lib/commands/verify-governance-external.js +77 -0
  150. package/lib/commands/verify-governance-result-builders.js +71 -0
  151. package/lib/commands/verify-operations-command-action.js +68 -0
  152. package/lib/commands/verify-operations-external.js +268 -0
  153. package/lib/commands/verify-operations-persist.js +75 -0
  154. package/lib/commands/verify-operations-step-plan.js +68 -0
  155. package/lib/commands/verify-operations-steps.js +111 -0
  156. package/lib/commands/verify-trust-command-action.js +70 -0
  157. package/lib/commands/verify-trust-external.js +67 -0
  158. package/lib/commands/wizard-config-normalizer.js +43 -23
  159. package/lib/commands/wizard-core.js +5 -0
  160. package/lib/core/admin-password-generator.js +38 -0
  161. package/lib/core/admin-secrets-bundle.js +111 -0
  162. package/lib/core/admin-secrets-env-overlay.js +62 -0
  163. package/lib/core/admin-secrets.js +32 -4
  164. package/lib/core/config-attach-extensions.js +3 -0
  165. package/lib/core/config-device-dataplane.js +185 -0
  166. package/lib/core/config-platform-controller.js +44 -0
  167. package/lib/core/config-setup-installation-profile.js +42 -0
  168. package/lib/core/config-setup-platform-mode.js +46 -0
  169. package/lib/core/config.js +46 -36
  170. package/lib/core/secrets-admin-env.js +6 -2
  171. package/lib/core/secrets-ensure.js +4 -0
  172. package/lib/core/secrets-env-content.js +54 -48
  173. package/lib/core/secrets-env-write.js +10 -1
  174. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  175. package/lib/core/setup-installation-profile.js +43 -0
  176. package/lib/core/setup-platform-mode.js +55 -0
  177. package/lib/datasource/agent-trust-debug-log.js +45 -0
  178. package/lib/datasource/agent-trust-fetch.js +115 -0
  179. package/lib/datasource/agent-trust-map.js +101 -0
  180. package/lib/datasource/agent-trust-mock.js +44 -0
  181. package/lib/datasource/agent-trust-run.js +95 -0
  182. package/lib/datasource/agent-trust-timeout.js +25 -0
  183. package/lib/datasource/audit-evidence-display.js +80 -0
  184. package/lib/datasource/audit-evidence-extract-debug.js +115 -0
  185. package/lib/datasource/audit-evidence-extract.js +180 -0
  186. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  187. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  188. package/lib/datasource/audit-evidence-poll.js +109 -0
  189. package/lib/datasource/audit-evidence-run.js +177 -0
  190. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  191. package/lib/datasource/audit-evidence-verify-matrix.js +205 -0
  192. package/lib/datasource/audit-evidence-verify.js +108 -0
  193. package/lib/datasource/bulk-loader-run-batches.js +100 -0
  194. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  195. package/lib/datasource/bulk-loader-service.js +127 -0
  196. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  197. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  198. package/lib/datasource/datasource-exporter-service.js +144 -0
  199. package/lib/datasource/datasource-identifier.js +148 -0
  200. package/lib/datasource/datasource-load-export-context.js +95 -0
  201. package/lib/datasource/integration-context.js +6 -4
  202. package/lib/datasource/local-data-paths.js +145 -0
  203. package/lib/datasource/log-cleaner.js +241 -0
  204. package/lib/datasource/log-viewer.js +111 -6
  205. package/lib/datasource/record-file-parser.js +85 -0
  206. package/lib/datasource/record-mapper.js +129 -0
  207. package/lib/datasource/resolve-app.js +74 -55
  208. package/lib/datasource/test-e2e.js +29 -2
  209. package/lib/datasource/unified-validation-run-body.js +3 -1
  210. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  211. package/lib/datasource/unified-validation-run.js +47 -18
  212. package/lib/datasource/validate.js +37 -20
  213. package/lib/deployment/environment-poll-status.js +138 -0
  214. package/lib/deployment/environment.js +42 -34
  215. package/lib/external-system/download.js +21 -20
  216. package/lib/external-system/generator.js +5 -1
  217. package/lib/external-system/integration-auth-context.js +45 -0
  218. package/lib/external-system/test-auth.js +12 -5
  219. package/lib/external-system/test-system-level.js +25 -4
  220. package/lib/external-system/test.js +9 -7
  221. package/lib/generator/external-controller-manifest.js +15 -0
  222. package/lib/governance/governance-cli-errors.js +96 -0
  223. package/lib/governance/governance-download-display.js +38 -0
  224. package/lib/governance/governance-pack-loader.js +70 -0
  225. package/lib/governance/governance-run-display.js +309 -0
  226. package/lib/identity/identity-apply-core.js +108 -0
  227. package/lib/identity/identity-apply-phases.js +244 -0
  228. package/lib/identity/identity-apply-service.js +77 -0
  229. package/lib/identity/identity-csv-parser.js +114 -0
  230. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  231. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  232. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  233. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  234. package/lib/infrastructure/helpers.js +23 -129
  235. package/lib/infrastructure/index.js +5 -6
  236. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  237. package/lib/internal/fs-real-sync.js +29 -1
  238. package/lib/lifecycle/certification-progress.js +231 -0
  239. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  240. package/lib/lifecycle/exit-codes.js +24 -0
  241. package/lib/lifecycle/json-envelope.js +25 -0
  242. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  243. package/lib/lifecycle/logger-quiet.js +29 -0
  244. package/lib/lifecycle/open-api-published.js +59 -0
  245. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  246. package/lib/lifecycle/product-model.js +196 -0
  247. package/lib/lifecycle/report-display.js +471 -0
  248. package/lib/lifecycle/scenario-upload.js +191 -0
  249. package/lib/lifecycle/verify-step-progress.js +87 -0
  250. package/lib/parameters/infra-kv-discovery.js +27 -2
  251. package/lib/protection/auth-context.js +51 -0
  252. package/lib/protection/convert-batch.js +90 -0
  253. package/lib/protection/load.js +41 -0
  254. package/lib/protection/paths.js +158 -0
  255. package/lib/protection/preflight-datasource-ready.js +46 -0
  256. package/lib/protection/protection-create-probe-lines.js +142 -0
  257. package/lib/protection/protection-create-probes.js +95 -0
  258. package/lib/protection/protection-create-scaffold.js +281 -0
  259. package/lib/protection/protection-display-helpers.js +445 -0
  260. package/lib/protection/protection-display.js +215 -0
  261. package/lib/protection/protection-preset-registry.js +97 -0
  262. package/lib/protection/report-exit.js +62 -0
  263. package/lib/protection/resolve.js +202 -0
  264. package/lib/protection/run-commands-validate.js +130 -0
  265. package/lib/protection/run-commands.js +216 -0
  266. package/lib/protection/run-protection-create-helpers.js +134 -0
  267. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  268. package/lib/protection/run-protection-create.js +191 -0
  269. package/lib/protection/scope.js +22 -0
  270. package/lib/protection/sync-after-upload.js +64 -0
  271. package/lib/protection/upload-batch.js +127 -0
  272. package/lib/protection/validate-batch.js +113 -0
  273. package/lib/protection/validate-local.js +65 -0
  274. package/lib/resolvers/dimension-file.js +29 -4
  275. package/lib/schema/application-schema.json +1 -1
  276. package/lib/schema/datasource-test-run.schema.json +89 -6
  277. package/lib/schema/external-datasource.schema.json +2636 -2588
  278. package/lib/schema/external-system.schema.json +1089 -761
  279. package/lib/schema/flag-map-validation-run.json +2 -2
  280. package/lib/schema/infra.parameter.yaml +45 -0
  281. package/lib/schema/protection.schema.json +374 -0
  282. package/lib/schema/type/credential-field-registry.json +642 -0
  283. package/lib/schema/wizard-config.schema.json +1 -1
  284. package/lib/utils/agent-trust-run-display.js +231 -0
  285. package/lib/utils/agent-trust-run-exit.js +59 -0
  286. package/lib/utils/agent-trust-wait-ui.js +94 -0
  287. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  288. package/lib/utils/api.js +55 -31
  289. package/lib/utils/app-relative-path-env.js +112 -0
  290. package/lib/utils/cli-exit-codes.js +30 -0
  291. package/lib/utils/cli-secrets-error-format.js +83 -21
  292. package/lib/utils/cli-sync-options.js +33 -0
  293. package/lib/utils/cli-utils.js +16 -4
  294. package/lib/utils/compose-generate-docker-compose.js +4 -0
  295. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  296. package/lib/utils/config-secrets-path.js +46 -0
  297. package/lib/utils/controller-url.js +88 -1
  298. package/lib/utils/credential-push-ui.js +90 -0
  299. package/lib/utils/credential-secrets-env.js +137 -9
  300. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  301. package/lib/utils/dataplane-cli-version-help.js +74 -0
  302. package/lib/utils/dataplane-resolver.js +129 -2
  303. package/lib/utils/datasource-export-display.js +137 -0
  304. package/lib/utils/datasource-load-display.js +174 -0
  305. package/lib/utils/datasource-load-export-ui.js +120 -0
  306. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  307. package/lib/utils/datasource-test-run-display.js +3 -3
  308. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  309. package/lib/utils/declarative-url-ports.js +3 -3
  310. package/lib/utils/deployment-auth-mode.js +41 -0
  311. package/lib/utils/developer-volume-id.js +33 -0
  312. package/lib/utils/env-copy.js +14 -9
  313. package/lib/utils/env-kv-orphan-lines.js +43 -0
  314. package/lib/utils/error-formatters/network-errors.js +13 -4
  315. package/lib/utils/external-env-template.js +1 -0
  316. package/lib/utils/external-system-readiness-core.js +10 -0
  317. package/lib/utils/external-system-readiness-display.js +80 -23
  318. package/lib/utils/health-check.js +4 -1
  319. package/lib/utils/help-builder.js +35 -26
  320. package/lib/utils/keycloak-realm-ready.js +101 -0
  321. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  322. package/lib/utils/manifest-source-emit.js +24 -0
  323. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  324. package/lib/utils/platform-controller-url.js +156 -0
  325. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  326. package/lib/utils/postgres-wipe.js +9 -2
  327. package/lib/utils/resolve-env-mode.js +47 -0
  328. package/lib/utils/secrets-generator.js +31 -25
  329. package/lib/utils/secrets-missing-error.js +164 -0
  330. package/lib/utils/semver-compare.js +127 -0
  331. package/lib/utils/service-url-label.js +81 -0
  332. package/lib/utils/setup-quiet-output.js +25 -0
  333. package/lib/utils/template-helpers.js +15 -0
  334. package/lib/utils/token-manager-client-credentials.js +201 -0
  335. package/lib/utils/token-manager-messages.js +8 -0
  336. package/lib/utils/token-manager-refresh.js +12 -2
  337. package/lib/utils/token-manager.js +86 -105
  338. package/lib/utils/upload-pipeline-ui.js +98 -0
  339. package/lib/utils/upload-sync-options.js +42 -0
  340. package/lib/utils/url-declarative-public-base.js +21 -3
  341. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  342. package/lib/utils/validation-poll-ui.js +22 -7
  343. package/lib/utils/validation-run-stage.js +45 -0
  344. package/lib/validation/wizard-config-validator.js +58 -19
  345. package/package.json +1 -1
  346. package/templates/applications/dataplane/application.yaml +2 -1
  347. package/templates/applications/dataplane/env.template +36 -1
  348. package/templates/applications/dataplane/rbac.yaml +4 -0
  349. package/templates/applications/miso-controller/env.template +16 -0
  350. package/templates/external-system/README.md.hbs +116 -11
  351. package/templates/python/docker-compose.hbs +19 -18
  352. package/templates/typescript/docker-compose.hbs +18 -17
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Retry dataplane pipeline upload with application client token when device Bearer fails.
3
+ *
4
+ * @fileoverview Pipeline publish auth fallback when device Bearer is rejected
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const chalk = require('chalk');
12
+ const logger = require('./logger');
13
+ const { uploadApplicationViaPipeline } = require('../api/pipeline.api');
14
+ const { getDeploymentAuth } = require('./token-manager');
15
+ const { resolveControllerUrl } = require('./controller-url');
16
+ const config = require('../core/config');
17
+
18
+ /**
19
+ * @param {Object} apiResponse
20
+ * @returns {boolean}
21
+ */
22
+ function isPipelineAuthFailure(apiResponse) {
23
+ if (!apiResponse || apiResponse.success !== false) {
24
+ return false;
25
+ }
26
+ const status = Number(apiResponse.status) || 0;
27
+ if (status === 401 || status === 403) {
28
+ return true;
29
+ }
30
+ const text = String(
31
+ apiResponse.formattedError || apiResponse.error || apiResponse.message || ''
32
+ ).toLowerCase();
33
+ return text.includes('invalid token') || text.includes('unauthorized') || text.includes('authentication');
34
+ }
35
+
36
+ /**
37
+ * @param {string} systemKey
38
+ * @returns {Promise<Object|null>}
39
+ */
40
+ async function resolveClientTokenAuthForPipelineRetry(systemKey) {
41
+ try {
42
+ const controllerUrl = await resolveControllerUrl();
43
+ const environment = await config.resolveEnvironment();
44
+ return await getDeploymentAuth(controllerUrl, environment, systemKey, {
45
+ deploymentAuth: 'client-credentials'
46
+ });
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * POST /api/v1/pipeline/upload with optional client-token retry after device Bearer 401.
54
+ *
55
+ * @async
56
+ * @param {string} dataplaneUrl
57
+ * @param {Object} authConfig
58
+ * @param {Object} payload
59
+ * @param {string} systemKey - External system key for credential lookup on retry
60
+ * @returns {Promise<Object>}
61
+ */
62
+ async function uploadApplicationViaPipelineWithAuthRetry(dataplaneUrl, authConfig, payload, systemKey) {
63
+ const first = await uploadApplicationViaPipeline(dataplaneUrl, authConfig, payload);
64
+ if (!isPipelineAuthFailure(first) || authConfig.type !== 'bearer') {
65
+ return first;
66
+ }
67
+ const clientAuth = await resolveClientTokenAuthForPipelineRetry(systemKey);
68
+ if (!clientAuth || clientAuth.type === 'bearer') {
69
+ return first;
70
+ }
71
+ logger.log(
72
+ chalk.yellow(
73
+ 'Device login token was rejected for pipeline publish; retrying with application client token…'
74
+ )
75
+ );
76
+ return uploadApplicationViaPipeline(dataplaneUrl, clientAuth, payload);
77
+ }
78
+
79
+ module.exports = {
80
+ isPipelineAuthFailure,
81
+ uploadApplicationViaPipelineWithAuthRetry
82
+ };
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Absolute public base URLs for platform apps (Keycloak, Miso Controller, Dataplane).
3
+ * Used by setup (`platform-controller`), guided footers, and declarative routing.
4
+ *
5
+ * @fileoverview Platform app public URL resolution (Traefik / localhost + dev ports)
6
+ * @author AI Fabrix Team
7
+ * @version 1.0.0
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ const config = require('../core/config');
16
+ const pathsUtil = require('./paths');
17
+ const { loadConfigFile } = require('./config-format');
18
+ const { computePublicUrlBaseString } = require('./url-declarative-public-base');
19
+ const { joinUrlPath, normalizeFrontDoorPatternForHealth } = require('./health-check-url');
20
+ const { getDevPorts } = require('./dev-config');
21
+
22
+ const MISO_CONTROLLER_APP = 'miso-controller';
23
+
24
+ /**
25
+ * Bundled `templates/applications/<app>/application.yaml` (fresh install has no builder copy yet).
26
+ *
27
+ * @param {string} appName
28
+ * @returns {Object|null}
29
+ */
30
+ function loadBundledApplicationVariables(appName) {
31
+ const templateConfigPath = path.join(
32
+ pathsUtil.getProjectRoot(),
33
+ 'templates',
34
+ 'applications',
35
+ appName,
36
+ 'application.yaml'
37
+ );
38
+ if (!fs.existsSync(templateConfigPath)) {
39
+ return null;
40
+ }
41
+ const variables = loadConfigFile(templateConfigPath);
42
+ return variables && typeof variables === 'object' ? variables : null;
43
+ }
44
+
45
+ /**
46
+ * Load platform app variables from materialized builder config, or from bundled template
47
+ * when `aifabrix setup` runs before `up-platform` has copied templates.
48
+ *
49
+ * @param {string} appName
50
+ * @returns {Object}
51
+ */
52
+ function loadAppVariables(appName) {
53
+ const builderPath = pathsUtil.getBuilderPath(appName);
54
+ if (fs.existsSync(builderPath)) {
55
+ try {
56
+ const configPath = pathsUtil.resolveApplicationConfigPath(builderPath);
57
+ return loadConfigFile(configPath) || {};
58
+ } catch (err) {
59
+ const msg = err && err.message ? String(err.message) : '';
60
+ if (!msg.includes('Application config not found')) {
61
+ throw err;
62
+ }
63
+ }
64
+ }
65
+ const fromTemplate = loadBundledApplicationVariables(appName);
66
+ if (fromTemplate) {
67
+ return fromTemplate;
68
+ }
69
+ throw new Error(
70
+ `Application config not found for platform app "${appName}". ` +
71
+ 'Run `aifabrix setup` or `aifabrix up-platform` to materialize builder templates.'
72
+ );
73
+ }
74
+
75
+ function computeLocalFullPlatformFrontDoorUrl(params) {
76
+ const { setupPlatformMode, remoteServer, developerIdNum, frontDoorRouting } = params;
77
+ if (setupPlatformMode !== 'full') {
78
+ return null;
79
+ }
80
+ if (remoteServer && String(remoteServer).trim()) {
81
+ return null;
82
+ }
83
+ const ports = getDevPorts(Number.isFinite(developerIdNum) ? developerIdNum : 0);
84
+ const mount = normalizeFrontDoorPatternForHealth(frontDoorRouting.pattern);
85
+ // Full local platform uses a single front-door port (3000 + devId×100), not the infra Traefik port.
86
+ return joinUrlPath(`http://localhost:${ports.app}`, mount);
87
+ }
88
+
89
+ /**
90
+ * Compute the absolute base URL for a platform app (same rules as guided `up-platform` footer).
91
+ *
92
+ * @async
93
+ * @param {string} appName - e.g. `miso-controller`, `keycloak`, `dataplane`
94
+ * @returns {Promise<string>}
95
+ */
96
+ async function computeAppBaseUrl(appName) {
97
+ const variables = loadAppVariables(appName);
98
+ const basePort = Number(variables.port || 3000);
99
+ const developerIdRaw = await config.getDeveloperId();
100
+ const developerIdNum =
101
+ typeof developerIdRaw === 'string' ? parseInt(developerIdRaw, 10) : developerIdRaw;
102
+ const userCfg = await config.getConfig();
103
+ const remoteServer = await config.getRemoteServer();
104
+ const infraTlsEnabled = Boolean(userCfg && userCfg.tlsEnabled);
105
+
106
+ const fd = variables.frontDoorRouting || null;
107
+ const frontDoorEnabled = Boolean(fd && fd.enabled === true);
108
+ const traefikOn = Boolean(userCfg && userCfg.traefik);
109
+ const pathActive = Boolean(traefikOn && frontDoorEnabled);
110
+ const setupPlatformMode = await config.getSetupPlatformMode();
111
+
112
+ const publicBase = computePublicUrlBaseString({
113
+ traefik: traefikOn,
114
+ pathActive,
115
+ hostTemplate: fd ? fd.host : null,
116
+ tls: fd ? fd.tls : true,
117
+ developerIdRaw,
118
+ remoteServer,
119
+ profile: 'docker',
120
+ listenPort: basePort,
121
+ developerIdNum,
122
+ infraTlsEnabled
123
+ });
124
+
125
+ if (pathActive) {
126
+ const localFullUrl = computeLocalFullPlatformFrontDoorUrl({
127
+ setupPlatformMode,
128
+ remoteServer,
129
+ developerIdNum,
130
+ frontDoorRouting: fd
131
+ });
132
+ if (localFullUrl) return localFullUrl;
133
+ const mount = normalizeFrontDoorPatternForHealth(fd.pattern);
134
+ return joinUrlPath(publicBase, mount);
135
+ }
136
+
137
+ return String(publicBase).replace(/\/+$/, '');
138
+ }
139
+
140
+ /**
141
+ * Absolute Miso Controller URL for local platform install / setup auth.
142
+ *
143
+ * @async
144
+ * @returns {Promise<string>}
145
+ */
146
+ async function resolvePlatformControllerUrl() {
147
+ return computeAppBaseUrl(MISO_CONTROLLER_APP);
148
+ }
149
+
150
+ module.exports = {
151
+ MISO_CONTROLLER_APP,
152
+ loadAppVariables,
153
+ loadBundledApplicationVariables,
154
+ computeAppBaseUrl,
155
+ resolvePlatformControllerUrl
156
+ };
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Recreate platform Postgres databases and roles after setup "wipe data".
3
+ *
4
+ * Infra init scripts run only on first volume creation; dropping DBs in a
5
+ * running Postgres container requires explicit bootstrap before Keycloak/Miso start.
6
+ *
7
+ * @fileoverview Platform database bootstrap after postgres wipe
8
+ * @author AI Fabrix Team
9
+ * @version 1.0.0
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const config = require('../core/config');
15
+ const adminSecrets = require('../core/admin-secrets');
16
+ const logger = require('./logger');
17
+ const chalk = require('chalk');
18
+ const { successGlyph } = require('./cli-test-layout-chalk');
19
+ const { getPostgresContainerName } = require('./postgres-wipe');
20
+ const dockerExec = require('./docker-exec');
21
+ const { isSetupQuietOutput } = require('./setup-quiet-output');
22
+
23
+ const SUPERUSER_ROLE = 'pgadmin';
24
+
25
+ /** @type {{ dbName: string, secretKey: string }[]} */
26
+ const PLATFORM_DATABASE_SLOTS = [
27
+ { dbName: 'keycloak', secretKey: 'databases-keycloak-0-passwordKeyVault' },
28
+ { dbName: 'miso', secretKey: 'databases-miso-controller-0-passwordKeyVault' },
29
+ { dbName: 'miso-logs', secretKey: 'databases-miso-controller-1-passwordKeyVault' },
30
+ { dbName: 'dataplane', secretKey: 'databases-dataplane-0-passwordKeyVault' },
31
+ { dbName: 'dataplane-vector', secretKey: 'databases-dataplane-1-passwordKeyVault' },
32
+ { dbName: 'dataplane-logs', secretKey: 'databases-dataplane-2-passwordKeyVault' },
33
+ { dbName: 'dataplane-records', secretKey: 'databases-dataplane-3-passwordKeyVault' }
34
+ ];
35
+
36
+ /**
37
+ * @param {string} dbName
38
+ * @returns {string}
39
+ */
40
+ function pgRoleName(dbName) {
41
+ return `${String(dbName).replace(/-/g, '_')}_user`;
42
+ }
43
+
44
+ /**
45
+ * @param {string} s
46
+ * @returns {string}
47
+ */
48
+ function escapePgString(s) {
49
+ if (s === null || s === undefined || typeof s !== 'string') return '';
50
+ return s.replace(/'/g, '\'\'');
51
+ }
52
+
53
+ /**
54
+ * @param {string} urlOrPassword
55
+ * @returns {string|null}
56
+ */
57
+ function extractPasswordFromUrlOrValue(urlOrPassword) {
58
+ if (typeof urlOrPassword !== 'string' || urlOrPassword.length === 0) return null;
59
+ if (!urlOrPassword.includes('://')) return urlOrPassword;
60
+ try {
61
+ const u = new URL(
62
+ urlOrPassword.replace(/\$\{DB_HOST\}/g, 'postgres').replace(/\$\{DB_PORT\}/g, '5432')
63
+ );
64
+ return u.password || null;
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Escape SQL for embedding in a double-quoted shell argument (-c "...").
72
+ * Bash expands `$` and `$$` (PID) inside double quotes; backslash-escape every `$`.
73
+ *
74
+ * @param {string} s
75
+ * @returns {string}
76
+ */
77
+ function escapeShellDoubleQuoted(s) {
78
+ return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\$/g, '\\$');
79
+ }
80
+
81
+ /**
82
+ * @async
83
+ * @param {string} container
84
+ * @param {string} sql
85
+ * @param {string} adminPassword
86
+ * @returns {Promise<void>}
87
+ */
88
+ async function runPsqlCommand(container, sql, adminPassword, database = 'postgres') {
89
+ const escapedSql = escapeShellDoubleQuoted(sql);
90
+ const cmd =
91
+ `docker exec -e PGPASSWORD -i ${container} psql -U ${SUPERUSER_ROLE} -d ${database} ` +
92
+ `-v ON_ERROR_STOP=1 -c "${escapedSql}"`;
93
+ await dockerExec.execWithDockerEnv(cmd, { env: { PGPASSWORD: adminPassword } });
94
+ }
95
+
96
+ /**
97
+ * @async
98
+ * @param {string} container
99
+ * @param {string} dbName
100
+ * @param {string} role
101
+ * @param {string} password
102
+ * @param {string} adminPassword
103
+ * @returns {Promise<void>}
104
+ */
105
+ async function ensureRoleAndDatabase(container, dbName, role, password, adminPassword) {
106
+ if (!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(dbName) || !/^[A-Za-z_][A-Za-z0-9_-]*$/.test(role)) {
107
+ throw new Error(`Unsafe database or role name: ${dbName} / ${role}`);
108
+ }
109
+ const pwd = escapePgString(password);
110
+ await runPsqlCommand(
111
+ container,
112
+ `DO $$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${role}') THEN CREATE USER "${role}" WITH PASSWORD '${pwd}'; ELSE ALTER USER "${role}" WITH PASSWORD '${pwd}'; END IF; END $$;`,
113
+ adminPassword
114
+ );
115
+ const exists = await dockerExec.execWithDockerEnv(
116
+ `docker exec -e PGPASSWORD -i ${container} psql -U ${SUPERUSER_ROLE} -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = '${dbName}'"`,
117
+ { env: { PGPASSWORD: adminPassword } }
118
+ );
119
+ const hasDb = String((exists && exists.stdout) || '').trim() === '1';
120
+ if (!hasDb) {
121
+ await runPsqlCommand(
122
+ container,
123
+ `CREATE DATABASE "${dbName}" OWNER "${role}";`,
124
+ adminPassword
125
+ );
126
+ }
127
+ await runPsqlCommand(
128
+ container,
129
+ `GRANT ALL ON SCHEMA public TO "${role}"; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "${role}";`,
130
+ adminPassword,
131
+ dbName
132
+ );
133
+ }
134
+
135
+ /**
136
+ * @async
137
+ * @param {string} container
138
+ * @param {{ dbName: string, secretKey: string }} slot
139
+ * @param {Object} secrets
140
+ * @param {string} adminPassword
141
+ * @returns {Promise<string>}
142
+ */
143
+ async function ensurePlatformDatabaseSlot(container, slot, secrets, adminPassword) {
144
+ const raw = secrets[slot.secretKey];
145
+ const password = extractPasswordFromUrlOrValue(raw);
146
+ if (!password || !password.trim()) {
147
+ throw new Error(
148
+ `Secret ${slot.secretKey} is missing or empty. Run "aifabrix up-infra" to ensure infra secrets.`
149
+ );
150
+ }
151
+ const role = pgRoleName(slot.dbName);
152
+ await ensureRoleAndDatabase(container, slot.dbName, role, password.trim(), adminPassword);
153
+ if (!isSetupQuietOutput()) {
154
+ logger.log(chalk.gray(` ${successGlyph()} Ensured database ${slot.dbName} (${role})`));
155
+ }
156
+ return slot.dbName;
157
+ }
158
+
159
+ /**
160
+ * @async
161
+ * @returns {Promise<{ container: string, adminPassword: string, secrets: Object }>}
162
+ */
163
+ async function resolveBootstrapContext() {
164
+ const devId = await config.getDeveloperId();
165
+ const container = getPostgresContainerName(devId);
166
+ const admin = await adminSecrets.readAndDecryptAdminSecrets();
167
+ const adminPassword = admin && admin.POSTGRES_PASSWORD;
168
+ if (!adminPassword) {
169
+ throw new Error('POSTGRES_PASSWORD not found in admin-secrets.env. Run "aifabrix up-infra" first.');
170
+ }
171
+ const secretsEnsure = require('../core/secrets-ensure');
172
+ await secretsEnsure.ensureInfraSecrets();
173
+ const { loadSecrets } = require('../core/secrets');
174
+ const secrets = await loadSecrets(undefined);
175
+ return { container, adminPassword, secrets };
176
+ }
177
+
178
+ /**
179
+ * Recreate platform DB users and databases from resolved kv secrets.
180
+ *
181
+ * @async
182
+ * @returns {Promise<string[]>} database names ensured
183
+ */
184
+ async function bootstrapPlatformPostgresDatabases() {
185
+ const { container, adminPassword, secrets } = await resolveBootstrapContext();
186
+ const ensured = [];
187
+ for (const slot of PLATFORM_DATABASE_SLOTS) {
188
+ ensured.push(await ensurePlatformDatabaseSlot(container, slot, secrets, adminPassword));
189
+ }
190
+ return ensured;
191
+ }
192
+
193
+ module.exports = {
194
+ PLATFORM_DATABASE_SLOTS,
195
+ bootstrapPlatformPostgresDatabases,
196
+ pgRoleName,
197
+ extractPasswordFromUrlOrValue,
198
+ escapeShellDoubleQuoted
199
+ };
@@ -22,6 +22,7 @@ const dockerExec = require('./docker-exec');
22
22
  const logger = require('./logger');
23
23
  const chalk = require('chalk');
24
24
  const { successGlyph } = require('./cli-test-layout-chalk');
25
+ const { isSetupQuietOutput } = require('./setup-quiet-output');
25
26
 
26
27
  /** Roles that must never be dropped (Postgres-managed predefined roles). */
27
28
  const PROTECTED_ROLES = new Set([
@@ -190,15 +191,21 @@ async function wipePostgresData() {
190
191
  }
191
192
 
192
193
  const databases = await listUserDatabases(container, password);
194
+ const quiet = isSetupQuietOutput();
195
+
193
196
  for (const dbName of databases) {
194
197
  await dropDatabase(container, dbName, password);
195
- logger.log(chalk.gray(` ${successGlyph()} Dropped database ${dbName}`));
198
+ if (!quiet) {
199
+ logger.log(chalk.gray(` ${successGlyph()} Dropped database ${dbName}`));
200
+ }
196
201
  }
197
202
 
198
203
  const roles = await listDropableRoles(container, password);
199
204
  for (const role of roles) {
200
205
  await dropRole(container, role, password);
201
- logger.log(chalk.gray(` ${successGlyph()} Dropped role ${role}`));
206
+ if (!quiet) {
207
+ logger.log(chalk.gray(` ${successGlyph()} Dropped role ${role}`));
208
+ }
202
209
  }
203
210
 
204
211
  return { databases, roles };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Chooses `local` vs `docker` for `generateEnvFile` / `resolve` (integration → local, no Docker rewrite).
3
+ *
4
+ * @fileoverview Resolve environment mode for env generation
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+
13
+ /** Platform builder apps that run in Docker and need docker host/port transforms on resolve. */
14
+ const PLATFORM_DOCKER_APP_KEYS = new Set(['keycloak', 'miso-controller', 'dataplane']);
15
+
16
+ /**
17
+ * @param {string} appPath - Resolved app directory from getResolveAppPath
18
+ * @returns {boolean}
19
+ */
20
+ function isIntegrationAppPath(appPath) {
21
+ if (!appPath || typeof appPath !== 'string') return false;
22
+ const normalized = path.resolve(appPath);
23
+ const marker = `${path.sep}integration${path.sep}`;
24
+ return normalized.includes(marker);
25
+ }
26
+
27
+ /**
28
+ * @param {string} appPath - Resolved application directory
29
+ * @param {string} [appName] - App or system key
30
+ * @returns {'local'|'docker'}
31
+ */
32
+ function resolveGenerateEnvEnvironment(appPath, appName) {
33
+ if (isIntegrationAppPath(appPath)) {
34
+ return 'local';
35
+ }
36
+ const key = String(appName || '').trim().toLowerCase();
37
+ if (PLATFORM_DOCKER_APP_KEYS.has(key)) {
38
+ return 'docker';
39
+ }
40
+ return 'docker';
41
+ }
42
+
43
+ module.exports = {
44
+ PLATFORM_DOCKER_APP_KEYS,
45
+ isIntegrationAppPath,
46
+ resolveGenerateEnvEnvironment
47
+ };
@@ -77,42 +77,44 @@ function loadYamlTolerantOfDuplicateKeys(content) {
77
77
  }
78
78
 
79
79
  /**
80
- * Skips commented or empty lines when scanning env.template
81
- * @param {string} line - Single line
80
+ * True when secrets map already has a non-empty value for a kv path (flat or nested).
81
+ * @param {Object} secrets
82
+ * @param {string} pathStr - Path after kv:// (e.g. hubspot-demo/token)
82
83
  * @returns {boolean}
83
84
  */
84
- function isCommentOrEmptyLine(line) {
85
- const t = line.trim();
86
- return t === '' || t.startsWith('#');
85
+ function secretPathExistsInStore(secrets, pathStr) {
86
+ if (!secrets || typeof secrets !== 'object' || !pathStr) {
87
+ return false;
88
+ }
89
+ const direct = secrets[pathStr];
90
+ if (direct !== undefined && direct !== null && String(direct).trim() !== '') {
91
+ return true;
92
+ }
93
+ if (!pathStr.includes('/')) {
94
+ return false;
95
+ }
96
+ let current = secrets;
97
+ for (const segment of pathStr.split('/')) {
98
+ if (current === null || current === undefined || typeof current !== 'object' || !(segment in current)) {
99
+ return false;
100
+ }
101
+ current = current[segment];
102
+ }
103
+ return current !== undefined && current !== null && String(current).trim() !== '';
87
104
  }
88
105
 
89
106
  /**
90
- * Finds missing secret keys from template (skips commented and empty lines)
107
+ * Finds missing secret keys from template (skips commented and empty lines).
108
+ * Uses full kv:// paths (e.g. hubspot-demo/token), aligned with collectMissingSecrets.
91
109
  * @function findMissingSecretKeys
92
110
  * @param {string} envTemplate - Environment template content
93
111
  * @param {Object} existingSecrets - Existing secrets object
94
112
  * @returns {string[]} Array of missing secret keys
95
113
  */
96
114
  function findMissingSecretKeys(envTemplate, existingSecrets) {
97
- const kvPattern = /kv:\/\/([a-zA-Z0-9-_]+)/g;
98
- const missingKeys = [];
99
- const seenKeys = new Set();
100
- const lines = envTemplate.split('\n');
101
-
102
- for (const line of lines) {
103
- if (isCommentOrEmptyLine(line)) continue;
104
- let match;
105
- kvPattern.lastIndex = 0;
106
- while ((match = kvPattern.exec(line)) !== null) {
107
- const secretKey = match[1];
108
- if (!seenKeys.has(secretKey) && !(secretKey in existingSecrets)) {
109
- missingKeys.push(secretKey);
110
- seenKeys.add(secretKey);
111
- }
112
- }
113
- }
114
-
115
- return missingKeys;
115
+ const { collectUniqueKvPathStrings } = require('./secrets-kv-refs');
116
+ const paths = collectUniqueKvPathStrings(envTemplate);
117
+ return paths.filter(pathStr => !secretPathExistsInStore(existingSecrets, pathStr));
116
118
  }
117
119
 
118
120
  /**
@@ -275,6 +277,8 @@ function loadExistingSecrets(resolvedPath) {
275
277
  * @throws {Error} If save fails
276
278
  */
277
279
  function saveSecretsFile(resolvedPath, secrets) {
280
+ const { assertWritableSecretsPathForTests } = require('./aifabrix-test-runtime-guard');
281
+ assertWritableSecretsPathForTests(resolvedPath);
278
282
  const dir = path.dirname(resolvedPath);
279
283
  if (!fs.existsSync(dir)) {
280
284
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
@@ -356,6 +360,8 @@ function appendSecretsToFile(resolvedPath, secrets) {
356
360
  if (!secrets || typeof secrets !== 'object' || Object.keys(secrets).length === 0) {
357
361
  return;
358
362
  }
363
+ const { assertWritableSecretsPathForTests } = require('./aifabrix-test-runtime-guard');
364
+ assertWritableSecretsPathForTests(resolvedPath);
359
365
  const dir = path.dirname(resolvedPath);
360
366
  if (!fs.existsSync(dir)) {
361
367
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 });