@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
@@ -0,0 +1,307 @@
1
+ /**
2
+ * @fileoverview Optional hosts-file helper for dev init: map Builder Server hostname to an IP on this machine.
3
+ * Wildcard DNS (*.host) is documented for router/DNS; hosts file only supports exact hostnames (OS limitation).
4
+ * Writing hosts usually requires administrator rights on Windows and macOS.
5
+ *
6
+ * @author AI Fabrix Team
7
+ * @version 2.0.0
8
+ */
9
+
10
+ const dns = require('dns').promises;
11
+ const path = require('path');
12
+ const readline = require('readline');
13
+ const chalk = require('chalk');
14
+ const { nodeFs } = require('../internal/node-fs');
15
+
16
+ const IPV4_RE = /^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)){3}$/;
17
+
18
+ /**
19
+ * @param {string} url - https://builder02.local or similar
20
+ * @returns {string} Hostname only
21
+ */
22
+ function hostnameFromServerUrl(url) {
23
+ try {
24
+ return new URL(url.trim()).hostname;
25
+ } catch {
26
+ throw new Error('Invalid --server URL for hosts setup');
27
+ }
28
+ }
29
+
30
+ /**
31
+ * @param {string} s
32
+ * @returns {boolean}
33
+ */
34
+ function isValidIpv4(s) {
35
+ return typeof s === 'string' && IPV4_RE.test(s.trim());
36
+ }
37
+
38
+ /**
39
+ * Path to system hosts file.
40
+ * @returns {string}
41
+ */
42
+ function getHostsFilePath() {
43
+ if (process.platform === 'win32') {
44
+ const windir = process.env.WINDIR || process.env.SystemRoot || 'C:\\Windows';
45
+ return path.join(windir, 'System32', 'drivers', 'etc', 'hosts');
46
+ }
47
+ return '/etc/hosts';
48
+ }
49
+
50
+ /**
51
+ * Hostnames to map to the Builder Server IP: the URL host plus devNN.url-host for
52
+ * per-developer subdomains. Wildcards (*.zone) are not supported by OS hosts files.
53
+ * @param {string|undefined} developerId - e.g. "02"
54
+ * @param {string} primaryHostname - Hostname from --server (e.g. builder02.local)
55
+ * @returns {string[]} Deduped list (primary first, then per-dev if applicable)
56
+ */
57
+ function hostsNamesForDevInit(developerId, primaryHostname) {
58
+ const primary = String(primaryHostname || '').trim();
59
+ if (!primary) return [];
60
+ const names = [primary];
61
+ const id = developerId !== undefined && developerId !== null ? String(developerId).trim() : '';
62
+ if (!id || isValidIpv4(primary)) return names;
63
+ if (/^dev\d+\./i.test(primary)) return names;
64
+ const perDev = `dev${id}.${primary}`;
65
+ if (perDev !== primary && !names.includes(perDev)) names.push(perDev);
66
+ return names;
67
+ }
68
+
69
+ /**
70
+ * Display URL for the per-developer host (devNN.zone), same scheme and port as --server.
71
+ * @param {string|undefined} developerId
72
+ * @param {string} baseUrl - Builder Server URL
73
+ * @returns {string|null} e.g. https://dev02.builder02.local
74
+ */
75
+ function perDeveloperServerDisplayUrl(developerId, baseUrl) {
76
+ let u;
77
+ try {
78
+ u = new URL(String(baseUrl || '').trim());
79
+ } catch {
80
+ return null;
81
+ }
82
+ const list = hostsNamesForDevInit(developerId, u.hostname);
83
+ if (list.length < 2) return null;
84
+ const perDevHost = list[1];
85
+ u.hostname = perDevHost;
86
+ const portPart = u.port ? `:${u.port}` : '';
87
+ return `${u.protocol}//${u.hostname}${portPart}`;
88
+ }
89
+
90
+ /**
91
+ * @param {{ log: function }} logger
92
+ * @param {string|undefined} developerId
93
+ * @param {string} baseUrl
94
+ * @returns {void}
95
+ */
96
+ function logPerDeveloperUrlHint(logger, developerId, baseUrl) {
97
+ const displayUrl = perDeveloperServerDisplayUrl(developerId, baseUrl);
98
+ if (!displayUrl) return;
99
+ logger.log(chalk.green(' Your per-developer URL: ') + chalk.cyan(displayUrl));
100
+ logger.log('');
101
+ }
102
+
103
+ /**
104
+ * True if hosts file already maps hostname (any IP).
105
+ * @param {string} hostsPath
106
+ * @param {string} hostname
107
+ * @returns {boolean}
108
+ */
109
+ function hostsFileHasHostname(hostsPath, hostname) {
110
+ const want = String(hostname || '').trim();
111
+ if (!want) return false;
112
+ let text;
113
+ try {
114
+ text = nodeFs().readFileSync(hostsPath, 'utf8');
115
+ } catch (e) {
116
+ if (e.code === 'ENOENT') return false;
117
+ throw e;
118
+ }
119
+ if (typeof text !== 'string') return false;
120
+ for (const line of text.split(/\r?\n/)) {
121
+ const t = line.replace(/#.*/, '').trim();
122
+ if (!t) continue;
123
+ const parts = t.split(/\s+/).filter(Boolean);
124
+ if (parts.length < 2) continue;
125
+ const names = parts.slice(1).map((n) => String(n).trim());
126
+ if (names.includes(want)) return true;
127
+ }
128
+ return false;
129
+ }
130
+
131
+ /**
132
+ * @param {string} question
133
+ * @returns {Promise<string>}
134
+ */
135
+ function promptLine(question) {
136
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
137
+ return new Promise(resolve => {
138
+ rl.question(question, answer => {
139
+ rl.close();
140
+ resolve((answer || '').trim());
141
+ });
142
+ });
143
+ }
144
+
145
+ /**
146
+ * @param {string} question
147
+ * @returns {Promise<boolean>}
148
+ */
149
+ async function promptYesNo(question) {
150
+ const a = (await promptLine(question)).toLowerCase();
151
+ return a === 'y' || a === 'yes';
152
+ }
153
+
154
+ /**
155
+ * Try IPv4 lookup for hostname.
156
+ * @param {string} hostname
157
+ * @returns {Promise<string|null>}
158
+ */
159
+ async function lookupIpv4(hostname) {
160
+ try {
161
+ const r = await dns.lookup(hostname, { family: 4 });
162
+ return r.address || null;
163
+ } catch {
164
+ return null;
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Print how to add the line with admin/sudo when direct write failed.
170
+ * @param {{ log: function }} logger
171
+ * @param {string} hostsPath
172
+ * @param {string} line
173
+ */
174
+ function printManualHostsInstructions(logger, hostsPath, line) {
175
+ if (process.platform === 'win32') {
176
+ logger.log(chalk.yellow('\n Could not write hosts file (need Administrator). Run PowerShell as Administrator, then:\n'));
177
+ logger.log(chalk.cyan(` Add-Content -Path "${hostsPath}" -Value "\`r\`n${line}\`r\`n"`));
178
+ } else {
179
+ logger.log(chalk.yellow('\n Could not write hosts file (need elevated permissions). Run:\n'));
180
+ logger.log(chalk.cyan(` echo '${line}' | sudo tee -a ${hostsPath}`));
181
+ }
182
+ }
183
+
184
+ /**
185
+ * @param {{ log: function }} logger
186
+ * @param {string[]} hostnames - Names we may add (URL host + optional devNN.zone)
187
+ */
188
+ function logHostsIntro(logger, hostnames) {
189
+ const list = (hostnames && hostnames.length ? hostnames : []).join(', ');
190
+ logger.log(chalk.blue('\n📍 Local name resolution (optional)\n'));
191
+ logger.log(
192
+ chalk.gray(
193
+ ' Wildcards such as *.local are not supported in the hosts file (OS limitation). '
194
+ + 'Use DNS or router DNS for wildcard zones; here we add exact names only.'
195
+ )
196
+ );
197
+ logger.log(chalk.gray(` Names to map to that IP (same line where possible): ${list}`));
198
+ logger.log('');
199
+ }
200
+
201
+ /**
202
+ * @param {string} hostname
203
+ * @param {string|undefined} hostsIp - From --hosts-ip
204
+ * @param {{ log: function }} logger
205
+ * @returns {Promise<string|null>} IPv4 or null if user skipped
206
+ */
207
+ async function resolveHostsIpForInit(hostname, hostsIp, logger) {
208
+ let ip = typeof hostsIp === 'string' && hostsIp.trim() ? hostsIp.trim() : null;
209
+ if (ip && !isValidIpv4(ip)) {
210
+ throw new Error(`Invalid --hosts-ip: "${ip}" (use an IPv4 address, e.g. 192.168.1.25)`);
211
+ }
212
+ if (!ip) {
213
+ const lookedUp = await lookupIpv4(hostname);
214
+ if (lookedUp && isValidIpv4(lookedUp)) {
215
+ logger.log(chalk.gray(` ${hostname} currently resolves to ${lookedUp} (DNS or existing hosts).`));
216
+ ip = lookedUp;
217
+ }
218
+ }
219
+ if (ip) return ip;
220
+ const entered = await promptLine(chalk.yellow(` Enter IPv4 address for ${hostname} (e.g. 192.168.1.25): `));
221
+ if (!entered) {
222
+ logger.log(chalk.gray(' Skipping hosts file (no IP entered).\n'));
223
+ return null;
224
+ }
225
+ if (!isValidIpv4(entered)) {
226
+ throw new Error(`Invalid IP address: "${entered}"`);
227
+ }
228
+ return entered;
229
+ }
230
+
231
+ /**
232
+ * @param {string} hostsPath
233
+ * @param {string} block - Full text to append
234
+ * @param {string} line - Single-line form for manual instructions
235
+ * @param {{ log: function }} logger
236
+ * @returns {Promise<void>}
237
+ */
238
+ async function appendHostsBlockOrPrintManual(hostsPath, block, line, logger) {
239
+ try {
240
+ await nodeFs().promises.appendFile(hostsPath, block, { encoding: 'utf8' });
241
+ logger.log(chalk.green(` ✔ Updated ${hostsPath}\n`));
242
+ } catch (e) {
243
+ if (e.code === 'EACCES' || e.code === 'EPERM') {
244
+ logger.log(chalk.yellow(` ✖ Could not write ${hostsPath} (permission denied).`));
245
+ printManualHostsInstructions(logger, hostsPath, line);
246
+ logger.log('');
247
+ return;
248
+ }
249
+ throw e;
250
+ }
251
+ }
252
+
253
+ /**
254
+ * @param {string} hostsPath
255
+ * @param {string[]} hostnames
256
+ * @param {string} ip
257
+ * @param {boolean} skipConfirm
258
+ * @param {{ log: function }} logger
259
+ * @returns {Promise<void>}
260
+ */
261
+ async function tryWriteHostsEntry(hostsPath, hostnames, ip, skipConfirm, logger) {
262
+ const missing = hostnames.filter((h) => !hostsFileHasHostname(hostsPath, h));
263
+ if (missing.length === 0) {
264
+ logger.log(chalk.green(` ✔ Required hostnames are already listed in ${hostsPath}. Nothing to do.\n`));
265
+ return;
266
+ }
267
+ const line = `${ip} ${missing.join(' ')}`;
268
+ logger.log(chalk.gray(` Suggested hosts line: ${line}`));
269
+ const yn = chalk.yellow(` Add this line to ${hostsPath}? This may require administrator approval. (y/n) `);
270
+ const confirm = skipConfirm || (await promptYesNo(yn));
271
+ if (!confirm) {
272
+ logger.log(chalk.gray(' Skipping hosts file update.\n'));
273
+ return;
274
+ }
275
+ const block = `\n# aifabrix dev init — ${missing.join(', ')}\n${line}\n`;
276
+ await appendHostsBlockOrPrintManual(hostsPath, block, line, logger);
277
+ }
278
+
279
+ /**
280
+ * Optional hosts setup for dev init (--add-hosts).
281
+ * @param {Object} params
282
+ * @param {string} params.baseUrl - Builder Server URL
283
+ * @param {string} [params.developerId] - From --developer-id (adds devNN.hostname)
284
+ * @param {string} [params.hostsIp] - From --hosts-ip
285
+ * @param {boolean} params.skipConfirm - When true (-y), append without y/n
286
+ * @param {{ log: function }} params.logger - logger like lib/utils/logger
287
+ * @returns {Promise<void>}
288
+ */
289
+ async function runOptionalHostsSetup({ baseUrl, developerId, hostsIp, skipConfirm, logger }) {
290
+ const primary = hostnameFromServerUrl(baseUrl);
291
+ const hostnames = hostsNamesForDevInit(developerId, primary);
292
+ logHostsIntro(logger, hostnames);
293
+ const ip = await resolveHostsIpForInit(primary, hostsIp, logger);
294
+ if (ip === null) return;
295
+ await tryWriteHostsEntry(getHostsFilePath(), hostnames, ip, skipConfirm, logger);
296
+ logPerDeveloperUrlHint(logger, developerId, baseUrl);
297
+ }
298
+
299
+ module.exports = {
300
+ hostnameFromServerUrl,
301
+ hostsNamesForDevInit,
302
+ perDeveloperServerDisplayUrl,
303
+ isValidIpv4,
304
+ getHostsFilePath,
305
+ hostsFileHasHostname,
306
+ runOptionalHostsSetup
307
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @fileoverview Cert troubleshooting hints for dev init (keeps dev-init.js under max-lines).
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const path = require('path');
8
+ const chalk = require('chalk');
9
+ const { getCertDir } = require('./dev-cert-helper');
10
+ const logger = require('./logger');
11
+
12
+ /**
13
+ * Message for 400 Bad Request: nginx often forwards X-Client-Cert with literal newlines.
14
+ * @returns {string} Hint for server-side nginx fix
15
+ */
16
+ function getBadRequestHint() {
17
+ return 'Bad Request (400) often means the server\'s nginx is forwarding the client certificate with literal newlines in X-Client-Cert. On the server, use nginx njs to escape newlines (see .cursor/plans/builder-cli.md §5).';
18
+ }
19
+
20
+ /**
21
+ * Log a one-line hint for cert troubleshooting (curl test and docs).
22
+ * @param {string} configDir - Config directory
23
+ * @param {string} devId - Developer ID
24
+ * @param {string} baseUrl - Builder Server base URL
25
+ */
26
+ function logCertTroubleshootingHint(configDir, devId, baseUrl) {
27
+ const certDir = getCertDir(configDir, devId);
28
+ const certPath = path.join(certDir, 'cert.pem');
29
+ const keyPath = path.join(certDir, 'key.pem');
30
+ logger.log(chalk.gray(` Test with: curl -v --cert ${certPath} --key ${keyPath} ${baseUrl}/api/dev/settings`));
31
+ logger.log(chalk.gray(' See .cursor/plans/builder-cli.md §5 for 200 vs 401 vs 400 and nginx/server fix.'));
32
+ }
33
+
34
+ module.exports = {
35
+ getBadRequestHint,
36
+ logCertTroubleshootingHint
37
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @fileoverview Health / TLS failure messages for dev init (keeps dev-init.js under max-lines).
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const { isLinuxCaSudoRequiredError } = require('./dev-ca-install');
8
+
9
+ /**
10
+ * True when /health returned a 5xx (server or gateway error). TLS succeeded; app may still be degraded.
11
+ * @param {Error} err - Thrown from getHealth
12
+ * @returns {boolean}
13
+ */
14
+ function isHealthHttpServerError(err) {
15
+ const s = err && typeof err.status === 'number' ? err.status : null;
16
+ return s !== null && s >= 500 && s < 600;
17
+ }
18
+
19
+ /**
20
+ * Message when trust/health step fails (avoid calling HTTP 5xx "cannot reach").
21
+ * @param {string} baseUrl - Builder Server URL
22
+ * @param {Error} err - Failure from getHealth or TLS
23
+ * @returns {string}
24
+ */
25
+ function formatEnsureServerTrustedFailure(baseUrl, err) {
26
+ const s = err && typeof err.status === 'number' ? err.status : null;
27
+ if (s !== null) {
28
+ const detail = err.message || `HTTP ${s}`;
29
+ if (s >= 500 && s < 600) {
30
+ return (
31
+ `Builder Server at ${baseUrl} returned HTTP ${s} (${detail}) on GET /health. ` +
32
+ 'TLS succeeded; the service reported an error or is not fully healthy. Check server logs or open /health in a browser.'
33
+ );
34
+ }
35
+ return (
36
+ `Builder Server at ${baseUrl} returned HTTP ${s} (${detail}) on GET /health. ` +
37
+ 'The host was reached but health did not succeed. Check the URL and server configuration.'
38
+ );
39
+ }
40
+ if (isLinuxCaSudoRequiredError(err)) {
41
+ return (
42
+ `Could not add the development CA to the system trust store for ${baseUrl} (Linux needs sudo for that step). ` +
43
+ `${err.message}`
44
+ );
45
+ }
46
+ return `Cannot reach Builder Server at ${baseUrl}. Check URL and network. ${err.message}`;
47
+ }
48
+
49
+ module.exports = {
50
+ isHealthHttpServerError,
51
+ formatEnsureServerTrustedFailure
52
+ };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Resolve Builder URL, developer id, and PIN for dev init.
3
+ * CLI flags override config. Developer id from config is not used when it is "0" (local default).
4
+ *
5
+ * @fileoverview Shared onboarding option resolution for dev-init command
6
+ * @author AI Fabrix Team
7
+ * @version 2.0.0
8
+ */
9
+
10
+ const config = require('../core/config');
11
+
12
+ function requirePin(options) {
13
+ const pin = options.pin;
14
+ if (!pin || typeof pin !== 'string' || !pin.trim()) {
15
+ throw new Error(
16
+ '--pin is required (one-time PIN). On an enrolled admin PC run: aifabrix dev pin <developer-id>, ' +
17
+ 'then use the PIN on this machine.'
18
+ );
19
+ }
20
+ }
21
+
22
+ /**
23
+ * @param {Object} options - Commander options
24
+ * @returns {Promise<{ devId: string, devIdFromConfig: boolean }>}
25
+ */
26
+ async function resolveDeveloperIdPart(options) {
27
+ const rawOptDev = options.developerId ?? options['developer-id'];
28
+ let devId;
29
+ let devIdFromConfig = false;
30
+ if (rawOptDev !== undefined && rawOptDev !== null && String(rawOptDev).trim() !== '') {
31
+ devId = String(rawOptDev).trim();
32
+ } else {
33
+ const fromCfg = await config.getDeveloperId();
34
+ devId = fromCfg !== undefined && fromCfg !== null ? String(fromCfg).trim() : '';
35
+ devIdFromConfig = true;
36
+ }
37
+
38
+ if (!devId || !/^[0-9]+$/.test(devId)) {
39
+ throw new Error(
40
+ 'Pass --developer-id <id> (digits only) or set developer-id in config (aifabrix dev set-id <id>).'
41
+ );
42
+ }
43
+
44
+ if (devIdFromConfig && devId === '0') {
45
+ throw new Error(
46
+ 'Cannot use default developer-id 0 from config for Builder onboarding. Pass --developer-id <id> (e.g. 02) or run aifabrix dev set-id first.'
47
+ );
48
+ }
49
+
50
+ return { devId, devIdFromConfig };
51
+ }
52
+
53
+ /**
54
+ * @param {Object} options - Commander options
55
+ * @returns {Promise<string>} Normalized base URL (no trailing slash)
56
+ */
57
+ async function resolveServerBaseUrl(options) {
58
+ let server = options.server;
59
+ if (!server || typeof server !== 'string' || !String(server).trim()) {
60
+ const fromCfg = await config.getRemoteServer();
61
+ server = fromCfg && typeof fromCfg === 'string' ? fromCfg.trim() : '';
62
+ } else {
63
+ server = String(server).trim();
64
+ }
65
+
66
+ if (!server) {
67
+ throw new Error(
68
+ 'Pass --server <Builder URL> or set remote-server in ~/.aifabrix/config.yaml (from settings merge or aifabrix dev show).'
69
+ );
70
+ }
71
+
72
+ return server.replace(/\/+$/, '');
73
+ }
74
+
75
+ /**
76
+ * @param {Object} options - Commander options
77
+ * @returns {Promise<{ baseUrl: string, devId: string }>}
78
+ */
79
+ async function resolveInitOptions(options) {
80
+ requirePin(options);
81
+ const { devId } = await resolveDeveloperIdPart(options);
82
+ const baseUrl = await resolveServerBaseUrl(options);
83
+ return { baseUrl, devId };
84
+ }
85
+
86
+ module.exports = { resolveInitOptions };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @fileoverview After dev init: merge SSH config Host alias from sync settings / server URL.
3
+ */
4
+
5
+ const chalk = require('chalk');
6
+ const config = require('../core/config');
7
+ const logger = require('./logger');
8
+ const { ensureDevSshConfigBlock } = require('./dev-ssh-config-helper');
9
+
10
+ /**
11
+ * Hostname from Builder Server URL (for sync-ssh-host fallback).
12
+ * @param {string} baseUrl - e.g. https://builder02.local
13
+ * @returns {string|null}
14
+ */
15
+ function hostnameFromBaseUrl(baseUrl) {
16
+ if (!baseUrl || typeof baseUrl !== 'string') return null;
17
+ const s = baseUrl.trim().replace(/\/+$/, '');
18
+ const withProtocol = /^https?:\/\//i.test(s) ? s : `https://${s}`;
19
+ try {
20
+ return new URL(withProtocol).hostname || null;
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Update ~/.ssh/config with Host <user>.<host> for interactive SSH; log result.
28
+ * @param {string} baseUrl - Builder Server base URL
29
+ * @param {string} devId - Developer ID (digits)
30
+ * @returns {Promise<{ hostAlias: string|null, syncUser: string, syncHost: string|null }>}
31
+ */
32
+ async function mergeDevSshConfigAfterInit(baseUrl, devId) {
33
+ const syncHost = (await config.getSyncSshHost()) || hostnameFromBaseUrl(baseUrl);
34
+ const syncUser = (await config.getSyncSshUser()) || `dev${devId}`;
35
+ if (!syncHost) {
36
+ return { hostAlias: null, syncUser, syncHost: null };
37
+ }
38
+ try {
39
+ const res = await ensureDevSshConfigBlock(syncUser, syncHost);
40
+ if (res.ok && res.configPath && res.hostAlias) {
41
+ if (res.skippedDuplicate) {
42
+ logger.log(
43
+ chalk.gray(' SSH config already has ') +
44
+ chalk.cyan(`${syncUser}@${syncHost}`) +
45
+ chalk.gray(` (Host ${res.hostAlias}); left unchanged.`)
46
+ );
47
+ } else {
48
+ logger.log(
49
+ chalk.green(' ✔ SSH config updated: ') +
50
+ chalk.cyan(`Host ${res.hostAlias}`) +
51
+ chalk.gray(` → ${res.configPath}`)
52
+ );
53
+ }
54
+ return { hostAlias: res.hostAlias, syncUser, syncHost };
55
+ }
56
+ if (!res.ok && res.error) {
57
+ logger.log(chalk.yellow(` ⚠ Could not update SSH config: ${res.error}`));
58
+ }
59
+ } catch (e) {
60
+ logger.log(chalk.yellow(` ⚠ Could not update SSH config: ${e.message || e}`));
61
+ }
62
+ return { hostAlias: null, syncUser, syncHost };
63
+ }
64
+
65
+ module.exports = { mergeDevSshConfigAfterInit, hostnameFromBaseUrl };