@aifabrix/builder 2.43.0 → 2.44.1

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 (371) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/.cursor/rules/cli-layout.mdc +75 -0
  3. package/.cursor/rules/project-rules.mdc +8 -0
  4. package/.npmrc.token +1 -0
  5. package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
  6. package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
  7. package/.nyc_output/processinfo/index.json +1 -0
  8. package/README.md +1 -1
  9. package/anchor-docs/README.md +10 -0
  10. package/anchor-docs/_TEMPLATE +24 -0
  11. package/bin/aifabrix.js +13 -4
  12. package/integration/hubspot-test/README.md +31 -0
  13. package/integration/hubspot-test/create-hubspot.js +5 -5
  14. package/integration/hubspot-test/hubspot-test-datasource-company.json +58 -462
  15. package/integration/hubspot-test/hubspot-test-datasource-contact.json +61 -555
  16. package/integration/hubspot-test/hubspot-test-datasource-deal.json +63 -506
  17. package/integration/hubspot-test/hubspot-test-datasource-users.json +42 -83
  18. package/integration/hubspot-test/hubspot-test-deploy.json +3 -3
  19. package/integration/hubspot-test/test-dataplane-down-tests.js +1 -7
  20. package/integration/hubspot-test/test-dataplane-down.js +3 -3
  21. package/integration/hubspot-test/test.js +35 -43
  22. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  23. package/integration/roundtrip-test-local/README.md +144 -0
  24. package/integration/roundtrip-test-local/application.yaml +13 -0
  25. package/integration/roundtrip-test-local/env.template +15 -0
  26. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  27. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  28. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  29. package/integration/roundtrip-test-local2/README.md +144 -0
  30. package/integration/roundtrip-test-local2/application.yaml +13 -0
  31. package/integration/roundtrip-test-local2/env.template +15 -0
  32. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  33. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  34. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  35. package/integration/test/wizard.yaml +8 -0
  36. package/jest.config.default.js +10 -0
  37. package/jest.config.integration.fixtures.js +22 -0
  38. package/jest.config.integration.js +21 -18
  39. package/jest.config.isolated.js +10 -0
  40. package/jest.projects.js +301 -0
  41. package/lib/api/certificates.api.js +62 -0
  42. package/lib/api/datasources-core.api.js +3 -3
  43. package/lib/api/dev-mtls-request.js +110 -0
  44. package/lib/api/dev-server-https.js +145 -0
  45. package/lib/api/dev.api.js +133 -144
  46. package/lib/api/index.js +11 -3
  47. package/lib/api/pipeline.api.js +67 -20
  48. package/lib/api/types/certificates.types.js +48 -0
  49. package/lib/api/types/dev.types.js +4 -3
  50. package/lib/api/types/pipeline.types.js +8 -5
  51. package/lib/api/types/validation-run.types.js +56 -0
  52. package/lib/api/validation-run.api.js +111 -0
  53. package/lib/api/validation-runner.js +109 -0
  54. package/lib/app/certification-show-enrich.js +129 -0
  55. package/lib/app/certification-verify-rows.js +60 -0
  56. package/lib/app/config.js +1 -1
  57. package/lib/app/deploy-status-display.js +2 -2
  58. package/lib/app/deploy.js +7 -6
  59. package/lib/app/display.js +2 -1
  60. package/lib/app/dockerfile.js +3 -2
  61. package/lib/app/down.js +2 -1
  62. package/lib/app/helpers.js +6 -5
  63. package/lib/app/index.js +27 -8
  64. package/lib/app/list.js +7 -6
  65. package/lib/app/push.js +4 -3
  66. package/lib/app/register.js +16 -7
  67. package/lib/app/rotate-secret.js +14 -13
  68. package/lib/app/run-container-start.js +184 -0
  69. package/lib/app/run-docker-fallback.js +108 -0
  70. package/lib/app/run-env-compose.js +30 -42
  71. package/lib/app/run-helpers.js +49 -126
  72. package/lib/app/run-infra-requirements.js +30 -0
  73. package/lib/app/run-resolve-image.js +21 -0
  74. package/lib/app/run.js +74 -21
  75. package/lib/app/show-display.js +44 -1
  76. package/lib/app/show.js +93 -9
  77. package/lib/build/index.js +13 -10
  78. package/lib/certification/cli-cert-sync-skip.js +21 -0
  79. package/lib/certification/merge-certification-from-artifact.js +185 -0
  80. package/lib/certification/post-unified-cert-sync.js +33 -0
  81. package/lib/certification/sync-after-external-command.js +52 -0
  82. package/lib/certification/sync-system-certification.js +197 -0
  83. package/lib/cli/index.js +2 -0
  84. package/lib/cli/setup-app.help.js +67 -0
  85. package/lib/cli/setup-app.js +61 -121
  86. package/lib/cli/setup-app.test-commands.js +195 -0
  87. package/lib/cli/setup-auth.js +19 -5
  88. package/lib/cli/setup-credential-deployment.js +22 -8
  89. package/lib/cli/setup-dev-path-commands.js +124 -0
  90. package/lib/cli/setup-dev.js +170 -113
  91. package/lib/cli/setup-environment.js +7 -1
  92. package/lib/cli/setup-external-system.js +84 -23
  93. package/lib/cli/setup-infra.js +126 -47
  94. package/lib/cli/setup-parameters.js +32 -0
  95. package/lib/cli/setup-secrets.js +137 -18
  96. package/lib/cli/setup-service-user.js +1 -1
  97. package/lib/cli/setup-utility.js +54 -22
  98. package/lib/commands/app-down.js +5 -7
  99. package/lib/commands/app-install.js +14 -7
  100. package/lib/commands/app-logs.js +13 -10
  101. package/lib/commands/app-shell.js +4 -1
  102. package/lib/commands/app-test.js +25 -19
  103. package/lib/commands/app.js +32 -11
  104. package/lib/commands/auth-config.js +6 -6
  105. package/lib/commands/auth-status.js +4 -3
  106. package/lib/commands/credential-env.js +4 -3
  107. package/lib/commands/credential-list.js +5 -4
  108. package/lib/commands/credential-push.js +4 -3
  109. package/lib/commands/datasource-unified-test-cli.js +428 -0
  110. package/lib/commands/datasource-unified-test-cli.options.js +191 -0
  111. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +106 -0
  112. package/lib/commands/datasource-validation-cli.js +143 -0
  113. package/lib/commands/datasource.js +125 -95
  114. package/lib/commands/deployment-list.js +6 -5
  115. package/lib/commands/dev-cli-handlers.js +122 -18
  116. package/lib/commands/dev-down.js +4 -3
  117. package/lib/commands/dev-init.js +231 -116
  118. package/lib/commands/dev-show-display.js +473 -0
  119. package/lib/commands/login-credentials.js +3 -2
  120. package/lib/commands/login-device.js +4 -3
  121. package/lib/commands/login.js +5 -4
  122. package/lib/commands/logout.js +8 -7
  123. package/lib/commands/parameters-validate.js +54 -0
  124. package/lib/commands/repair-datasource.js +314 -68
  125. package/lib/commands/repair-env-template.js +2 -2
  126. package/lib/commands/repair.js +21 -3
  127. package/lib/commands/secrets-list.js +23 -12
  128. package/lib/commands/secrets-remove-all.js +220 -0
  129. package/lib/commands/secrets-remove.js +21 -12
  130. package/lib/commands/secrets-set.js +21 -12
  131. package/lib/commands/secrets-validate.js +4 -4
  132. package/lib/commands/secure.js +10 -9
  133. package/lib/commands/service-user.js +26 -25
  134. package/lib/commands/test-e2e-external.js +27 -1
  135. package/lib/commands/up-common.js +3 -2
  136. package/lib/commands/up-dataplane.js +29 -16
  137. package/lib/commands/up-miso.js +19 -29
  138. package/lib/commands/upload.js +149 -39
  139. package/lib/commands/wizard-core-helpers.js +1 -1
  140. package/lib/commands/wizard-dataplane.js +4 -3
  141. package/lib/commands/wizard-helpers.js +3 -3
  142. package/lib/commands/wizard.js +2 -2
  143. package/lib/core/admin-secrets.js +14 -5
  144. package/lib/core/audit-logger.js +12 -4
  145. package/lib/core/config-attach-extensions.js +46 -0
  146. package/lib/core/config-runtime-paths.js +29 -0
  147. package/lib/core/config.js +55 -56
  148. package/lib/core/diff.js +3 -2
  149. package/lib/core/ensure-encryption-key.js +1 -1
  150. package/lib/core/secrets-ensure-infra.js +77 -0
  151. package/lib/core/secrets-ensure.js +120 -64
  152. package/lib/core/secrets-env-write.js +35 -7
  153. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  154. package/lib/core/secrets.js +200 -37
  155. package/lib/core/templates-env.js +4 -3
  156. package/lib/datasource/abac-validator.js +1 -10
  157. package/lib/datasource/deploy.js +75 -53
  158. package/lib/datasource/field-reference-validator.js +9 -6
  159. package/lib/datasource/integration-context.js +63 -0
  160. package/lib/datasource/list.js +8 -7
  161. package/lib/datasource/log-viewer.js +189 -67
  162. package/lib/datasource/resolve-app.js +4 -4
  163. package/lib/datasource/test-e2e.js +113 -146
  164. package/lib/datasource/test-integration.js +114 -122
  165. package/lib/datasource/unified-validation-run-body.js +68 -0
  166. package/lib/datasource/unified-validation-run-post.js +23 -0
  167. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  168. package/lib/datasource/unified-validation-run.js +93 -0
  169. package/lib/datasource/validate.js +157 -13
  170. package/lib/deployment/deployer.js +4 -3
  171. package/lib/deployment/environment.js +7 -6
  172. package/lib/deployment/push.js +17 -8
  173. package/lib/external-system/delete.js +4 -3
  174. package/lib/external-system/deploy.js +166 -53
  175. package/lib/external-system/download-helpers.js +1 -1
  176. package/lib/external-system/download.js +7 -6
  177. package/lib/external-system/generator.js +92 -6
  178. package/lib/external-system/integration-test-dispatch.js +26 -0
  179. package/lib/external-system/test-execution.js +5 -1
  180. package/lib/external-system/test-helpers.js +0 -4
  181. package/lib/external-system/test-system-level-helpers.js +110 -0
  182. package/lib/external-system/test-system-level.js +83 -44
  183. package/lib/external-system/test.js +59 -8
  184. package/lib/generator/builders.js +23 -11
  185. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  186. package/lib/generator/external.js +16 -4
  187. package/lib/generator/helpers.js +58 -3
  188. package/lib/generator/index.js +4 -0
  189. package/lib/generator/split-readme.js +12 -7
  190. package/lib/generator/split-variables.js +2 -1
  191. package/lib/generator/split.js +1 -1
  192. package/lib/generator/wizard-readme.js +3 -3
  193. package/lib/generator/wizard.js +8 -8
  194. package/lib/infrastructure/compose.js +70 -7
  195. package/lib/infrastructure/helpers-docker-check.js +67 -0
  196. package/lib/infrastructure/helpers.js +203 -42
  197. package/lib/infrastructure/index.js +31 -18
  198. package/lib/infrastructure/services.js +21 -67
  199. package/lib/internal/fs-real-sync.js +104 -0
  200. package/lib/internal/node-fs.js +98 -0
  201. package/lib/parameters/database-secret-values.js +173 -0
  202. package/lib/parameters/infra-kv-discovery.js +121 -0
  203. package/lib/parameters/infra-parameter-catalog.js +458 -0
  204. package/lib/parameters/infra-parameter-validate.js +64 -0
  205. package/lib/schema/application-schema.json +37 -17
  206. package/lib/schema/datasource-test-run.schema.json +493 -0
  207. package/lib/schema/deployment-rules.yaml +102 -63
  208. package/lib/schema/external-datasource.schema.json +1200 -442
  209. package/lib/schema/external-system.schema.json +203 -5
  210. package/lib/schema/flag-map-validation-run.json +31 -0
  211. package/lib/schema/infra-parameter.schema.json +106 -0
  212. package/lib/schema/infra.parameter.yaml +421 -0
  213. package/lib/schema/type/credential-auth-templates.json +40 -0
  214. package/lib/schema/type/document-storage.json +226 -0
  215. package/lib/schema/type/message-service.json +123 -0
  216. package/lib/schema/type/vector-store.json +88 -0
  217. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  218. package/lib/utils/api-error-handler.js +2 -2
  219. package/lib/utils/api.js +77 -17
  220. package/lib/utils/app-register-api.js +3 -2
  221. package/lib/utils/app-register-auth.js +1 -1
  222. package/lib/utils/app-register-config.js +4 -4
  223. package/lib/utils/app-register-display.js +3 -2
  224. package/lib/utils/app-register-validator.js +3 -2
  225. package/lib/utils/app-run-containers.js +26 -22
  226. package/lib/utils/app-scoped-config.js +31 -0
  227. package/lib/utils/app-service-env-from-builder.js +164 -0
  228. package/lib/utils/build-copy.js +1 -1
  229. package/lib/utils/build-helpers.js +20 -20
  230. package/lib/utils/build-resolve-image.js +165 -0
  231. package/lib/utils/cli-layout-chalk.js +8 -0
  232. package/lib/utils/cli-test-layout-chalk.js +267 -0
  233. package/lib/utils/cli-utils.js +88 -11
  234. package/lib/utils/compose-db-passwords.js +138 -0
  235. package/lib/utils/compose-generate-docker-compose.js +216 -0
  236. package/lib/utils/compose-generator.js +197 -291
  237. package/lib/utils/compose-miso-env.js +18 -0
  238. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  239. package/lib/utils/config-paths.js +166 -7
  240. package/lib/utils/config-scoped-resources-preference.js +41 -0
  241. package/lib/utils/configuration-env-resolver.js +11 -8
  242. package/lib/utils/controller-deployment-outcome.js +68 -0
  243. package/lib/utils/credential-display.js +2 -2
  244. package/lib/utils/credential-secrets-env.js +5 -5
  245. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  246. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  247. package/lib/utils/datasource-test-run-certificate-tty.js +82 -0
  248. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  249. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  250. package/lib/utils/datasource-test-run-display.js +459 -0
  251. package/lib/utils/datasource-test-run-exit.js +83 -0
  252. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  253. package/lib/utils/datasource-test-run-report-version.js +51 -0
  254. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  255. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  256. package/lib/utils/datasource-validation-watch.js +266 -0
  257. package/lib/utils/declarative-url-ports.js +47 -0
  258. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  259. package/lib/utils/dev-ca-install.js +185 -23
  260. package/lib/utils/dev-cert-helper.js +266 -17
  261. package/lib/utils/dev-hosts-helper.js +307 -0
  262. package/lib/utils/dev-init-cert-hints.js +37 -0
  263. package/lib/utils/dev-init-health-messages.js +52 -0
  264. package/lib/utils/dev-init-resolve.js +86 -0
  265. package/lib/utils/dev-init-ssh-merge.js +65 -0
  266. package/lib/utils/dev-ssh-config-helper.js +196 -0
  267. package/lib/utils/dev-user-groups.js +93 -0
  268. package/lib/utils/docker-build.js +42 -17
  269. package/lib/utils/docker-exec.js +28 -0
  270. package/lib/utils/docker-manifest-public-port.js +116 -0
  271. package/lib/utils/docker-not-running-hint.js +52 -0
  272. package/lib/utils/docker.js +98 -11
  273. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  274. package/lib/utils/env-config-loader.js +10 -91
  275. package/lib/utils/env-copy.js +19 -10
  276. package/lib/utils/env-map.js +35 -8
  277. package/lib/utils/env-template.js +2 -2
  278. package/lib/utils/environment-scoped-resources.js +144 -0
  279. package/lib/utils/error-formatter.js +92 -13
  280. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  281. package/lib/utils/error-formatters/network-errors.js +2 -1
  282. package/lib/utils/error-formatters/permission-errors.js +2 -1
  283. package/lib/utils/error-formatters/validation-errors.js +2 -1
  284. package/lib/utils/external-readme.js +8 -1
  285. package/lib/utils/external-system-display.js +242 -136
  286. package/lib/utils/external-system-local-test-tty.js +389 -0
  287. package/lib/utils/external-system-readiness-core.js +377 -0
  288. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  289. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  290. package/lib/utils/external-system-readiness-display.js +186 -0
  291. package/lib/utils/external-system-system-test-tty-overview.js +120 -0
  292. package/lib/utils/external-system-system-test-tty.js +417 -0
  293. package/lib/utils/external-system-test-helpers.js +24 -6
  294. package/lib/utils/external-system-validators.js +30 -12
  295. package/lib/utils/health-check-url.js +119 -0
  296. package/lib/utils/health-check.js +59 -25
  297. package/lib/utils/help-builder.js +11 -8
  298. package/lib/utils/image-version.js +4 -8
  299. package/lib/utils/infra-containers.js +4 -7
  300. package/lib/utils/infra-env-defaults.js +162 -0
  301. package/lib/utils/infra-status-display.js +167 -0
  302. package/lib/utils/infra-status.js +16 -8
  303. package/lib/utils/local-secrets.js +3 -4
  304. package/lib/utils/paths.js +148 -47
  305. package/lib/utils/port-resolver.js +10 -23
  306. package/lib/utils/redis-env-scope.js +62 -0
  307. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  308. package/lib/utils/remote-builder-validation.js +99 -0
  309. package/lib/utils/remote-dev-auth.js +117 -21
  310. package/lib/utils/remote-docker-env.js +67 -15
  311. package/lib/utils/remote-secrets-loader.js +13 -4
  312. package/lib/utils/resolve-docker-image-ref.js +124 -0
  313. package/lib/utils/schema-loader.js +22 -9
  314. package/lib/utils/secrets-bash-kv.js +25 -0
  315. package/lib/utils/secrets-generator.js +169 -49
  316. package/lib/utils/secrets-helpers.js +70 -59
  317. package/lib/utils/secrets-kv-scope.js +60 -0
  318. package/lib/utils/secrets-utils.js +32 -38
  319. package/lib/utils/secrets-validation.js +3 -1
  320. package/lib/utils/secrets-yaml-preserve.js +109 -0
  321. package/lib/utils/ssh-key-helper.js +4 -2
  322. package/lib/utils/template-helpers.js +2 -2
  323. package/lib/utils/test-log-writer.js +3 -3
  324. package/lib/utils/token-manager.js +1 -2
  325. package/lib/utils/url-declarative-public-base.js +188 -0
  326. package/lib/utils/url-declarative-resolve-build.js +493 -0
  327. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  328. package/lib/utils/url-declarative-resolve.js +220 -0
  329. package/lib/utils/url-declarative-token-parse.js +74 -0
  330. package/lib/utils/url-declarative-url-flags.js +50 -0
  331. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  332. package/lib/utils/url-public-path-prefix.js +34 -0
  333. package/lib/utils/urls-local-registry.js +220 -0
  334. package/lib/utils/validation-report-tty-kit.js +77 -0
  335. package/lib/utils/validation-run-poll.js +112 -0
  336. package/lib/utils/validation-run-post-retry.js +85 -0
  337. package/lib/utils/validation-run-request.js +116 -0
  338. package/lib/utils/variable-transformer.js +21 -4
  339. package/lib/utils/yaml-preserve.js +33 -14
  340. package/lib/validation/datasource-warnings.js +56 -0
  341. package/lib/validation/env-template-auth.js +1 -1
  342. package/lib/validation/external-manifest-validator.js +27 -7
  343. package/lib/validation/validate-display.js +37 -31
  344. package/lib/validation/validate-external-cert-sync.js +23 -0
  345. package/lib/validation/validate.js +8 -14
  346. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  347. package/lib/validation/validator.js +22 -65
  348. package/lib/validation/wizard-config-validator.js +2 -1
  349. package/package.json +9 -4
  350. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  351. package/scripts/diagnose-cli.js +150 -0
  352. package/scripts/install-local.js +307 -55
  353. package/scripts/pnpm-global-remove.js +48 -0
  354. package/templates/README.md +15 -2
  355. package/templates/applications/dataplane/application.yaml +52 -2
  356. package/templates/applications/dataplane/env.template +79 -17
  357. package/templates/applications/dataplane/rbac.yaml +8 -0
  358. package/templates/applications/keycloak/application.yaml +9 -1
  359. package/templates/applications/keycloak/env.template +15 -6
  360. package/templates/applications/miso-controller/application.yaml +10 -2
  361. package/templates/applications/miso-controller/env.template +42 -12
  362. package/templates/applications/miso-controller/rbac.yaml +5 -0
  363. package/templates/external-system/README.md.hbs +20 -7
  364. package/templates/external-system/deploy.js.hbs +5 -5
  365. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  366. package/templates/infra/compose.yaml.hbs +33 -16
  367. package/templates/infra/servers.json.hbs +3 -1
  368. package/templates/python/docker-compose.hbs +16 -0
  369. package/templates/typescript/docker-compose.hbs +16 -0
  370. package/lib/api/external-test.api.js +0 -111
  371. package/lib/schema/env-config.yaml +0 -60
@@ -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 };