@aifabrix/builder 2.43.0 → 2.44.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 (346) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/README.md +1 -1
  3. package/anchor-docs/README.md +10 -0
  4. package/anchor-docs/_TEMPLATE +24 -0
  5. package/bin/aifabrix.js +13 -4
  6. package/integration/hubspot-test/README.md +31 -0
  7. package/integration/hubspot-test/create-hubspot.js +5 -5
  8. package/integration/hubspot-test/hubspot-test-datasource-company.json +58 -462
  9. package/integration/hubspot-test/hubspot-test-datasource-contact.json +61 -555
  10. package/integration/hubspot-test/hubspot-test-datasource-deal.json +63 -506
  11. package/integration/hubspot-test/hubspot-test-datasource-users.json +42 -83
  12. package/integration/hubspot-test/hubspot-test-deploy.json +3 -3
  13. package/integration/hubspot-test/test-dataplane-down-tests.js +1 -7
  14. package/integration/hubspot-test/test-dataplane-down.js +3 -3
  15. package/integration/hubspot-test/test.js +35 -43
  16. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  17. package/integration/roundtrip-test-local/README.md +144 -0
  18. package/integration/roundtrip-test-local/application.yaml +13 -0
  19. package/integration/roundtrip-test-local/env.template +15 -0
  20. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  21. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  22. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  23. package/integration/roundtrip-test-local2/README.md +144 -0
  24. package/integration/roundtrip-test-local2/application.yaml +13 -0
  25. package/integration/roundtrip-test-local2/env.template +15 -0
  26. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  27. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  28. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  29. package/integration/test/wizard.yaml +8 -0
  30. package/jest.config.default.js +10 -0
  31. package/jest.config.integration.fixtures.js +22 -0
  32. package/jest.config.integration.js +21 -18
  33. package/jest.config.isolated.js +10 -0
  34. package/jest.projects.js +288 -0
  35. package/lib/api/datasources-core.api.js +3 -3
  36. package/lib/api/dev-mtls-request.js +110 -0
  37. package/lib/api/dev-server-https.js +145 -0
  38. package/lib/api/dev.api.js +133 -144
  39. package/lib/api/index.js +0 -1
  40. package/lib/api/pipeline.api.js +67 -20
  41. package/lib/api/types/dev.types.js +4 -3
  42. package/lib/api/types/pipeline.types.js +8 -5
  43. package/lib/api/types/validation-run.types.js +56 -0
  44. package/lib/api/validation-run.api.js +99 -0
  45. package/lib/api/validation-runner.js +99 -0
  46. package/lib/app/config.js +1 -1
  47. package/lib/app/deploy-status-display.js +2 -2
  48. package/lib/app/deploy.js +7 -6
  49. package/lib/app/display.js +2 -1
  50. package/lib/app/dockerfile.js +3 -2
  51. package/lib/app/down.js +2 -1
  52. package/lib/app/helpers.js +6 -5
  53. package/lib/app/index.js +27 -8
  54. package/lib/app/list.js +7 -6
  55. package/lib/app/push.js +4 -3
  56. package/lib/app/register.js +16 -7
  57. package/lib/app/rotate-secret.js +14 -13
  58. package/lib/app/run-container-start.js +184 -0
  59. package/lib/app/run-docker-fallback.js +108 -0
  60. package/lib/app/run-env-compose.js +30 -42
  61. package/lib/app/run-helpers.js +49 -126
  62. package/lib/app/run-infra-requirements.js +30 -0
  63. package/lib/app/run-resolve-image.js +21 -0
  64. package/lib/app/run.js +74 -21
  65. package/lib/app/show-display.js +1 -1
  66. package/lib/app/show.js +1 -1
  67. package/lib/build/index.js +13 -10
  68. package/lib/cli/index.js +2 -0
  69. package/lib/cli/setup-app.help.js +67 -0
  70. package/lib/cli/setup-app.js +57 -121
  71. package/lib/cli/setup-app.test-commands.js +179 -0
  72. package/lib/cli/setup-auth.js +19 -5
  73. package/lib/cli/setup-credential-deployment.js +22 -8
  74. package/lib/cli/setup-dev-path-commands.js +124 -0
  75. package/lib/cli/setup-dev.js +170 -113
  76. package/lib/cli/setup-environment.js +7 -1
  77. package/lib/cli/setup-external-system.js +62 -22
  78. package/lib/cli/setup-infra.js +126 -47
  79. package/lib/cli/setup-parameters.js +32 -0
  80. package/lib/cli/setup-secrets.js +106 -8
  81. package/lib/cli/setup-service-user.js +1 -1
  82. package/lib/cli/setup-utility.js +36 -20
  83. package/lib/commands/app-down.js +5 -7
  84. package/lib/commands/app-install.js +14 -7
  85. package/lib/commands/app-logs.js +13 -10
  86. package/lib/commands/app-shell.js +4 -1
  87. package/lib/commands/app-test.js +25 -19
  88. package/lib/commands/app.js +22 -10
  89. package/lib/commands/auth-config.js +6 -6
  90. package/lib/commands/auth-status.js +4 -3
  91. package/lib/commands/credential-env.js +4 -3
  92. package/lib/commands/credential-list.js +5 -4
  93. package/lib/commands/credential-push.js +4 -3
  94. package/lib/commands/datasource-unified-test-cli.js +495 -0
  95. package/lib/commands/datasource-unified-test-cli.options.js +149 -0
  96. package/lib/commands/datasource-validation-cli.js +129 -0
  97. package/lib/commands/datasource.js +105 -98
  98. package/lib/commands/deployment-list.js +6 -5
  99. package/lib/commands/dev-cli-handlers.js +122 -18
  100. package/lib/commands/dev-down.js +4 -3
  101. package/lib/commands/dev-init.js +231 -116
  102. package/lib/commands/dev-show-display.js +473 -0
  103. package/lib/commands/login-credentials.js +3 -2
  104. package/lib/commands/login-device.js +4 -3
  105. package/lib/commands/login.js +5 -4
  106. package/lib/commands/logout.js +8 -7
  107. package/lib/commands/parameters-validate.js +54 -0
  108. package/lib/commands/repair-datasource.js +314 -68
  109. package/lib/commands/repair-env-template.js +2 -2
  110. package/lib/commands/repair.js +21 -3
  111. package/lib/commands/secrets-list.js +23 -12
  112. package/lib/commands/secrets-remove-all.js +220 -0
  113. package/lib/commands/secrets-remove.js +21 -12
  114. package/lib/commands/secrets-set.js +21 -12
  115. package/lib/commands/secrets-validate.js +4 -4
  116. package/lib/commands/secure.js +10 -9
  117. package/lib/commands/service-user.js +26 -25
  118. package/lib/commands/test-e2e-external.js +27 -1
  119. package/lib/commands/up-common.js +3 -2
  120. package/lib/commands/up-dataplane.js +29 -16
  121. package/lib/commands/up-miso.js +19 -29
  122. package/lib/commands/upload.js +138 -39
  123. package/lib/commands/wizard-core-helpers.js +1 -1
  124. package/lib/commands/wizard-dataplane.js +4 -3
  125. package/lib/commands/wizard-helpers.js +3 -3
  126. package/lib/commands/wizard.js +2 -2
  127. package/lib/core/admin-secrets.js +14 -5
  128. package/lib/core/audit-logger.js +12 -4
  129. package/lib/core/config-attach-extensions.js +46 -0
  130. package/lib/core/config-runtime-paths.js +29 -0
  131. package/lib/core/config.js +55 -56
  132. package/lib/core/diff.js +3 -2
  133. package/lib/core/ensure-encryption-key.js +1 -1
  134. package/lib/core/secrets-ensure-infra.js +77 -0
  135. package/lib/core/secrets-ensure.js +120 -64
  136. package/lib/core/secrets-env-write.js +35 -7
  137. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  138. package/lib/core/secrets.js +200 -37
  139. package/lib/core/templates-env.js +4 -3
  140. package/lib/datasource/abac-validator.js +1 -10
  141. package/lib/datasource/deploy.js +75 -53
  142. package/lib/datasource/field-reference-validator.js +9 -6
  143. package/lib/datasource/integration-context.js +63 -0
  144. package/lib/datasource/list.js +8 -7
  145. package/lib/datasource/log-viewer.js +84 -53
  146. package/lib/datasource/resolve-app.js +4 -4
  147. package/lib/datasource/test-e2e.js +95 -146
  148. package/lib/datasource/test-integration.js +114 -122
  149. package/lib/datasource/unified-validation-run-body.js +65 -0
  150. package/lib/datasource/unified-validation-run-post.js +23 -0
  151. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  152. package/lib/datasource/unified-validation-run.js +92 -0
  153. package/lib/datasource/validate.js +157 -13
  154. package/lib/deployment/deployer.js +4 -3
  155. package/lib/deployment/environment.js +7 -6
  156. package/lib/deployment/push.js +17 -8
  157. package/lib/external-system/delete.js +4 -3
  158. package/lib/external-system/deploy.js +131 -53
  159. package/lib/external-system/download-helpers.js +1 -1
  160. package/lib/external-system/download.js +7 -6
  161. package/lib/external-system/generator.js +92 -6
  162. package/lib/external-system/integration-test-dispatch.js +26 -0
  163. package/lib/external-system/test-execution.js +5 -1
  164. package/lib/external-system/test-helpers.js +0 -4
  165. package/lib/external-system/test-system-level-helpers.js +110 -0
  166. package/lib/external-system/test-system-level.js +83 -44
  167. package/lib/external-system/test.js +59 -8
  168. package/lib/generator/builders.js +23 -11
  169. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  170. package/lib/generator/external.js +16 -4
  171. package/lib/generator/helpers.js +58 -3
  172. package/lib/generator/index.js +4 -0
  173. package/lib/generator/split-readme.js +12 -7
  174. package/lib/generator/split-variables.js +2 -1
  175. package/lib/generator/split.js +1 -1
  176. package/lib/generator/wizard-readme.js +3 -3
  177. package/lib/generator/wizard.js +8 -8
  178. package/lib/infrastructure/compose.js +60 -6
  179. package/lib/infrastructure/helpers.js +201 -29
  180. package/lib/infrastructure/index.js +28 -17
  181. package/lib/infrastructure/services.js +21 -15
  182. package/lib/internal/fs-real-sync.js +104 -0
  183. package/lib/internal/node-fs.js +98 -0
  184. package/lib/parameters/database-secret-values.js +173 -0
  185. package/lib/parameters/infra-kv-discovery.js +121 -0
  186. package/lib/parameters/infra-parameter-catalog.js +458 -0
  187. package/lib/parameters/infra-parameter-validate.js +64 -0
  188. package/lib/schema/application-schema.json +37 -17
  189. package/lib/schema/datasource-test-run.schema.json +493 -0
  190. package/lib/schema/deployment-rules.yaml +102 -63
  191. package/lib/schema/external-datasource.schema.json +1200 -442
  192. package/lib/schema/external-system.schema.json +181 -5
  193. package/lib/schema/flag-map-validation-run.json +31 -0
  194. package/lib/schema/infra-parameter.schema.json +106 -0
  195. package/lib/schema/infra.parameter.yaml +421 -0
  196. package/lib/schema/type/credential-auth-templates.json +40 -0
  197. package/lib/schema/type/document-storage.json +213 -0
  198. package/lib/schema/type/message-service.json +123 -0
  199. package/lib/schema/type/vector-store.json +88 -0
  200. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  201. package/lib/utils/api-error-handler.js +2 -2
  202. package/lib/utils/api.js +49 -14
  203. package/lib/utils/app-register-api.js +3 -2
  204. package/lib/utils/app-register-auth.js +1 -1
  205. package/lib/utils/app-register-config.js +4 -4
  206. package/lib/utils/app-register-display.js +3 -2
  207. package/lib/utils/app-register-validator.js +3 -2
  208. package/lib/utils/app-run-containers.js +26 -22
  209. package/lib/utils/app-scoped-config.js +31 -0
  210. package/lib/utils/app-service-env-from-builder.js +164 -0
  211. package/lib/utils/build-copy.js +1 -1
  212. package/lib/utils/build-helpers.js +20 -20
  213. package/lib/utils/build-resolve-image.js +165 -0
  214. package/lib/utils/cli-layout-chalk.js +8 -0
  215. package/lib/utils/cli-test-layout-chalk.js +267 -0
  216. package/lib/utils/cli-utils.js +88 -11
  217. package/lib/utils/compose-db-passwords.js +138 -0
  218. package/lib/utils/compose-generate-docker-compose.js +216 -0
  219. package/lib/utils/compose-generator.js +197 -291
  220. package/lib/utils/compose-miso-env.js +18 -0
  221. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  222. package/lib/utils/config-paths.js +166 -7
  223. package/lib/utils/config-scoped-resources-preference.js +41 -0
  224. package/lib/utils/controller-deployment-outcome.js +68 -0
  225. package/lib/utils/credential-display.js +2 -2
  226. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  227. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  228. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  229. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  230. package/lib/utils/datasource-test-run-display.js +442 -0
  231. package/lib/utils/datasource-test-run-exit.js +58 -0
  232. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  233. package/lib/utils/datasource-test-run-report-version.js +51 -0
  234. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  235. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  236. package/lib/utils/datasource-validation-watch.js +266 -0
  237. package/lib/utils/declarative-url-ports.js +47 -0
  238. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  239. package/lib/utils/dev-ca-install.js +185 -23
  240. package/lib/utils/dev-cert-helper.js +266 -17
  241. package/lib/utils/dev-hosts-helper.js +307 -0
  242. package/lib/utils/dev-init-cert-hints.js +37 -0
  243. package/lib/utils/dev-init-health-messages.js +52 -0
  244. package/lib/utils/dev-init-resolve.js +86 -0
  245. package/lib/utils/dev-init-ssh-merge.js +65 -0
  246. package/lib/utils/dev-ssh-config-helper.js +196 -0
  247. package/lib/utils/dev-user-groups.js +93 -0
  248. package/lib/utils/docker-build.js +42 -17
  249. package/lib/utils/docker-exec.js +28 -0
  250. package/lib/utils/docker-manifest-public-port.js +116 -0
  251. package/lib/utils/docker-not-running-hint.js +52 -0
  252. package/lib/utils/docker.js +98 -11
  253. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  254. package/lib/utils/env-config-loader.js +10 -91
  255. package/lib/utils/env-copy.js +19 -10
  256. package/lib/utils/env-map.js +35 -8
  257. package/lib/utils/env-template.js +2 -2
  258. package/lib/utils/environment-scoped-resources.js +144 -0
  259. package/lib/utils/error-formatter.js +92 -13
  260. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  261. package/lib/utils/error-formatters/network-errors.js +2 -1
  262. package/lib/utils/error-formatters/permission-errors.js +2 -1
  263. package/lib/utils/error-formatters/validation-errors.js +2 -1
  264. package/lib/utils/external-readme.js +8 -1
  265. package/lib/utils/external-system-display.js +234 -136
  266. package/lib/utils/external-system-local-test-tty.js +389 -0
  267. package/lib/utils/external-system-readiness-core.js +377 -0
  268. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  269. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  270. package/lib/utils/external-system-readiness-display.js +186 -0
  271. package/lib/utils/external-system-test-helpers.js +24 -6
  272. package/lib/utils/external-system-validators.js +30 -12
  273. package/lib/utils/health-check-url.js +119 -0
  274. package/lib/utils/health-check.js +59 -25
  275. package/lib/utils/help-builder.js +11 -8
  276. package/lib/utils/image-version.js +4 -8
  277. package/lib/utils/infra-containers.js +4 -7
  278. package/lib/utils/infra-env-defaults.js +162 -0
  279. package/lib/utils/infra-status-display.js +167 -0
  280. package/lib/utils/infra-status.js +16 -8
  281. package/lib/utils/local-secrets.js +3 -4
  282. package/lib/utils/paths.js +134 -47
  283. package/lib/utils/port-resolver.js +10 -23
  284. package/lib/utils/redis-env-scope.js +62 -0
  285. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  286. package/lib/utils/remote-builder-validation.js +99 -0
  287. package/lib/utils/remote-dev-auth.js +117 -21
  288. package/lib/utils/remote-docker-env.js +67 -15
  289. package/lib/utils/remote-secrets-loader.js +13 -4
  290. package/lib/utils/resolve-docker-image-ref.js +124 -0
  291. package/lib/utils/schema-loader.js +22 -9
  292. package/lib/utils/secrets-bash-kv.js +25 -0
  293. package/lib/utils/secrets-generator.js +169 -49
  294. package/lib/utils/secrets-helpers.js +70 -59
  295. package/lib/utils/secrets-kv-scope.js +60 -0
  296. package/lib/utils/secrets-utils.js +32 -38
  297. package/lib/utils/secrets-validation.js +3 -1
  298. package/lib/utils/secrets-yaml-preserve.js +109 -0
  299. package/lib/utils/ssh-key-helper.js +4 -2
  300. package/lib/utils/template-helpers.js +2 -2
  301. package/lib/utils/test-log-writer.js +3 -3
  302. package/lib/utils/token-manager.js +1 -2
  303. package/lib/utils/url-declarative-public-base.js +188 -0
  304. package/lib/utils/url-declarative-resolve-build.js +493 -0
  305. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  306. package/lib/utils/url-declarative-resolve.js +220 -0
  307. package/lib/utils/url-declarative-token-parse.js +74 -0
  308. package/lib/utils/url-declarative-url-flags.js +50 -0
  309. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  310. package/lib/utils/url-public-path-prefix.js +34 -0
  311. package/lib/utils/urls-local-registry.js +220 -0
  312. package/lib/utils/validation-report-tty-kit.js +77 -0
  313. package/lib/utils/validation-run-poll.js +89 -0
  314. package/lib/utils/validation-run-post-retry.js +73 -0
  315. package/lib/utils/validation-run-request.js +98 -0
  316. package/lib/utils/variable-transformer.js +21 -4
  317. package/lib/utils/yaml-preserve.js +33 -14
  318. package/lib/validation/datasource-warnings.js +56 -0
  319. package/lib/validation/env-template-auth.js +1 -1
  320. package/lib/validation/external-manifest-validator.js +27 -7
  321. package/lib/validation/validate-display.js +37 -31
  322. package/lib/validation/validate.js +4 -13
  323. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  324. package/lib/validation/validator.js +22 -65
  325. package/lib/validation/wizard-config-validator.js +2 -1
  326. package/package.json +7 -3
  327. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  328. package/scripts/diagnose-cli.js +150 -0
  329. package/scripts/install-local.js +304 -55
  330. package/templates/README.md +15 -2
  331. package/templates/applications/dataplane/application.yaml +52 -2
  332. package/templates/applications/dataplane/env.template +75 -17
  333. package/templates/applications/dataplane/rbac.yaml +8 -0
  334. package/templates/applications/keycloak/application.yaml +9 -1
  335. package/templates/applications/keycloak/env.template +15 -6
  336. package/templates/applications/miso-controller/application.yaml +10 -2
  337. package/templates/applications/miso-controller/env.template +42 -12
  338. package/templates/applications/miso-controller/rbac.yaml +5 -0
  339. package/templates/external-system/README.md.hbs +20 -7
  340. package/templates/external-system/deploy.js.hbs +5 -5
  341. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  342. package/templates/infra/compose.yaml.hbs +20 -4
  343. package/templates/python/docker-compose.hbs +16 -0
  344. package/templates/typescript/docker-compose.hbs +16 -0
  345. package/lib/api/external-test.api.js +0 -111
  346. package/lib/schema/env-config.yaml +0 -60
