@aifabrix/builder 2.42.1 → 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 (392) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/README.md +2 -2
  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 +157 -0
  7. package/integration/{hubspot → hubspot-test}/application.json +6 -6
  8. package/integration/{hubspot → hubspot-test}/create-hubspot.js +10 -10
  9. package/integration/hubspot-test/env.template +4 -0
  10. package/integration/hubspot-test/hubspot-test-datasource-company.json +138 -0
  11. package/integration/hubspot-test/hubspot-test-datasource-contact.json +146 -0
  12. package/integration/hubspot-test/hubspot-test-datasource-deal.json +146 -0
  13. package/integration/hubspot-test/hubspot-test-datasource-users.json +76 -0
  14. package/integration/{hubspot/hubspot-deploy.json → hubspot-test/hubspot-test-deploy.json} +201 -24
  15. package/integration/{hubspot/hubspot-system.json → hubspot-test/hubspot-test-system.json} +8 -7
  16. package/integration/hubspot-test/rbac.json +166 -0
  17. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-credential-real.yaml +3 -3
  18. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-env-vars.yaml +2 -2
  19. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-add-datasource.yaml +1 -1
  20. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-create.yaml +1 -1
  21. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-select.yaml +1 -1
  22. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-known-platform.yaml +1 -1
  23. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
  24. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-mode.yaml +1 -1
  25. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-file.yaml +1 -1
  26. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-url.yaml +1 -1
  27. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-source.yaml +1 -1
  28. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-array-test.yaml +1 -1
  29. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
  30. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
  31. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-test.yaml +1 -1
  32. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-test.yaml +1 -1
  33. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +1 -1
  34. package/integration/{hubspot → hubspot-test}/test-dataplane-down-tests.js +1 -7
  35. package/integration/{hubspot → hubspot-test}/test-dataplane-down.js +3 -3
  36. package/integration/{hubspot → hubspot-test}/test.js +137 -102
  37. package/integration/{hubspot → hubspot-test}/wizard-hubspot-e2e.yaml +2 -2
  38. package/integration/{hubspot → hubspot-test}/wizard-hubspot-platform.yaml +1 -1
  39. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  40. package/integration/roundtrip-test-local/README.md +144 -0
  41. package/integration/roundtrip-test-local/application.yaml +13 -0
  42. package/integration/roundtrip-test-local/env.template +15 -0
  43. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  44. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  45. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  46. package/integration/roundtrip-test-local2/README.md +144 -0
  47. package/integration/roundtrip-test-local2/application.yaml +13 -0
  48. package/integration/roundtrip-test-local2/env.template +15 -0
  49. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  50. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  51. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  52. package/integration/test/wizard.yaml +8 -0
  53. package/jest.config.default.js +10 -0
  54. package/jest.config.integration.fixtures.js +22 -0
  55. package/jest.config.integration.js +21 -18
  56. package/jest.config.isolated.js +10 -0
  57. package/jest.projects.js +288 -0
  58. package/lib/api/datasources-core.api.js +3 -3
  59. package/lib/api/dev-mtls-request.js +110 -0
  60. package/lib/api/dev-server-https.js +145 -0
  61. package/lib/api/dev.api.js +133 -144
  62. package/lib/api/index.js +0 -1
  63. package/lib/api/pipeline.api.js +67 -20
  64. package/lib/api/service-users.api.js +111 -2
  65. package/lib/api/types/dev.types.js +4 -3
  66. package/lib/api/types/pipeline.types.js +8 -5
  67. package/lib/api/types/service-users.types.js +41 -0
  68. package/lib/api/types/validation-run.types.js +56 -0
  69. package/lib/api/validation-run.api.js +99 -0
  70. package/lib/api/validation-runner.js +99 -0
  71. package/lib/app/config.js +1 -1
  72. package/lib/app/deploy-status-display.js +2 -2
  73. package/lib/app/deploy.js +7 -6
  74. package/lib/app/display.js +2 -1
  75. package/lib/app/dockerfile.js +3 -2
  76. package/lib/app/down.js +2 -1
  77. package/lib/app/helpers.js +6 -5
  78. package/lib/app/index.js +27 -8
  79. package/lib/app/list.js +7 -6
  80. package/lib/app/push.js +4 -3
  81. package/lib/app/register.js +19 -8
  82. package/lib/app/rotate-secret.js +17 -13
  83. package/lib/app/run-container-start.js +184 -0
  84. package/lib/app/run-docker-fallback.js +108 -0
  85. package/lib/app/run-env-compose.js +30 -42
  86. package/lib/app/run-helpers.js +49 -126
  87. package/lib/app/run-infra-requirements.js +30 -0
  88. package/lib/app/run-resolve-image.js +21 -0
  89. package/lib/app/run.js +74 -21
  90. package/lib/app/show-display.js +1 -1
  91. package/lib/app/show.js +1 -1
  92. package/lib/build/index.js +13 -10
  93. package/lib/cli/index.js +2 -0
  94. package/lib/cli/setup-app.help.js +67 -0
  95. package/lib/cli/setup-app.js +59 -123
  96. package/lib/cli/setup-app.test-commands.js +179 -0
  97. package/lib/cli/setup-auth.js +36 -14
  98. package/lib/cli/setup-credential-deployment.js +22 -8
  99. package/lib/cli/setup-dev-path-commands.js +124 -0
  100. package/lib/cli/setup-dev.js +190 -103
  101. package/lib/cli/setup-environment.js +11 -20
  102. package/lib/cli/setup-external-system.js +62 -22
  103. package/lib/cli/setup-infra.js +139 -47
  104. package/lib/cli/setup-parameters.js +32 -0
  105. package/lib/cli/setup-secrets.js +147 -10
  106. package/lib/cli/setup-service-user.js +146 -20
  107. package/lib/cli/setup-utility.js +47 -19
  108. package/lib/commands/app-down.js +5 -7
  109. package/lib/commands/app-install.js +14 -7
  110. package/lib/commands/app-logs.js +13 -10
  111. package/lib/commands/app-shell.js +4 -1
  112. package/lib/commands/app-test.js +25 -19
  113. package/lib/commands/app.js +22 -10
  114. package/lib/commands/auth-config.js +10 -14
  115. package/lib/commands/auth-status.js +4 -3
  116. package/lib/commands/credential-env.js +4 -3
  117. package/lib/commands/credential-list.js +5 -4
  118. package/lib/commands/credential-push.js +4 -3
  119. package/lib/commands/datasource-unified-test-cli.js +495 -0
  120. package/lib/commands/datasource-unified-test-cli.options.js +149 -0
  121. package/lib/commands/datasource-validation-cli.js +129 -0
  122. package/lib/commands/datasource.js +123 -71
  123. package/lib/commands/deployment-list.js +6 -5
  124. package/lib/commands/dev-cli-handlers.js +122 -18
  125. package/lib/commands/dev-down.js +4 -3
  126. package/lib/commands/dev-init.js +231 -116
  127. package/lib/commands/dev-show-display.js +473 -0
  128. package/lib/commands/login-credentials.js +3 -2
  129. package/lib/commands/login-device.js +4 -3
  130. package/lib/commands/login.js +5 -4
  131. package/lib/commands/logout.js +8 -7
  132. package/lib/commands/parameters-validate.js +54 -0
  133. package/lib/commands/repair-datasource.js +314 -68
  134. package/lib/commands/repair-env-template.js +16 -10
  135. package/lib/commands/repair-rbac.js +25 -19
  136. package/lib/commands/repair.js +116 -32
  137. package/lib/commands/secrets-list.js +23 -12
  138. package/lib/commands/secrets-remove-all.js +220 -0
  139. package/lib/commands/secrets-remove.js +22 -13
  140. package/lib/commands/secrets-set.js +21 -12
  141. package/lib/commands/secrets-validate.js +20 -7
  142. package/lib/commands/secure.js +10 -9
  143. package/lib/commands/service-user.js +243 -13
  144. package/lib/commands/test-e2e-external.js +27 -1
  145. package/lib/commands/up-common.js +28 -2
  146. package/lib/commands/up-dataplane.js +31 -18
  147. package/lib/commands/up-miso.js +19 -29
  148. package/lib/commands/upload.js +138 -39
  149. package/lib/commands/wizard-core-helpers.js +1 -1
  150. package/lib/commands/wizard-dataplane.js +4 -3
  151. package/lib/commands/wizard-helpers.js +3 -3
  152. package/lib/commands/wizard.js +2 -2
  153. package/lib/core/admin-secrets.js +16 -5
  154. package/lib/core/audit-logger.js +12 -4
  155. package/lib/core/config-attach-extensions.js +46 -0
  156. package/lib/core/config-runtime-paths.js +29 -0
  157. package/lib/core/config.js +59 -58
  158. package/lib/core/diff.js +3 -2
  159. package/lib/core/ensure-encryption-key.js +2 -4
  160. package/lib/core/secrets-ensure-infra.js +77 -0
  161. package/lib/core/secrets-ensure.js +120 -64
  162. package/lib/core/secrets-env-write.js +35 -7
  163. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  164. package/lib/core/secrets.js +228 -42
  165. package/lib/core/templates-env.js +4 -3
  166. package/lib/core/templates.js +1 -1
  167. package/lib/datasource/abac-validator.js +148 -0
  168. package/lib/datasource/deploy.js +75 -53
  169. package/lib/datasource/field-reference-validator.js +77 -36
  170. package/lib/datasource/integration-context.js +63 -0
  171. package/lib/datasource/list.js +8 -7
  172. package/lib/datasource/log-viewer.js +252 -0
  173. package/lib/datasource/resolve-app.js +109 -0
  174. package/lib/datasource/test-e2e.js +95 -155
  175. package/lib/datasource/test-integration.js +121 -109
  176. package/lib/datasource/unified-validation-run-body.js +65 -0
  177. package/lib/datasource/unified-validation-run-post.js +23 -0
  178. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  179. package/lib/datasource/unified-validation-run.js +92 -0
  180. package/lib/datasource/validate.js +162 -15
  181. package/lib/deployment/deployer.js +4 -3
  182. package/lib/deployment/environment.js +7 -6
  183. package/lib/deployment/push.js +17 -8
  184. package/lib/external-system/delete.js +4 -3
  185. package/lib/external-system/deploy.js +131 -53
  186. package/lib/external-system/download-helpers.js +1 -1
  187. package/lib/external-system/download.js +7 -6
  188. package/lib/external-system/generator.js +104 -14
  189. package/lib/external-system/integration-test-dispatch.js +26 -0
  190. package/lib/external-system/test-execution.js +5 -1
  191. package/lib/external-system/test-helpers.js +0 -4
  192. package/lib/external-system/test-system-level-helpers.js +110 -0
  193. package/lib/external-system/test-system-level.js +83 -44
  194. package/lib/external-system/test.js +59 -8
  195. package/lib/generator/builders.js +23 -11
  196. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  197. package/lib/generator/external-controller-manifest.js +3 -3
  198. package/lib/generator/external.js +23 -11
  199. package/lib/generator/helpers.js +71 -12
  200. package/lib/generator/index.js +8 -4
  201. package/lib/generator/split-readme.js +12 -7
  202. package/lib/generator/split-variables.js +2 -1
  203. package/lib/generator/split.js +46 -11
  204. package/lib/generator/wizard-readme.js +3 -3
  205. package/lib/generator/wizard.js +16 -13
  206. package/lib/infrastructure/compose.js +60 -6
  207. package/lib/infrastructure/helpers.js +238 -51
  208. package/lib/infrastructure/index.js +64 -37
  209. package/lib/infrastructure/services.js +21 -15
  210. package/lib/internal/fs-real-sync.js +104 -0
  211. package/lib/internal/node-fs.js +98 -0
  212. package/lib/parameters/database-secret-values.js +173 -0
  213. package/lib/parameters/infra-kv-discovery.js +121 -0
  214. package/lib/parameters/infra-parameter-catalog.js +458 -0
  215. package/lib/parameters/infra-parameter-validate.js +64 -0
  216. package/lib/schema/application-schema.json +37 -17
  217. package/lib/schema/datasource-test-run.schema.json +493 -0
  218. package/lib/schema/deployment-rules.yaml +102 -63
  219. package/lib/schema/external-datasource.schema.json +1201 -433
  220. package/lib/schema/external-system.schema.json +181 -5
  221. package/lib/schema/flag-map-validation-run.json +31 -0
  222. package/lib/schema/infra-parameter.schema.json +106 -0
  223. package/lib/schema/infra.parameter.yaml +421 -0
  224. package/lib/schema/type/credential-auth-templates.json +40 -0
  225. package/lib/schema/type/document-storage.json +213 -0
  226. package/lib/schema/type/message-service.json +123 -0
  227. package/lib/schema/type/vector-store.json +88 -0
  228. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  229. package/lib/utils/api-error-handler.js +2 -2
  230. package/lib/utils/api.js +49 -14
  231. package/lib/utils/app-config-resolver.js +23 -1
  232. package/lib/utils/app-register-api.js +3 -2
  233. package/lib/utils/app-register-auth.js +1 -1
  234. package/lib/utils/app-register-config.js +4 -4
  235. package/lib/utils/app-register-display.js +3 -2
  236. package/lib/utils/app-register-validator.js +3 -2
  237. package/lib/utils/app-run-containers.js +26 -22
  238. package/lib/utils/app-scoped-config.js +31 -0
  239. package/lib/utils/app-service-env-from-builder.js +164 -0
  240. package/lib/utils/build-copy.js +1 -1
  241. package/lib/utils/build-helpers.js +20 -20
  242. package/lib/utils/build-resolve-image.js +165 -0
  243. package/lib/utils/cli-layout-chalk.js +8 -0
  244. package/lib/utils/cli-test-layout-chalk.js +267 -0
  245. package/lib/utils/cli-utils.js +88 -11
  246. package/lib/utils/compose-db-passwords.js +138 -0
  247. package/lib/utils/compose-generate-docker-compose.js +216 -0
  248. package/lib/utils/compose-generator.js +197 -291
  249. package/lib/utils/compose-miso-env.js +18 -0
  250. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  251. package/lib/utils/config-paths.js +209 -6
  252. package/lib/utils/config-scoped-resources-preference.js +41 -0
  253. package/lib/utils/controller-deployment-outcome.js +68 -0
  254. package/lib/utils/credential-display.js +2 -2
  255. package/lib/utils/credential-secrets-env.js +16 -1
  256. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  257. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  258. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  259. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  260. package/lib/utils/datasource-test-run-display.js +442 -0
  261. package/lib/utils/datasource-test-run-exit.js +58 -0
  262. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  263. package/lib/utils/datasource-test-run-report-version.js +51 -0
  264. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  265. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  266. package/lib/utils/datasource-validation-watch.js +266 -0
  267. package/lib/utils/declarative-url-ports.js +47 -0
  268. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  269. package/lib/utils/dev-ca-install.js +185 -23
  270. package/lib/utils/dev-cert-helper.js +266 -17
  271. package/lib/utils/dev-hosts-helper.js +307 -0
  272. package/lib/utils/dev-init-cert-hints.js +37 -0
  273. package/lib/utils/dev-init-health-messages.js +52 -0
  274. package/lib/utils/dev-init-resolve.js +86 -0
  275. package/lib/utils/dev-init-ssh-merge.js +65 -0
  276. package/lib/utils/dev-ssh-config-helper.js +196 -0
  277. package/lib/utils/dev-user-groups.js +93 -0
  278. package/lib/utils/docker-build.js +42 -17
  279. package/lib/utils/docker-exec.js +28 -0
  280. package/lib/utils/docker-manifest-public-port.js +116 -0
  281. package/lib/utils/docker-not-running-hint.js +52 -0
  282. package/lib/utils/docker.js +98 -11
  283. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  284. package/lib/utils/env-config-loader.js +10 -91
  285. package/lib/utils/env-copy.js +19 -10
  286. package/lib/utils/env-map.js +42 -11
  287. package/lib/utils/env-template.js +2 -2
  288. package/lib/utils/environment-scoped-resources.js +144 -0
  289. package/lib/utils/error-formatter.js +125 -9
  290. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  291. package/lib/utils/error-formatters/network-errors.js +2 -1
  292. package/lib/utils/error-formatters/permission-errors.js +2 -1
  293. package/lib/utils/error-formatters/validation-errors.js +2 -1
  294. package/lib/utils/external-env-template.js +180 -0
  295. package/lib/utils/external-readme.js +8 -1
  296. package/lib/utils/external-system-display.js +277 -136
  297. package/lib/utils/external-system-local-test-tty.js +389 -0
  298. package/lib/utils/external-system-readiness-core.js +377 -0
  299. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  300. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  301. package/lib/utils/external-system-readiness-display.js +186 -0
  302. package/lib/utils/external-system-test-helpers.js +24 -6
  303. package/lib/utils/external-system-validators.js +32 -14
  304. package/lib/utils/health-check-url.js +119 -0
  305. package/lib/utils/health-check.js +59 -25
  306. package/lib/utils/help-builder.js +14 -13
  307. package/lib/utils/image-version.js +4 -8
  308. package/lib/utils/infra-containers.js +4 -7
  309. package/lib/utils/infra-env-defaults.js +162 -0
  310. package/lib/utils/infra-status-display.js +167 -0
  311. package/lib/utils/infra-status.js +16 -8
  312. package/lib/utils/local-secrets.js +29 -7
  313. package/lib/utils/paths.js +136 -48
  314. package/lib/utils/port-resolver.js +10 -23
  315. package/lib/utils/redis-env-scope.js +62 -0
  316. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  317. package/lib/utils/remote-builder-validation.js +99 -0
  318. package/lib/utils/remote-dev-auth.js +117 -21
  319. package/lib/utils/remote-docker-env.js +67 -15
  320. package/lib/utils/remote-secrets-loader.js +13 -4
  321. package/lib/utils/resolve-docker-image-ref.js +124 -0
  322. package/lib/utils/schema-loader.js +22 -9
  323. package/lib/utils/secrets-bash-kv.js +25 -0
  324. package/lib/utils/secrets-generator.js +171 -51
  325. package/lib/utils/secrets-helpers.js +70 -59
  326. package/lib/utils/secrets-kv-scope.js +60 -0
  327. package/lib/utils/secrets-utils.js +35 -37
  328. package/lib/utils/secrets-validation.js +3 -1
  329. package/lib/utils/secrets-yaml-preserve.js +109 -0
  330. package/lib/utils/secure-file-permissions.js +91 -0
  331. package/lib/utils/ssh-key-helper.js +4 -2
  332. package/lib/utils/template-helpers.js +2 -2
  333. package/lib/utils/test-log-writer.js +3 -3
  334. package/lib/utils/token-manager.js +37 -5
  335. package/lib/utils/url-declarative-public-base.js +188 -0
  336. package/lib/utils/url-declarative-resolve-build.js +493 -0
  337. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  338. package/lib/utils/url-declarative-resolve.js +220 -0
  339. package/lib/utils/url-declarative-token-parse.js +74 -0
  340. package/lib/utils/url-declarative-url-flags.js +50 -0
  341. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  342. package/lib/utils/url-public-path-prefix.js +34 -0
  343. package/lib/utils/urls-local-registry.js +220 -0
  344. package/lib/utils/validation-report-tty-kit.js +77 -0
  345. package/lib/utils/validation-run-poll.js +89 -0
  346. package/lib/utils/validation-run-post-retry.js +73 -0
  347. package/lib/utils/validation-run-request.js +98 -0
  348. package/lib/utils/variable-transformer.js +21 -4
  349. package/lib/utils/yaml-preserve.js +78 -1
  350. package/lib/validation/datasource-warnings.js +56 -0
  351. package/lib/validation/env-template-auth.js +50 -2
  352. package/lib/validation/external-manifest-validator.js +35 -7
  353. package/lib/validation/validate-display.js +37 -31
  354. package/lib/validation/validate.js +9 -10
  355. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  356. package/lib/validation/validator.js +32 -78
  357. package/lib/validation/wizard-config-validator.js +2 -1
  358. package/package.json +11 -3
  359. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  360. package/scripts/diagnose-cli.js +150 -0
  361. package/scripts/install-local.js +304 -55
  362. package/templates/README.md +15 -2
  363. package/templates/applications/dataplane/application.yaml +52 -2
  364. package/templates/applications/dataplane/env.template +80 -18
  365. package/templates/applications/dataplane/rbac.yaml +8 -0
  366. package/templates/applications/keycloak/application.yaml +9 -1
  367. package/templates/applications/keycloak/env.template +15 -6
  368. package/templates/applications/miso-controller/application.yaml +10 -2
  369. package/templates/applications/miso-controller/env.template +55 -14
  370. package/templates/applications/miso-controller/rbac.yaml +5 -0
  371. package/templates/external-system/README.md.hbs +20 -7
  372. package/templates/external-system/deploy.js.hbs +5 -5
  373. package/templates/external-system/env.template.hbs +22 -0
  374. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  375. package/templates/infra/compose.yaml.hbs +20 -4
  376. package/templates/python/docker-compose.hbs +16 -0
  377. package/templates/typescript/docker-compose.hbs +16 -0
  378. package/integration/hubspot/README.md +0 -102
  379. package/integration/hubspot/env.template +0 -4
  380. package/integration/hubspot/hubspot-datasource-company.json +0 -541
  381. package/integration/hubspot/hubspot-datasource-contact.json +0 -639
  382. package/integration/hubspot/hubspot-datasource-deal.json +0 -588
  383. package/integration/hubspot/hubspot-datasource-users.json +0 -116
  384. package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  385. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  386. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  387. package/lib/api/external-test.api.js +0 -111
  388. package/lib/schema/env-config.yaml +0 -43
  389. /package/integration/{hubspot → hubspot-test}/companies.json +0 -0
  390. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-app-name.yaml +0 -0
  391. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-missing-app.yaml +0 -0
  392. /package/integration/{hubspot → hubspot-test}/test-dataplane-down-helpers.js +0 -0
@@ -10,28 +10,37 @@
10
10
  const chalk = require('chalk');
11
11
  const logger = require('../utils/logger');
12
12
  const { handleCommandError } = require('../utils/cli-utils');
13
- const { runServiceUserCreate } = require('../commands/service-user');
13
+ const {
14
+ runServiceUserCreate,
15
+ runServiceUserList,
16
+ runServiceUserRotateSecret,
17
+ runServiceUserDelete,
18
+ runServiceUserUpdateGroups,
19
+ runServiceUserUpdateRedirectUris
20
+ } = require('../commands/service-user');
14
21
 
15
- /**
16
- * Sets up service-user commands
17
- * @param {Command} program - Commander program instance
18
- */
19
- function setupServiceUserCommands(program) {
20
- const serviceUser = program
21
- .command('service-user')
22
- .description('Create and manage service users (API clients) for integrations and CI')
23
- .addHelpText('after', `
22
+ const HELP_AFTER = `
24
23
  Service users are dedicated accounts for integrations, CI pipelines, or API clients.
25
- The controller returns a one-time clientSecret on create—save it immediately; it cannot be retrieved again.
24
+ Use: list (service-user:read), create (service-user:create), rotate-secret, update-groups, update-redirect-uris (service-user:update), delete (service-user:delete).
25
+ The controller returns a one-time clientSecret on create and rotate-secret—save it immediately; it cannot be retrieved again.
26
26
 
27
- Example:
28
- $ aifabrix service-user create -u api-client-001 -e api@example.com \\
29
- --redirect-uris "https://app.example.com/callback" --group-names "AI-Fabrix-Developers"
27
+ Examples:
28
+ $ aifabrix service-user create -u postman -e postman@aifabrix.dev \\
29
+ --redirect-uris https://oauth.pstmn.io/v1/callback --group-names AI-Fabrix-Platform-Admins
30
+ $ aifabrix service-user list
31
+ $ aifabrix service-user rotate-secret --id <uuid>
32
+ $ aifabrix service-user delete --id <uuid>
33
+ $ aifabrix service-user update-groups --id <uuid> --group-names Group1,Group2
34
+ $ aifabrix service-user update-redirect-uris --id <uuid> --redirect-uris https://app.example.com/callback
30
35
 
31
- Required: permission service-user:create on the controller. Run "aifabrix login" first.`);
36
+ Run "aifabrix login" first.`;
37
+
38
+ function parseOptionalInt(val) {
39
+ return (val !== undefined && val !== null) ? parseInt(val, 10) : undefined;
40
+ }
32
41
 
33
- serviceUser
34
- .command('create')
42
+ function addCreateCommand(serviceUser) {
43
+ serviceUser.command('create')
35
44
  .description('Create a service user and receive a one-time clientSecret (save it now; it will not be shown again)')
36
45
  .option('--controller <url>', 'Controller base URL (default: from config)')
37
46
  .option('-u, --username <username>', 'Service user username (required)')
@@ -41,15 +50,14 @@ Required: permission service-user:create on the controller. Run "aifabrix login"
41
50
  .option('-d, --description <description>', 'Description for the service user')
42
51
  .action(async(options) => {
43
52
  try {
44
- const opts = {
53
+ await runServiceUserCreate({
45
54
  controller: options.controller,
46
55
  username: options.username,
47
56
  email: options.email,
48
57
  redirectUris: options.redirectUris,
49
58
  groupNames: options.groupNames,
50
59
  description: options.description
51
- };
52
- await runServiceUserCreate(opts);
60
+ });
53
61
  } catch (error) {
54
62
  logger.error(chalk.red(`Error: ${error.message}`));
55
63
  handleCommandError(error, 'service-user create');
@@ -58,4 +66,122 @@ Required: permission service-user:create on the controller. Run "aifabrix login"
58
66
  });
59
67
  }
60
68
 
69
+ function addListCommand(serviceUser) {
70
+ serviceUser.command('list')
71
+ .description('List service users (supports pagination and search)')
72
+ .option('--controller <url>', 'Controller base URL (default: from config)')
73
+ .option('--page <n>', 'Page number')
74
+ .option('--page-size <n>', 'Items per page')
75
+ .option('--search <term>', 'Search term')
76
+ .option('--sort <field>', 'Sort field/direction')
77
+ .option('--filter <expr>', 'Filter expression')
78
+ .action(async(options) => {
79
+ try {
80
+ await runServiceUserList({
81
+ controller: options.controller,
82
+ page: parseOptionalInt(options.page),
83
+ pageSize: parseOptionalInt(options.pageSize),
84
+ search: options.search,
85
+ sort: options.sort,
86
+ filter: options.filter
87
+ });
88
+ } catch (error) {
89
+ logger.error(chalk.red(`Error: ${error.message}`));
90
+ handleCommandError(error, 'service-user list');
91
+ process.exit(1);
92
+ }
93
+ });
94
+ }
95
+
96
+ function addRotateSecretCommand(serviceUser) {
97
+ serviceUser.command('rotate-secret')
98
+ .description('Rotate (regenerate) secret for a service user; new secret shown once only')
99
+ .option('--controller <url>', 'Controller base URL (default: from config)')
100
+ .option('--id <uuid>', 'Service user ID (required)')
101
+ .action(async(options) => {
102
+ try {
103
+ await runServiceUserRotateSecret({ controller: options.controller, id: options.id });
104
+ } catch (error) {
105
+ logger.error(chalk.red(`Error: ${error.message}`));
106
+ handleCommandError(error, 'service-user rotate-secret');
107
+ process.exit(1);
108
+ }
109
+ });
110
+ }
111
+
112
+ function addDeleteCommand(serviceUser) {
113
+ serviceUser.command('delete')
114
+ .description('Delete (deactivate) a service user')
115
+ .option('--controller <url>', 'Controller base URL (default: from config)')
116
+ .option('--id <uuid>', 'Service user ID (required)')
117
+ .action(async(options) => {
118
+ try {
119
+ await runServiceUserDelete({ controller: options.controller, id: options.id });
120
+ } catch (error) {
121
+ logger.error(chalk.red(`Error: ${error.message}`));
122
+ handleCommandError(error, 'service-user delete');
123
+ process.exit(1);
124
+ }
125
+ });
126
+ }
127
+
128
+ function addUpdateGroupsCommand(serviceUser) {
129
+ serviceUser.command('update-groups')
130
+ .description('Update group assignments for a service user')
131
+ .option('--controller <url>', 'Controller base URL (default: from config)')
132
+ .option('--id <uuid>', 'Service user ID (required)')
133
+ .option('--group-names <names>', 'Comma-separated group names (required)')
134
+ .action(async(options) => {
135
+ try {
136
+ await runServiceUserUpdateGroups({
137
+ controller: options.controller,
138
+ id: options.id,
139
+ groupNames: options.groupNames
140
+ });
141
+ } catch (error) {
142
+ logger.error(chalk.red(`Error: ${error.message}`));
143
+ handleCommandError(error, 'service-user update-groups');
144
+ process.exit(1);
145
+ }
146
+ });
147
+ }
148
+
149
+ function addUpdateRedirectUrisCommand(serviceUser) {
150
+ serviceUser.command('update-redirect-uris')
151
+ .description('Update redirect URIs for a service user (min 1)')
152
+ .option('--controller <url>', 'Controller base URL (default: from config)')
153
+ .option('--id <uuid>', 'Service user ID (required)')
154
+ .option('--redirect-uris <uris>', 'Comma-separated redirect URIs (required, min 1)')
155
+ .action(async(options) => {
156
+ try {
157
+ await runServiceUserUpdateRedirectUris({
158
+ controller: options.controller,
159
+ id: options.id,
160
+ redirectUris: options.redirectUris
161
+ });
162
+ } catch (error) {
163
+ logger.error(chalk.red(`Error: ${error.message}`));
164
+ handleCommandError(error, 'service-user update-redirect-uris');
165
+ process.exit(1);
166
+ }
167
+ });
168
+ }
169
+
170
+ /**
171
+ * Sets up service-user commands
172
+ * @param {Command} program - Commander program instance
173
+ */
174
+ function setupServiceUserCommands(program) {
175
+ const serviceUser = program
176
+ .command('service-user')
177
+ .description('OAuth service users on Controller (integrations, CI)')
178
+ .addHelpText('after', HELP_AFTER);
179
+ addCreateCommand(serviceUser);
180
+ addListCommand(serviceUser);
181
+ addRotateSecretCommand(serviceUser);
182
+ addDeleteCommand(serviceUser);
183
+ addUpdateGroupsCommand(serviceUser);
184
+ addUpdateRedirectUrisCommand(serviceUser);
185
+ }
186
+
61
187
  module.exports = { setupServiceUserCommands };
@@ -1,3 +1,4 @@
1
+ const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * CLI utility command setup (resolve, json, split-json, show, validate, diff).
3
4
  *
@@ -15,6 +16,19 @@ const logger = require('../utils/logger');
15
16
  const { handleCommandError, logOfflinePathWhenType } = require('../utils/cli-utils');
16
17
  const { detectAppType, getDeployJsonPath, getResolveAppPath } = require('../utils/paths');
17
18
 
19
+ const JSON_HELP_AFTER = `
20
+ Example:
21
+ $ aifabrix json myapp
22
+ Generates *-deploy.json (or application-schema.json) for commit before deploy.
23
+ `;
24
+
25
+ const VALIDATE_HELP_AFTER = `
26
+ Examples:
27
+ $ aifabrix validate myapp
28
+ $ aifabrix validate --integration
29
+ $ aifabrix validate --builder
30
+ `;
31
+
18
32
  /**
19
33
  * Resolve app path and type for split-json (integration first, then builder).
20
34
  *
@@ -67,7 +81,7 @@ async function handleSplitJsonCommand(appName, options) {
67
81
  * @returns {void}
68
82
  */
69
83
  function logSplitJsonResult(result) {
70
- logger.log(chalk.green('\n✓ Successfully split deployment JSON into component files:'));
84
+ logger.log(formatSuccessParagraph('Successfully split deployment JSON into component files:'));
71
85
  logger.log(` • env.template: ${result.envTemplate}`);
72
86
  logger.log(` • application.yaml: ${result.variables}`);
73
87
  if (result.systemFile) {
@@ -88,7 +102,7 @@ function logSplitJsonResult(result) {
88
102
 
89
103
  function setupResolveCommand(program) {
90
104
  program.command('resolve <app>')
91
- .description('Generate .env file from template and validate application files')
105
+ .description('Generate .env from template; optional validate after')
92
106
  .option('-f, --force', 'Generate missing secret keys in secrets file')
93
107
  .option('--skip-validation', 'Skip file validation after generating .env')
94
108
  .action(async(appName, options) => {
@@ -101,7 +115,7 @@ function setupResolveCommand(program) {
101
115
  options.force,
102
116
  { appPath, envOnly, skipOutputPath: false, preserveFromPath: null }
103
117
  );
104
- logger.log(`✓ Generated .env file: ${envPath}`);
118
+ logger.log(`✔ Generated .env file: ${envPath}`);
105
119
  if (envOnly) {
106
120
  logger.log(chalk.gray(' (env-only mode: validation skipped; no application.yaml)'));
107
121
  } else if (!options.skipValidation) {
@@ -109,7 +123,7 @@ function setupResolveCommand(program) {
109
123
  const result = await validate.validateAppOrFile(appName);
110
124
  validate.displayValidationResults(result);
111
125
  if (!result.valid) {
112
- logger.log(chalk.yellow('\n⚠️ Validation found errors. Fix them before deploying.'));
126
+ logger.log(chalk.yellow('\n Validation found errors. Fix them before deploying.'));
113
127
  process.exit(1);
114
128
  }
115
129
  }
@@ -122,19 +136,20 @@ function setupResolveCommand(program) {
122
136
 
123
137
  function setupJsonCommand(program) {
124
138
  program.command('json <app>')
125
- .description('Generate deployment JSON to disk (<app>-deploy.json). Use before commit so version control has the correct file.')
139
+ .description('Write deployment JSON to disk for version control')
140
+ .addHelpText('after', JSON_HELP_AFTER)
126
141
  .action(async(appName, options) => {
127
142
  try {
128
143
  const result = await generator.generateDeployJsonWithValidation(appName, options);
129
144
  if (result.success) {
130
145
  const fileName = result.path.includes('application-schema.json') ? 'application-schema.json' : 'deployment JSON';
131
- logger.log(`✓ Generated ${fileName}: ${result.path}`);
146
+ logger.log(`✔ Generated ${fileName}: ${result.path}`);
132
147
  if (result.validation.warnings && result.validation.warnings.length > 0) {
133
- logger.log('\n⚠️ Warnings:');
148
+ logger.log('\n Warnings:');
134
149
  result.validation.warnings.forEach(w => logger.log(` • ${w}`));
135
150
  }
136
151
  } else {
137
- logger.log(' Validation failed:');
152
+ logger.log(' Validation failed:');
138
153
  (result.validation.errors || []).forEach(e => logger.log(` • ${e}`));
139
154
  process.exit(1);
140
155
  }
@@ -147,7 +162,7 @@ function setupJsonCommand(program) {
147
162
 
148
163
  function setupSplitJsonCommand(program) {
149
164
  program.command('split-json <app>')
150
- .description('Split deployment JSON into component files (env.template, application.yaml, rbac.yml, README.md)')
165
+ .description('Split deploy JSON into env.template, application.yaml, rbac, README, …')
151
166
  .option('-o, --output <dir>', 'Output directory for component files (defaults to same directory as JSON)')
152
167
  .action(async(appName, options) => {
153
168
  try {
@@ -160,12 +175,13 @@ function setupSplitJsonCommand(program) {
160
175
  }
161
176
 
162
177
  function setupRepairCommand(program) {
163
- program.command('repair <app>')
164
- .description('Repair external integration config: fix drift (file lists, app key, datasource alignment, rbac, manifest)')
178
+ program.command('repair <systemKey>')
179
+ .description('Fix external integration drift (files, RBAC, manifest, )')
165
180
  .option('--auth <method>', 'Set authentication method (oauth2, aad, apikey, basic, queryParam, oidc, hmac, none); updates system file and env.template')
181
+ .option('--doc', 'Regenerate README.md from deployment manifest')
166
182
  .option('--dry-run', 'Report changes only; do not write')
167
183
  .option('--rbac', 'Ensure RBAC permissions per datasource and add default Admin/Reader roles if none exist')
168
- .option('--expose', 'Set exposed.attributes on each datasource to all fieldMappings.attributes keys')
184
+ .option('--expose', 'Set exposed.schema on each datasource from all fieldMappings.attributes keys (metadata.<key>); removes deprecated exposed.attributes if present')
169
185
  .option('--sync', 'Add default sync section to datasources that lack it')
170
186
  .option('--test', 'Generate testPayload.payloadTemplate and testPayload.expectedResult from attributes')
171
187
  .action(async(appName, options) => {
@@ -174,9 +190,18 @@ function setupRepairCommand(program) {
174
190
  const { detectAppType } = require('../utils/paths');
175
191
  const { appPath } = await detectAppType(appName);
176
192
  logOfflinePathWhenType(appPath);
193
+ let format = 'yaml';
194
+ try {
195
+ const config = require('../core/config');
196
+ format = (await config.getFormat()) || format;
197
+ } catch (_) {
198
+ // use default yaml when config unavailable
199
+ }
177
200
  const result = await repairExternalIntegration(appName, {
178
201
  auth: options.auth,
202
+ doc: options.doc,
179
203
  dryRun: options.dryRun,
204
+ format,
180
205
  rbac: options.rbac,
181
206
  expose: options.expose,
182
207
  sync: options.sync,
@@ -186,7 +211,9 @@ function setupRepairCommand(program) {
186
211
  logger.log(chalk.yellow('\nWould apply:'));
187
212
  result.changes.forEach(c => logger.log(chalk.gray(` ${c}`)));
188
213
  } else if (result.updated) {
189
- logger.log(chalk.green('\n✓ Repaired external integration config.'));
214
+ logger.log(formatSuccessParagraph('Repaired external integration config.'));
215
+ } else if (result.readmeRegenerated) {
216
+ logger.log(formatSuccessParagraph('Regenerated README.md from deployment manifest.'));
190
217
  } else {
191
218
  logger.log(chalk.gray('No changes needed; config already matches files on disk.'));
192
219
  }
@@ -199,7 +226,7 @@ function setupRepairCommand(program) {
199
226
 
200
227
  function setupConvertCommand(program) {
201
228
  program.command('convert <app>')
202
- .description('Convert integration/external system and datasource config files between JSON and YAML')
229
+ .description('Convert integration config files between JSON and YAML')
203
230
  .option('--format <format>', 'Target format: json | yaml (required unless config format is set)')
204
231
  .option('-f, --force', 'Skip confirmation prompt')
205
232
  .action(async(appName, options) => {
@@ -217,7 +244,7 @@ function setupConvertCommand(program) {
217
244
  }
218
245
  const { runConvert } = require('../commands/convert');
219
246
  const { converted, deleted } = await runConvert(appName, { format: normalized, force: options.force });
220
- logger.log(chalk.green('\n✓ Convert complete.'));
247
+ logger.log(formatSuccessParagraph('Convert complete.'));
221
248
  converted.forEach(p => logger.log(` • ${p}`));
222
249
  if (deleted.length > 0) {
223
250
  logger.log(chalk.gray(' Removed old files:'));
@@ -231,8 +258,8 @@ function setupConvertCommand(program) {
231
258
  }
232
259
 
233
260
  function setupShowCommand(program) {
234
- program.command('show <appKey>')
235
- .description('Show application info from local builder/ or integration/ (offline) or from controller (--online)')
261
+ program.command('show <app>')
262
+ .description('Show app from local tree (default) or controller (--online)')
236
263
  .option('--online', 'Fetch application data from the controller')
237
264
  .option('--json', 'Output as JSON')
238
265
  .action(async(appKey, options) => {
@@ -291,7 +318,8 @@ async function runValidateCommand(appOrFile, options) {
291
318
 
292
319
  function setupValidateDiffCommands(program) {
293
320
  program.command('validate [appOrFile]')
294
- .description('Validate application or external integration file; use --integration or --builder to validate all apps')
321
+ .description('Validate one app/file or all under integration/ or builder/')
322
+ .addHelpText('after', VALIDATE_HELP_AFTER)
295
323
  .option('--format <format>', 'Output format: json | default (human-readable)')
296
324
  .option('--integration', 'Validate all applications under integration/')
297
325
  .option('--builder', 'Validate all applications under builder/')
@@ -303,7 +331,7 @@ function setupValidateDiffCommands(program) {
303
331
  });
304
332
 
305
333
  program.command('diff <file1> <file2>')
306
- .description('Compare two configuration files (for deployment pipeline)')
334
+ .description('Diff two config files (optional schema validate)')
307
335
  .option('--no-validate', 'Skip schema validation (type check still applied)')
308
336
  .action(async(file1, file2, cmd) => {
309
337
  try {
@@ -1,3 +1,4 @@
1
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * Down-app command – stop container, optionally volumes, then remove image if unused
3
4
  *
@@ -7,14 +8,11 @@
7
8
  */
8
9
 
9
10
  const chalk = require('chalk');
10
- const { exec } = require('child_process');
11
- const { promisify } = require('util');
12
11
  const logger = require('../utils/logger');
13
12
  const config = require('../core/config');
14
13
  const containerHelpers = require('../utils/app-run-containers');
15
14
  const { downApp } = require('../app/down');
16
-
17
- const execAsync = promisify(exec);
15
+ const { execWithDockerEnv } = require('../utils/docker-exec');
18
16
 
19
17
  /**
20
18
  * Get image ID of a running container (sha or name:tag)
@@ -24,7 +22,7 @@ const execAsync = promisify(exec);
24
22
  */
25
23
  async function getContainerImageId(containerName) {
26
24
  try {
27
- const { stdout } = await execAsync(
25
+ const { stdout } = await execWithDockerEnv(
28
26
  `docker inspect --format='{{.Image}}' ${containerName}`,
29
27
  { encoding: 'utf8' }
30
28
  );
@@ -43,8 +41,8 @@ async function getContainerImageId(containerName) {
43
41
  async function removeImageIfUnused(imageId) {
44
42
  if (!imageId) return;
45
43
  try {
46
- await execAsync(`docker rmi ${imageId}`);
47
- logger.log(chalk.green(`✓ Image ${imageId} removed`));
44
+ await execWithDockerEnv(`docker rmi ${imageId}`);
45
+ logger.log(formatSuccessLine(`Image ${imageId} removed`));
48
46
  } catch (err) {
49
47
  const msg = (err && err.message) || '';
50
48
  if (msg.includes('in use') || msg.includes('is being used')) {
@@ -1,3 +1,4 @@
1
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * Install command – run install (dependencies) inside app container (dev: running; tst: ephemeral with .env).
3
4
  *
@@ -81,7 +82,7 @@ async function runInstallInDev(appName, developerId, installCmd) {
81
82
  logger.log(chalk.blue(`Running install in container ${containerName}: ${installCmd}\n`));
82
83
  const cmd = installCommandForReadOnlyApp(installCmd);
83
84
  const envFilePath = await secretsEnvWrite.resolveAndWriteEnvFile(appName, {});
84
- return runDockerExec(containerName, cmd, envFilePath);
85
+ return await runDockerExec(containerName, cmd, envFilePath);
85
86
  }
86
87
 
87
88
  /**
@@ -92,7 +93,9 @@ async function runInstallInDev(appName, developerId, installCmd) {
92
93
  * @param {string|null} [envFilePath] - Path to resolved .env for --env-file (optional; when set, install has registry tokens)
93
94
  * @returns {Promise<number>} Exit code
94
95
  */
95
- function runDockerExec(containerName, cmd, envFilePath = null) {
96
+ async function runDockerExec(containerName, cmd, envFilePath = null) {
97
+ const { getDockerExecEnv } = require('../utils/remote-docker-env');
98
+ const dockerEnv = await getDockerExecEnv();
96
99
  return new Promise((resolve) => {
97
100
  const args = [
98
101
  'exec',
@@ -106,7 +109,8 @@ function runDockerExec(containerName, cmd, envFilePath = null) {
106
109
  args.push(containerName, 'sh', '-c', cmd);
107
110
  const proc = spawn('docker', args, {
108
111
  stdio: 'inherit',
109
- shell: false
112
+ shell: false,
113
+ env: dockerEnv
110
114
  });
111
115
  proc.on('close', code => resolve(code !== null ? code : 1));
112
116
  proc.on('error', () => resolve(1));
@@ -120,7 +124,9 @@ function runDockerExec(containerName, cmd, envFilePath = null) {
120
124
  * @param {string|null} [envFilePath] - Path to .env file for --env-file (optional)
121
125
  * @returns {Promise<number>} Exit code
122
126
  */
123
- function runDockerRunEphemeral(fullImage, cmd, envFilePath = null) {
127
+ async function runDockerRunEphemeral(fullImage, cmd, envFilePath = null) {
128
+ const { getDockerExecEnv } = require('../utils/remote-docker-env');
129
+ const dockerEnv = await getDockerExecEnv();
124
130
  return new Promise((resolve) => {
125
131
  const args = ['run', '--rm', '-e', `TMPDIR=${TMPDIR_VALUE}`, '-e', `npm_config_store_dir=${PNPM_STORE_DIR}`, '-e', 'CI=true'];
126
132
  if (envFilePath) {
@@ -129,7 +135,8 @@ function runDockerRunEphemeral(fullImage, cmd, envFilePath = null) {
129
135
  args.push(fullImage, 'sh', '-c', cmd);
130
136
  const proc = spawn('docker', args, {
131
137
  stdio: 'inherit',
132
- shell: false
138
+ shell: false,
139
+ env: dockerEnv
133
140
  });
134
141
  proc.on('close', code => resolve(code !== null ? code : 1));
135
142
  proc.on('error', () => resolve(1));
@@ -157,7 +164,7 @@ async function runAppInstall(appName, options = {}) {
157
164
  if (env === 'dev') {
158
165
  const code = await runInstallInDev(appName, developerId, installCmd);
159
166
  if (code !== 0) process.exit(code);
160
- logger.log(chalk.green('Install completed'));
167
+ logger.log(formatSuccessLine('Install completed'));
161
168
  return;
162
169
  }
163
170
 
@@ -166,7 +173,7 @@ async function runAppInstall(appName, options = {}) {
166
173
  const cmd = installCommandForReadOnlyApp(installCmd);
167
174
  const code = await runDockerRunEphemeral(fullImage, cmd, envFilePath);
168
175
  if (code !== 0) process.exit(code);
169
- logger.log(chalk.green('Install completed'));
176
+ logger.log(formatSuccessLine('Install completed'));
170
177
  }
171
178
 
172
179
  module.exports = { runAppInstall, getInstallCommand };
@@ -7,15 +7,14 @@
7
7
  */
8
8
 
9
9
  const chalk = require('chalk');
10
- const { exec, spawn } = require('child_process');
10
+ const { spawn } = require('child_process');
11
11
  const readline = require('readline');
12
- const { promisify } = require('util');
13
12
  const logger = require('../utils/logger');
14
13
  const config = require('../core/config');
15
14
  const containerHelpers = require('../utils/app-run-containers');
16
15
  const { validateAppName } = require('../app/push');
17
16
 
18
- const execAsync = promisify(exec);
17
+ const { execWithDockerEnv } = require('../utils/docker-exec');
19
18
 
20
19
  /** Default number of log lines */
21
20
  const DEFAULT_TAIL_LINES = 100;
@@ -132,7 +131,7 @@ function passesLevelFilter(lineLevel, minLevel) {
132
131
  */
133
132
  async function dumpMaskedEnv(containerName) {
134
133
  try {
135
- const { stdout } = await execAsync(`docker exec ${containerName} env`, { encoding: 'utf8', timeout: 5000 });
134
+ const { stdout } = await execWithDockerEnv(`docker exec ${containerName} env`, { encoding: 'utf8', timeout: 5000 });
136
135
  const lines = stdout.split('\n').filter((l) => l.trim());
137
136
  if (lines.length === 0) return;
138
137
  logger.log(chalk.bold('\n--- Environment (sensitive values masked) ---\n'));
@@ -156,6 +155,8 @@ async function dumpMaskedEnv(containerName) {
156
155
  * @returns {Promise<void>}
157
156
  */
158
157
  async function runDockerLogs(containerName, options) {
158
+ const { getDockerExecEnv } = require('../utils/remote-docker-env');
159
+ const dockerEnv = await getDockerExecEnv();
159
160
  const args = options.tail === 0 ? ['logs', containerName] : ['logs', '--tail', String(options.tail), containerName];
160
161
  const minLevel =
161
162
  options.level !== undefined && options.level !== null && options.level !== ''
@@ -164,14 +165,14 @@ async function runDockerLogs(containerName, options) {
164
165
 
165
166
  if (minLevel === null || minLevel === undefined || !LOG_LEVELS.includes(minLevel)) {
166
167
  return new Promise((resolve, reject) => {
167
- const proc = spawn('docker', args, { stdio: 'inherit' });
168
+ const proc = spawn('docker', args, { stdio: 'inherit', env: dockerEnv });
168
169
  proc.on('error', reject);
169
170
  proc.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`docker logs exited with ${code}`))));
170
171
  });
171
172
  }
172
173
 
173
174
  return new Promise((resolve, reject) => {
174
- const proc = spawn('docker', args, { stdio: ['inherit', 'pipe', 'pipe'] });
175
+ const proc = spawn('docker', args, { stdio: ['inherit', 'pipe', 'pipe'], env: dockerEnv });
175
176
  proc.on('error', reject);
176
177
 
177
178
  function onLine(line) {
@@ -213,7 +214,9 @@ async function runDockerLogs(containerName, options) {
213
214
  * @param {number} [tail] - Lines to show (0 = full, omit --tail)
214
215
  * @param {string|null} [minLevel] - Minimum log level to show (debug|info|warn|error)
215
216
  */
216
- function runDockerLogsFollow(containerName, tail, minLevel) {
217
+ async function runDockerLogsFollow(containerName, tail, minLevel) {
218
+ const { getDockerExecEnv } = require('../utils/remote-docker-env');
219
+ const dockerEnv = await getDockerExecEnv();
217
220
  const args = tail === 0 ? ['logs', '-f', containerName] : ['logs', '-f', '--tail', String(tail), containerName];
218
221
  const level =
219
222
  minLevel !== undefined && minLevel !== null && minLevel !== ''
@@ -222,7 +225,7 @@ function runDockerLogsFollow(containerName, tail, minLevel) {
222
225
  const useFilter = level !== null && level !== undefined && LOG_LEVELS.includes(level);
223
226
 
224
227
  if (!useFilter) {
225
- const proc = spawn('docker', args, { stdio: 'inherit' });
228
+ const proc = spawn('docker', args, { stdio: 'inherit', env: dockerEnv });
226
229
  proc.on('error', (err) => {
227
230
  logger.log(chalk.red(`Error: ${err.message}`));
228
231
  process.exit(1);
@@ -233,7 +236,7 @@ function runDockerLogsFollow(containerName, tail, minLevel) {
233
236
  return;
234
237
  }
235
238
 
236
- const proc = spawn('docker', args, { stdio: ['inherit', 'pipe', 'pipe'] });
239
+ const proc = spawn('docker', args, { stdio: ['inherit', 'pipe', 'pipe'], env: dockerEnv });
237
240
  proc.on('error', (err) => {
238
241
  logger.log(chalk.red(`Error: ${err.message}`));
239
242
  process.exit(1);
@@ -286,7 +289,7 @@ async function runAppLogs(appKey, options = {}) {
286
289
  }
287
290
 
288
291
  if (follow) {
289
- runDockerLogsFollow(containerName, tail, level);
292
+ await runDockerLogsFollow(containerName, tail, level);
290
293
  return;
291
294
  }
292
295
 
@@ -48,6 +48,8 @@ async function runAppShell(appName, _options = {}) {
48
48
 
49
49
  logger.log(chalk.blue(`Opening shell in ${containerName} (exit with 'exit' or Ctrl+D)...\n`));
50
50
 
51
+ const { getDockerExecEnv } = require('../utils/remote-docker-env');
52
+ const dockerEnv = await getDockerExecEnv();
51
53
  const envFilePath = await secretsEnvWrite.resolveAndWriteEnvFile(appName, {});
52
54
  const proc = spawn('docker', [
53
55
  'exec', '-it',
@@ -57,7 +59,8 @@ async function runAppShell(appName, _options = {}) {
57
59
  'sh'
58
60
  ], {
59
61
  stdio: 'inherit',
60
- shell: false
62
+ shell: false,
63
+ env: dockerEnv
61
64
  });
62
65
 
63
66
  return new Promise((resolve, reject) => {