@@ -15,14 +15,13 @@ const config = require('../core/config');
15
15
  const devConfig = require('../utils/dev-config');
16
16
  const logger = require('../utils/logger');
17
17
  const dockerUtils = require('../utils/docker');
18
- const paths = require('../utils/paths');
19
18
  const statusHelpers = require('../utils/infra-status');
20
19
  const {
21
- getInfraDirName,
22
20
  getInfraProjectName,
23
21
  checkDockerAvailability,
24
22
  ensureAdminSecrets,
25
23
  prepareInfraDirectory,
24
+ resolveInfraStatePaths,
26
25
  ensureMisoInitScript,
27
26
  registerHandlebarsHelper
28
27
  } = require('./helpers');
@@ -72,18 +71,35 @@ async function withRunEnv(infraDir, adminSecretsPath, fn) {
72
71
  * @async
73
72
  * @function prepareInfrastructureEnvironment
74
73
  * @param {string|number|null} developerId - Developer ID
75
- * @param {Object} [options] - Options (traefik, adminPwd)
74
+ * @param {Object} [options] - Options (traefik, adminPwd, tlsEnabled)
76
75
  * @returns {Promise<Object>} Prepared environment configuration
77
76
  */
78
77
  async function prepareInfrastructureEnvironment(developerId, options = {}) {
79
78
  await checkDockerAvailability();
80
- await secretsEnsure.ensureInfraSecrets({ adminPwd: options.adminPwd });
81
- const adminSecretsPath = await ensureAdminSecrets({ adminPwd: options.adminPwd });
79
+ const adminPass = options.adminPassword || options.adminPwd;
80
+ const tlsEnabled = options.tlsEnabled === true;
81
+ const infraOpts = {
82
+ adminPassword: adminPass,
83
+ adminPwd: adminPass,
84
+ adminEmail: options.adminEmail,
85
+ userPassword: options.userPassword,
86
+ tlsEnabled
87
+ };
88
+ await secretsEnsure.ensureInfraSecrets(infraOpts);
89
+ const adminSecretsPath = await ensureAdminSecrets(infraOpts);
82
90
 
83
91
  const devId = developerId || await config.getDeveloperId();
92
+ const remoteServer = await config.getRemoteServer();
93
+ const {
94
+ assertRemoteBuilderDeveloperId,
95
+ remoteServerHostIsNonLocalhost
96
+ } = require('../utils/remote-builder-validation');
97
+ assertRemoteBuilderDeveloperId(remoteServer, devId);
98
+
84
99
  const devIdNum = typeof devId === 'string' ? parseInt(devId, 10) : devId;
85
100
  const ports = devConfig.getDevPorts(devIdNum);
86
101
  const idNum = devIdNum;
102
+ const trustForwardedHeaders = remoteServerHostIsNonLocalhost(remoteServer);
87
103
 
88
104
  const templatePath = path.join(__dirname, '..', '..', 'templates', 'infra', 'compose.yaml.hbs');
89
105
  if (!fs.existsSync(templatePath)) {
@@ -94,7 +110,7 @@ async function prepareInfrastructureEnvironment(developerId, options = {}) {
94
110
  const { infraDir } = await prepareInfraDirectory(devId, adminSecretsPath);
95
111
  await ensureMisoInitScript(infraDir);
96
112
 
97
- return { devId, idNum, ports, templatePath, infraDir, adminSecretsPath };
113
+ return { devId, idNum, ports, templatePath, infraDir, adminSecretsPath, trustForwardedHeaders };
98
114
  }
99
115
 
100
116
  /**
@@ -114,9 +130,10 @@ async function prepareInfrastructureEnvironment(developerId, options = {}) {
114
130
  * // Infrastructure services are now running
115
131
  */
116
132
  async function startInfra(developerId = null, options = {}) {
117
- const { devId, idNum, ports, templatePath, infraDir } = await prepareInfrastructureEnvironment(developerId, options);
133
+ const { devId, idNum, ports, templatePath, infraDir, trustForwardedHeaders } =
134
+ await prepareInfrastructureEnvironment(developerId, options);
118
135
  const { traefik = false, pgadmin = true, redisCommander = true } = options;
119
- const traefikConfig = buildTraefikConfig(traefik);
136
+ const traefikConfig = { ...buildTraefikConfig(traefik), trustForwardedHeaders };
120
137
  const validation = validateTraefikConfig(traefikConfig);
121
138
  if (!validation.valid) {
122
139
  throw new Error(validation.errors.join('\n'));
@@ -199,10 +216,8 @@ async function removeAppVolumes(appNames, devId) {
199
216
  */
200
217
  async function stopInfra() {
201
218
  const devId = await config.getDeveloperId();
202
- const aifabrixDir = paths.getAifabrixHome();
203
- const infraDir = path.join(aifabrixDir, getInfraDirName(devId));
219
+ const { infraDir, adminSecretsPath } = resolveInfraStatePaths(devId);
204
220
  const composePath = path.join(infraDir, 'compose.yaml');
205
- const adminSecretsPath = path.join(aifabrixDir, 'admin-secrets.env');
206
221
 
207
222
  if (!fs.existsSync(composePath) || !fs.existsSync(adminSecretsPath)) {
208
223
  logger.log('Infrastructure not running or not properly configured');
@@ -262,10 +277,8 @@ async function stopAllAppContainersAndVolumes(devId) {
262
277
  */
263
278
  async function stopInfraWithVolumes() {
264
279
  const devId = await config.getDeveloperId();
265
- const aifabrixDir = paths.getAifabrixHome();
266
- const infraDir = path.join(aifabrixDir, getInfraDirName(devId));
280
+ const { infraDir, adminSecretsPath } = resolveInfraStatePaths(devId);
267
281
  const composePath = path.join(infraDir, 'compose.yaml');
268
- const adminSecretsPath = path.join(aifabrixDir, 'admin-secrets.env');
269
282
 
270
283
  if (!fs.existsSync(composePath) || !fs.existsSync(adminSecretsPath)) {
271
284
  logger.log('Infrastructure not running or not properly configured');
@@ -307,10 +320,8 @@ async function restartService(serviceName) {
307
320
  }
308
321
 
309
322
  const devId = await config.getDeveloperId();
310
- const aifabrixDir = paths.getAifabrixHome();
311
- const infraDir = path.join(aifabrixDir, getInfraDirName(devId));
323
+ const { infraDir, adminSecretsPath } = resolveInfraStatePaths(devId);
312
324
  const composePath = path.join(infraDir, 'compose.yaml');
313
- const adminSecretsPath = path.join(aifabrixDir, 'admin-secrets.env');
314
325
 
315
326
  if (!fs.existsSync(composePath) || !fs.existsSync(adminSecretsPath)) {
316
327
  throw new Error('Infrastructure not properly configured');
@@ -9,7 +9,6 @@
9
9
  */
10
10
 
11
11
  const { exec } = require('child_process');
12
- const { promisify } = require('util');
13
12
  const path = require('path');
14
13
  const fs = require('fs');
15
14
  const logger = require('../utils/logger');
@@ -19,13 +18,13 @@ const config = require('../core/config');
19
18
  const { getInfraProjectName } = require('./helpers');
20
19
  const adminSecrets = require('../core/admin-secrets');
21
20
 
22
- const execAsync = promisify(exec);
23
-
24
- // Wrapper to support cwd option
25
- function execAsyncWithCwd(command, options = {}) {
21
+ // Wrapper to support cwd option and dev-config remote Docker (docker-endpoint + TLS)
22
+ async function execAsyncWithCwd(command, options = {}) {
23
+ const { getDockerExecEnv } = require('../utils/remote-docker-env');
24
+ const { cwd, env: extraEnv, ...execOptions } = options;
25
+ const env = { ...(await getDockerExecEnv()), ...(extraEnv || {}) };
26
26
  return new Promise((resolve, reject) => {
27
- const { cwd, ...execOptions } = options;
28
- exec(command, { ...execOptions, cwd }, (error, stdout, stderr) => {
27
+ exec(command, { ...execOptions, cwd, env }, (error, stdout, stderr) => {
29
28
  if (error) {
30
29
  reject(error);
31
30
  } else {
@@ -59,14 +58,15 @@ async function startDockerServices(composePath, projectName, adminSecretsPath, i
59
58
  * @param {string} pgpassPath - Path to pgpass file
60
59
  */
61
60
  async function copyPgAdminConfig(pgadminContainerName, serversJsonPath, pgpassPath) {
61
+ const { execWithDockerEnv } = require('../utils/docker-exec');
62
62
  try {
63
63
  await new Promise(resolve => setTimeout(resolve, 2000)); // Wait for container to be ready
64
64
  if (fs.existsSync(serversJsonPath)) {
65
- await execAsync(`docker cp "${serversJsonPath}" ${pgadminContainerName}:/pgadmin4/servers.json`);
65
+ await execWithDockerEnv(`docker cp "${serversJsonPath}" ${pgadminContainerName}:/pgadmin4/servers.json`);
66
66
  }
67
67
  if (fs.existsSync(pgpassPath)) {
68
- await execAsync(`docker cp "${pgpassPath}" ${pgadminContainerName}:/pgpass`);
69
- await execAsync(`docker exec ${pgadminContainerName} chmod 600 /pgpass`);
68
+ await execWithDockerEnv(`docker cp "${pgpassPath}" ${pgadminContainerName}:/pgpass`);
69
+ await execWithDockerEnv(`docker exec ${pgadminContainerName} chmod 600 /pgpass`);
70
70
  }
71
71
  } catch (error) {
72
72
  // Ignore copy errors - files might already be there or container not ready
@@ -195,8 +195,10 @@ async function waitForServices(devId = null, opts = {}) {
195
195
  * @param {number|string|null} [devId] - Developer ID (null = use current)
196
196
  * @param {Object} [options] - Options
197
197
  * @param {boolean} [options.strict=false] - When true, only consider current dev's containers (no fallback to dev 0); use for up-miso and status consistency
198
- * @param {boolean} [options.pgadmin=true] - Include pgAdmin in health check
199
- * @param {boolean} [options.redisCommander=true] - Include Redis Commander in health check
198
+ * @param {boolean} [options.postgres=true] - When false, skip Postgres (and pgAdmin) checks for apps with requires.database: false
199
+ * @param {boolean} [options.redis=true] - When false, skip Redis (and Redis Commander) checks for apps with requires.redis: false
200
+ * @param {boolean} [options.pgadmin=true] - Include pgAdmin in health check (only when Postgres is checked)
201
+ * @param {boolean} [options.redisCommander=true] - Include Redis Commander in health check (only when Redis is checked)
200
202
  * @param {boolean} [options.traefik=false] - Include Traefik in health check
201
203
  * @returns {Promise<Object>} Health status of each service
202
204
  *
@@ -206,10 +208,14 @@ async function waitForServices(devId = null, opts = {}) {
206
208
  */
207
209
  async function checkInfraHealth(devId = null, options = {}) {
208
210
  const developerId = devId || await config.getDeveloperId();
209
- const servicesWithHealthCheck = ['postgres', 'redis'];
211
+ const includePostgres = options.postgres !== false;
212
+ const includeRedis = options.redis !== false;
213
+ const servicesWithHealthCheck = [];
214
+ if (includePostgres) servicesWithHealthCheck.push('postgres');
215
+ if (includeRedis) servicesWithHealthCheck.push('redis');
210
216
  const servicesWithoutHealthCheck = [];
211
- if (options.pgadmin !== false) servicesWithoutHealthCheck.push('pgadmin');
212
- if (options.redisCommander !== false) servicesWithoutHealthCheck.push('redis-commander');
217
+ if (includePostgres && options.pgadmin !== false) servicesWithoutHealthCheck.push('pgadmin');
218
+ if (includeRedis && options.redisCommander !== false) servicesWithoutHealthCheck.push('redis-commander');
213
219
  if (options.traefik === true) servicesWithoutHealthCheck.push('traefik');
214
220
  const health = {};
215
221
  const lookupOptions = options.strict ? { strict: true } : {};
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Synchronous fs helpers that use `global.__AIFABRIX_NODE_FS_UNMOCKED__` from
3
+ * `tests/capture-real-fs.js` when present so Jest `jest.mock('fs')` and partial
4
+ * `jest.mock('internal/node-fs')` cannot break config, schema, or builder scans.
5
+ *
6
+ * @fileoverview Real filesystem sync for config, schemas, urls.local registry, url:// resolution
7
+ */
8
+ 'use strict';
9
+
10
+ /**
11
+ * @returns {import('node:fs')|null}
12
+ */
13
+ function unmockedFsSnapshot() {
14
+ const g = typeof globalThis !== 'undefined' ? globalThis : global;
15
+ return g && g.__AIFABRIX_NODE_FS_UNMOCKED__ ? g.__AIFABRIX_NODE_FS_UNMOCKED__ : null;
16
+ }
17
+
18
+ function delegate(name, args) {
19
+ const snap = unmockedFsSnapshot();
20
+ if (snap && typeof snap[name] === 'function') {
21
+ return snap[name](...args);
22
+ }
23
+ // When Jest is active, bypass any manual module mocks of node:fs.
24
+ // This keeps schema/catalog reads stable even if a suite does jest.mock('node:fs').
25
+ const fs =
26
+ typeof jest !== 'undefined' && typeof jest.requireActual === 'function'
27
+ ? jest.requireActual('node:fs')
28
+ : require('node:fs');
29
+ return fs[name](...args);
30
+ }
31
+
32
+ /**
33
+ * @param {string} p
34
+ * @returns {boolean}
35
+ */
36
+ function existsSync(p) {
37
+ return delegate('existsSync', [p]);
38
+ }
39
+
40
+ /**
41
+ * Read file via snapshot/delegate only — not the same binding as {@link readFileSync} on `module.exports`.
42
+ * Tests may `jest.spyOn(exports, 'readFileSync')`; this function stays the real implementation for env/PIN paths.
43
+ * @param {string} p
44
+ * @param {string|import('node:fs').ObjectEncodingOptions} [enc]
45
+ * @returns {string|Buffer}
46
+ */
47
+ function readFileSyncDirect(p, enc) {
48
+ return delegate('readFileSync', enc !== undefined ? [p, enc] : [p]);
49
+ }
50
+
51
+ /**
52
+ * @param {string} p
53
+ * @param {string|import('node:fs').ObjectEncodingOptions} [enc]
54
+ * @returns {string|Buffer}
55
+ */
56
+ function readFileSync(p, enc) {
57
+ return readFileSyncDirect(p, enc);
58
+ }
59
+
60
+ /**
61
+ * @param {string} p
62
+ * @param {string|Buffer} data
63
+ * @param {object|string} [opts]
64
+ * @returns {void}
65
+ */
66
+ function writeFileSync(p, data, opts) {
67
+ return delegate('writeFileSync', opts !== undefined ? [p, data, opts] : [p, data]);
68
+ }
69
+
70
+ /**
71
+ * @param {string} p
72
+ * @param {object} [opts]
73
+ * @returns {string|undefined}
74
+ */
75
+ function mkdirSync(p, opts) {
76
+ return delegate('mkdirSync', opts !== undefined ? [p, opts] : [p]);
77
+ }
78
+
79
+ /**
80
+ * @param {string} p
81
+ * @returns {import('node:fs').Stats}
82
+ */
83
+ function statSync(p) {
84
+ return delegate('statSync', [p]);
85
+ }
86
+
87
+ /**
88
+ * @param {string} p
89
+ * @param {object} [opts]
90
+ * @returns {string[]|import('node:fs').Dirent[]}
91
+ */
92
+ function readdirSync(p, opts) {
93
+ return delegate('readdirSync', opts !== undefined ? [p, opts] : [p]);
94
+ }
95
+
96
+ module.exports = {
97
+ existsSync,
98
+ readFileSync,
99
+ readFileSyncDirect,
100
+ writeFileSync,
101
+ mkdirSync,
102
+ statSync,
103
+ readdirSync
104
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @fileoverview Filesystem helpers that ignore jest.spyOn on require('node:fs') for sync reads.
3
+ * Uses jest.requireActual when Jest is active so .bind() captures native implementations even if
4
+ * this module loads after another suite spied fs. watch still delegates to live fs so tests can spy it.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ /**
10
+ * Bound native fs from tests/capture-real-fs.js (Jest setupFiles, before mocks).
11
+ * @returns {Record<string, unknown>|null}
12
+ */
13
+ function getUnmockedFsSnapshot() {
14
+ const g = typeof globalThis !== 'undefined' ? globalThis : global;
15
+ return g && g.__AIFABRIX_NODE_FS_UNMOCKED__ ? g.__AIFABRIX_NODE_FS_UNMOCKED__ : null;
16
+ }
17
+
18
+ function getRealFs() {
19
+ const snap = getUnmockedFsSnapshot();
20
+ if (snap) {
21
+ return snap;
22
+ }
23
+ // When Jest is active, bypass manual module mocks (singleton may still carry spyOn).
24
+ if (typeof jest !== 'undefined' && typeof jest.requireActual === 'function') {
25
+ return jest.requireActual('node:fs');
26
+ }
27
+ return require('node:fs');
28
+ }
29
+
30
+ /**
31
+ * @param {import('node:fs')} fsModule
32
+ * @param {string} name
33
+ * @returns {unknown}
34
+ */
35
+ function bindSync(fsModule, name) {
36
+ const fn = fsModule[name];
37
+ return typeof fn === 'function' ? fn.bind(fsModule) : fn;
38
+ }
39
+
40
+ function liveFs() {
41
+ return require('node:fs');
42
+ }
43
+
44
+ /**
45
+ * Resolve real fs on each access so callers still see the filesystem after other
46
+ * suites replace require('fs') with mocks (schema-loader, schema sync helpers).
47
+ * @returns {typeof import('node:fs')}
48
+ */
49
+ function freshRealFs() {
50
+ return getRealFs();
51
+ }
52
+
53
+ /**
54
+ * @returns {Record<string, unknown>}
55
+ */
56
+ function buildBoundFs() {
57
+ const snap = getUnmockedFsSnapshot();
58
+ const rf = freshRealFs();
59
+ const live = liveFs();
60
+ const promiseHost = live.promises || {};
61
+ const boundPromises = {
62
+ mkdir: bindSync(promiseHost, 'mkdir'),
63
+ writeFile: bindSync(promiseHost, 'writeFile'),
64
+ appendFile: bindSync(promiseHost, 'appendFile'),
65
+ readFile: bindSync(promiseHost, 'readFile')
66
+ };
67
+ if (snap) {
68
+ return {
69
+ existsSync: snap.existsSync,
70
+ readFileSync: snap.readFileSync,
71
+ writeFileSync: snap.writeFileSync,
72
+ mkdirSync: snap.mkdirSync,
73
+ readdirSync: snap.readdirSync,
74
+ statSync: snap.statSync,
75
+ watch: (...args) => live.watch(...args),
76
+ promises: boundPromises
77
+ };
78
+ }
79
+ return {
80
+ existsSync: bindSync(rf, 'existsSync'),
81
+ readFileSync: bindSync(rf, 'readFileSync'),
82
+ writeFileSync: bindSync(rf, 'writeFileSync'),
83
+ mkdirSync: bindSync(rf, 'mkdirSync'),
84
+ readdirSync: bindSync(rf, 'readdirSync'),
85
+ statSync: bindSync(rf, 'statSync'),
86
+ watch: (...args) => live.watch(...args),
87
+ promises: boundPromises
88
+ };
89
+ }
90
+
91
+ /**
92
+ * @returns {ReturnType<typeof buildBoundFs>}
93
+ */
94
+ function nodeFs() {
95
+ return buildBoundFs();
96
+ }
97
+
98
+ module.exports = { nodeFs };
@@ -0,0 +1,173 @@
1
+ /**
2
+ * @fileoverview Index-aware local PostgreSQL URL/password defaults for databases-{app}-{i}-* keys
3
+ * @author AI Fabrix Team
4
+ * @version 2.1.0
5
+ */
6
+
7
+ const path = require('path');
8
+ const { nodeFs } = require('../internal/node-fs');
9
+ const yaml = require('js-yaml');
10
+
11
+ /**
12
+ * Shipped platform templates (when builder/<app> does not exist yet).
13
+ * Prefer Jest/global.PROJECT_ROOT when that tree contains templates (stable under isolated projects).
14
+ * @returns {string}
15
+ */
16
+ function getTemplatesApplicationsRoot() {
17
+ const fallback = path.join(__dirname, '..', '..', 'templates', 'applications');
18
+ try {
19
+ if (typeof global !== 'undefined' && global.PROJECT_ROOT) {
20
+ const g = path.join(path.resolve(String(global.PROJECT_ROOT)), 'templates', 'applications');
21
+ const marker = path.join(g, 'miso-controller', 'application.yaml');
22
+ if (nodeFs().existsSync(marker)) {
23
+ return g;
24
+ }
25
+ }
26
+ } catch {
27
+ /* ignore */
28
+ }
29
+ return fallback;
30
+ }
31
+
32
+ /**
33
+ * PostgreSQL role name from database name (same as compose pgUserName helper).
34
+ * @param {string} dbName - Logical database name (e.g. miso-logs)
35
+ * @returns {string}
36
+ */
37
+ function pgUserFromDbName(dbName) {
38
+ if (!dbName) return '';
39
+ return `${String(dbName).replace(/-/g, '_')}_user`;
40
+ }
41
+
42
+ /**
43
+ * Local dev password aligned with infra init scripts (user base + _pass123).
44
+ * @param {string} userName - e.g. miso_user, miso_logs_user
45
+ * @returns {string}
46
+ */
47
+ function localDevPasswordFromPgUser(userName) {
48
+ const base = String(userName).replace(/_user$/i, '');
49
+ return `${base}_pass123`;
50
+ }
51
+
52
+ /**
53
+ * Load requires.databases from application config if present (read-only; no rename).
54
+ * @param {string} appDir - Absolute path to builder/integration app folder
55
+ * @returns {Array<{name?: string}>|null}
56
+ */
57
+ function loadRequiresDatabasesArray(appDir) {
58
+ if (!appDir || !nodeFs().existsSync(appDir)) return null;
59
+ const candidates = ['application.yaml', 'application.yml', 'variables.yaml'];
60
+ for (const name of candidates) {
61
+ const p = path.join(appDir, name);
62
+ if (!nodeFs().existsSync(p)) continue;
63
+ try {
64
+ const doc = yaml.load(nodeFs().readFileSync(p, 'utf8'));
65
+ const dbs = doc?.requires?.databases;
66
+ if (Array.isArray(dbs) && dbs.length > 0) return dbs;
67
+ } catch {
68
+ /* ignore */
69
+ }
70
+ }
71
+ return null;
72
+ }
73
+
74
+ /**
75
+ * Load requires.databases from shipped Builder template (templates/applications/<appKey>/application.yaml).
76
+ * @param {string} appKey - Application key
77
+ * @returns {Array<{name?: string}>|null}
78
+ */
79
+ function loadShippedRequiresDatabases(appKey) {
80
+ if (!appKey || typeof appKey !== 'string') return null;
81
+ const p = path.join(getTemplatesApplicationsRoot(), appKey, 'application.yaml');
82
+ if (!nodeFs().existsSync(p)) return null;
83
+ try {
84
+ const doc = yaml.load(nodeFs().readFileSync(p, 'utf8'));
85
+ const dbs = doc?.requires?.databases;
86
+ return Array.isArray(dbs) && dbs.length > 0 ? dbs : null;
87
+ } catch {
88
+ return null;
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Resolve logical PostgreSQL database name for a databases-{appKey}-{index}-* key.
94
+ * @param {string} appKey - App key segment from secret key
95
+ * @param {number} index - Database index
96
+ * @param {string|null} appDir - Optional app directory to read application.yaml
97
+ * @returns {string}
98
+ */
99
+ function resolveLogicalDbName(appKey, index, appDir) {
100
+ const fromDir = appDir ? loadRequiresDatabasesArray(appDir) : null;
101
+ const dbs = fromDir || loadShippedRequiresDatabases(appKey);
102
+ if (dbs && dbs[index] && dbs[index].name) {
103
+ return String(dbs[index].name);
104
+ }
105
+ if (index === 0) {
106
+ return String(appKey).replace(/-/g, '_');
107
+ }
108
+ return `${String(appKey).replace(/-/g, '_')}_${index}`;
109
+ }
110
+
111
+ /**
112
+ * Parse databases-{appKey}-{index}-(urlKeyVault|passwordKeyVault).
113
+ * @param {string} key - Secret key
114
+ * @returns {{ appKey: string, index: number, kind: 'url'|'password' }|null}
115
+ */
116
+ function parseDatabaseSecretKey(key) {
117
+ const m = String(key).match(/^databases-([a-z0-9-]+)-(\d+)-(urlKeyVault|passwordKeyVault)$/i);
118
+ if (!m) return null;
119
+ return {
120
+ appKey: m[1],
121
+ index: parseInt(m[2], 10),
122
+ kind: m[3].toLowerCase().startsWith('url') ? 'url' : 'password'
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Build postgres URL with unresolved host/port placeholders.
128
+ * @param {string} dbName - Database name in connection path (may contain hyphens)
129
+ * @param {string} userName - DB user
130
+ * @param {string} password - DB password
131
+ * @returns {string}
132
+ */
133
+ function buildPostgresUrlTemplate(dbName, userName, password) {
134
+ return `postgresql://${userName}:${password}@\${DB_HOST}:\${DB_PORT}/${dbName}`;
135
+ }
136
+
137
+ /**
138
+ * Generate password value for databases-*-passwordKeyVault.
139
+ * @param {string} key - Full secret key
140
+ * @param {string|null} appDir - Optional app directory for YAML lookup
141
+ * @returns {string|null} Value or null if key does not match
142
+ */
143
+ function generateDatabasePasswordValueForKey(key, appDir = null) {
144
+ const parsed = parseDatabaseSecretKey(key);
145
+ if (!parsed || parsed.kind !== 'password') return null;
146
+ const dbName = resolveLogicalDbName(parsed.appKey, parsed.index, appDir);
147
+ const user = pgUserFromDbName(dbName);
148
+ return localDevPasswordFromPgUser(user);
149
+ }
150
+
151
+ /**
152
+ * Generate URL value for databases-*-urlKeyVault.
153
+ * @param {string} key - Full secret key
154
+ * @param {string|null} appDir - Optional app directory for YAML lookup
155
+ * @returns {string|null} Value or null if key does not match
156
+ */
157
+ function generateDatabaseUrlValueForKey(key, appDir = null) {
158
+ const parsed = parseDatabaseSecretKey(key);
159
+ if (!parsed || parsed.kind !== 'url') return null;
160
+ const dbName = resolveLogicalDbName(parsed.appKey, parsed.index, appDir);
161
+ const user = pgUserFromDbName(dbName);
162
+ const pass = localDevPasswordFromPgUser(user);
163
+ return buildPostgresUrlTemplate(dbName, user, pass);
164
+ }
165
+
166
+ module.exports = {
167
+ parseDatabaseSecretKey,
168
+ generateDatabasePasswordValueForKey,
169
+ generateDatabaseUrlValueForKey,
170
+ loadRequiresDatabasesArray,
171
+ loadShippedRequiresDatabases,
172
+ resolveLogicalDbName
173
+ };
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @fileoverview Discover kv:// keys for up-infra from workspace templates and application.yaml
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const fsRealSync = require('../internal/fs-real-sync');
8
+ const path = require('path');
9
+ const { loadRequiresDatabasesArray } = require('./database-secret-values');
10
+
11
+ /**
12
+ * Extract kv:// secret key names from env template content (active lines only).
13
+ * @param {string} content - env.template body
14
+ * @returns {string[]}
15
+ */
16
+ function extractKvKeysFromEnvContent(content) {
17
+ if (!content || typeof content !== 'string') return [];
18
+ const keys = new Set();
19
+ const kvPattern = /kv:\/\/([a-zA-Z0-9-_]+)/g;
20
+ const lines = content.split('\n');
21
+ for (const line of lines) {
22
+ const t = line.trim();
23
+ if (t === '' || t.startsWith('#')) continue;
24
+ let m;
25
+ kvPattern.lastIndex = 0;
26
+ while ((m = kvPattern.exec(line)) !== null) {
27
+ keys.add(m[1]);
28
+ }
29
+ }
30
+ return [...keys];
31
+ }
32
+
33
+ /**
34
+ * List app directories for discovery (builder first, then integration-only apps).
35
+ * @param {object} pathsUtil - paths module
36
+ * @returns {{ appKey: string, dir: string }[]}
37
+ */
38
+ function listAppDirsForDiscovery(pathsUtil) {
39
+ const out = [];
40
+ const seen = new Set();
41
+ for (const name of pathsUtil.listBuilderAppNames()) {
42
+ const dir = pathsUtil.getBuilderPath(name);
43
+ if (fsRealSync.existsSync(dir)) {
44
+ out.push({ appKey: name, dir });
45
+ seen.add(name);
46
+ }
47
+ }
48
+ for (const name of pathsUtil.listIntegrationAppNames()) {
49
+ if (seen.has(name)) continue;
50
+ const dir = pathsUtil.getIntegrationPath(name);
51
+ if (fsRealSync.existsSync(dir)) {
52
+ out.push({ appKey: name, dir });
53
+ }
54
+ }
55
+ return out;
56
+ }
57
+
58
+ /**
59
+ * Derive databases-{appKey}-{i}-url/password keys from requires.databases length.
60
+ * @param {object} pathsUtil - paths module
61
+ * @returns {string[]}
62
+ */
63
+ function deriveDatabaseKvKeysFromWorkspace(pathsUtil) {
64
+ const keys = new Set();
65
+ for (const { appKey, dir } of listAppDirsForDiscovery(pathsUtil)) {
66
+ const dbs = loadRequiresDatabasesArray(dir);
67
+ if (!Array.isArray(dbs) || dbs.length === 0) continue;
68
+ for (let i = 0; i < dbs.length; i++) {
69
+ keys.add(`databases-${appKey}-${i}-urlKeyVault`);
70
+ keys.add(`databases-${appKey}-${i}-passwordKeyVault`);
71
+ }
72
+ }
73
+ return [...keys];
74
+ }
75
+
76
+ /**
77
+ * Keys from env.template files whose catalog entry includes the given hook (e.g. upInfra).
78
+ * @param {object} pathsUtil - paths module
79
+ * @param {string} hook - ensureOn hook name
80
+ * @param {{ keyMatchesEnsureHook: Function }} catalog - loaded catalog API
81
+ * @returns {string[]}
82
+ */
83
+ function discoverKvKeysFromEnvTemplatesForHook(pathsUtil, hook, catalog) {
84
+ const keys = new Set();
85
+ for (const { dir } of listAppDirsForDiscovery(pathsUtil)) {
86
+ const envPath = path.join(dir, 'env.template');
87
+ if (!fsRealSync.existsSync(envPath)) continue;
88
+ let content;
89
+ try {
90
+ content = fsRealSync.readFileSync(envPath, 'utf8');
91
+ } catch {
92
+ continue;
93
+ }
94
+ for (const k of extractKvKeysFromEnvContent(content)) {
95
+ if (catalog.keyMatchesEnsureHook(k, hook)) keys.add(k);
96
+ }
97
+ }
98
+ return [...keys];
99
+ }
100
+
101
+ /**
102
+ * Full key list for ensureInfraSecrets: catalog exact upInfra + standard miso DB + derived DB + template hooks.
103
+ * @param {{ getEnsureOnKeys: Function, keyMatchesEnsureHook: Function }} catalog
104
+ * @param {object} pathsUtil - paths module
105
+ * @returns {string[]}
106
+ */
107
+ function getAllInfraEnsureKeys(catalog, pathsUtil) {
108
+ const set = new Set(catalog.getEnsureOnKeys('upInfra'));
109
+ for (const k of catalog.getStandardUpInfraBootstrapKeys()) set.add(k);
110
+ for (const k of deriveDatabaseKvKeysFromWorkspace(pathsUtil)) set.add(k);
111
+ for (const k of discoverKvKeysFromEnvTemplatesForHook(pathsUtil, 'upInfra', catalog)) set.add(k);
112
+ return [...set].sort();
113
+ }
114
+
115
+ module.exports = {
116
+ extractKvKeysFromEnvContent,
117
+ deriveDatabaseKvKeysFromWorkspace,
118
+ discoverKvKeysFromEnvTemplatesForHook,
119
+ getAllInfraEnsureKeys,
120
+ listAppDirsForDiscovery
121
+ };