@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
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Single source of truth for resolving application port from application config.
5
5
  * Use getContainerPort for container/Docker/deployment/registration; use getLocalPort
6
- * for local .env and dev-id–adjusted host port.
6
+ * for manifest listen port (same as port; local host uses port+10+dev*100 in secrets-helpers).
7
7
  *
8
8
  * @fileoverview Port resolution from variables (port, build.containerPort)
9
9
  * @author AI Fabrix Team
@@ -14,6 +14,7 @@
14
14
 
15
15
  const fs = require('fs');
16
16
  const yaml = require('js-yaml');
17
+ const { DECLARATIVE_URL_INFRA_DEFAULTS } = require('./infra-env-defaults');
17
18
 
18
19
  /**
19
20
  * Resolve container port from variables object.
@@ -21,10 +22,10 @@ const yaml = require('js-yaml');
21
22
  * When containerPort is empty or missing, main port is used (e.g. keycloak 8082:8080 vs miso 3000:3000).
22
23
  *
23
24
  * @param {Object} variables - Parsed application config (or subset with build, port)
24
- * @param {number} [defaultPort=3000] - Default when neither build.containerPort nor port is set
25
+ * @param {number} [defaultPort] - Default when neither build.containerPort nor port is set (infra fallback)
25
26
  * @returns {number} Resolved container port
26
27
  */
27
- function getContainerPort(variables, defaultPort = 3000) {
28
+ function getContainerPort(variables, defaultPort = DECLARATIVE_URL_INFRA_DEFAULTS.manifestPortFallback) {
28
29
  const v = variables || {};
29
30
  const containerPort = v.build?.containerPort;
30
31
  const useMain = containerPort === undefined || containerPort === null ||
@@ -36,20 +37,13 @@ function getContainerPort(variables, defaultPort = 3000) {
36
37
  }
37
38
 
38
39
  /**
39
- * Resolve local (development) port from variables object.
40
- * Precedence: build.localPort (when positive integer) → port → defaultPort.
41
- * Used for env-copy, env-ports, getLocalPortFromPath, run compose host port.
42
- *
40
+ * Resolve base application listen port (manifest `port` only; build.localPort removed).
43
41
  * @param {Object} variables - Parsed application config
44
- * @param {number} [defaultPort=3000] - Default when neither build.localPort nor port is set
45
- * @returns {number} Resolved local port
42
+ * @param {number} [defaultPort] - Default when port is unset (infra fallback)
43
+ * @returns {number} Listen port
46
44
  */
47
- function getLocalPort(variables, defaultPort = 3000) {
45
+ function getLocalPort(variables, defaultPort = DECLARATIVE_URL_INFRA_DEFAULTS.manifestPortFallback) {
48
46
  const v = variables || {};
49
- const localPort = v.build?.localPort;
50
- if (typeof localPort === 'number' && localPort > 0) {
51
- return localPort;
52
- }
53
47
  return v.port ?? defaultPort;
54
48
  }
55
49
 
@@ -94,22 +88,15 @@ function getContainerPortFromPath(variablesPath) {
94
88
  }
95
89
 
96
90
  /**
97
- * Resolve local port from application config path.
98
- * Same rule as getLocalPort: build.localPort (when positive integer) → port.
99
- * Returns null when file is missing or neither localPort nor port is set.
100
- *
91
+ * Resolve manifest port from application config path.
101
92
  * @param {string} variablesPath - Path to application config
102
- * @returns {number|null} Local port or null
93
+ * @returns {number|null} Port or null
103
94
  */
104
95
  function getLocalPortFromPath(variablesPath) {
105
96
  const v = loadVariablesFromPath(variablesPath);
106
97
  if (!v) {
107
98
  return null;
108
99
  }
109
- const localPort = v.build?.localPort;
110
- if (typeof localPort === 'number' && localPort > 0) {
111
- return localPort;
112
- }
113
100
  const p = v.port;
114
101
  return (p !== undefined && p !== null) ? p : null;
115
102
  }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Adjust Redis DB index in resolved .env content when environment-scoped resources are effective.
3
+ *
4
+ * @fileoverview REDIS_DB and redis:// URL path segment (plan 117)
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * Set pathname /dbIndex for redis(s) URLs.
13
+ *
14
+ * @param {string} urlStr - redis:// or rediss:// URL
15
+ * @param {number} dbIndex - logical DB (0–15 typical)
16
+ * @returns {string}
17
+ */
18
+ function setRedisUrlDbIndex(urlStr, dbIndex) {
19
+ if (!urlStr || typeof urlStr !== 'string') return urlStr;
20
+ try {
21
+ const u = new URL(urlStr.trim());
22
+ if (u.protocol !== 'redis:' && u.protocol !== 'rediss:') {
23
+ return urlStr;
24
+ }
25
+ u.pathname = `/${dbIndex}`;
26
+ return u.toString();
27
+ } catch {
28
+ return urlStr;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Apply Redis DB index to REDIS_DB= and REDIS_URL= lines when effective.
34
+ *
35
+ * @param {string} content - .env text
36
+ * @param {number|null} dbIndex - from redisDbIndexForScopedRunEnv; skip if null
37
+ * @returns {string}
38
+ */
39
+ function applyRedisDbIndexToEnvContent(content, dbIndex) {
40
+ if (dbIndex === null || dbIndex === undefined || typeof content !== 'string') {
41
+ return content;
42
+ }
43
+ const lines = content.split('\n');
44
+ const out = lines.map((line) => {
45
+ const trimmed = line.trim();
46
+ if (!trimmed || trimmed.startsWith('#')) return line;
47
+ const eq = trimmed.indexOf('=');
48
+ if (eq <= 0) return line;
49
+ const key = trimmed.slice(0, eq).trim();
50
+ const value = trimmed.slice(eq + 1);
51
+ if (key === 'REDIS_DB') {
52
+ return `${key}=${dbIndex}`;
53
+ }
54
+ if (key === 'REDIS_URL' && value && !value.startsWith('kv://')) {
55
+ return `${key}=${setRedisUrlDbIndex(value, dbIndex)}`;
56
+ }
57
+ return line;
58
+ });
59
+ return out.join('\n');
60
+ }
61
+
62
+ module.exports = { applyRedisDbIndexToEnvContent, setRedisUrlDbIndex };
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Register AIFABRIX_HOME and AIFABRIX_WORK for new shells: Windows user env vars;
3
+ * POSIX writes a sourced script next to config.yaml and ensures a profile snippet.
4
+ *
5
+ * @fileoverview OS/shell registration after dev set-home / dev set-work
6
+ * @author AI Fabrix Team
7
+ * @version 2.0.0
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const fsp = require('node:fs').promises;
13
+ const path = require('path');
14
+ const os = require('os');
15
+ const { promisify } = require('util');
16
+ const execFileCb = require('child_process').execFile;
17
+ const execFile = promisify(execFileCb);
18
+
19
+ const { getConfigDirForPaths } = require('./paths');
20
+
21
+ const SHELL_ENV_BASENAME = 'aifabrix-shell-env.sh';
22
+ const BLOCK_BEGIN = '# BEGIN aifabrix-builder shell env';
23
+ const BLOCK_END = '# END aifabrix-builder shell env';
24
+
25
+ const PROFILE_BLOCK_RE = /\n?# BEGIN aifabrix-builder shell env\n[\s\S]*?\n# END aifabrix-builder shell env\n?/;
26
+
27
+ /**
28
+ * Single-quote a path for POSIX export lines.
29
+ * @param {string} p - Path
30
+ * @returns {string} Quoted for sh
31
+ */
32
+ function shSingleQuoted(p) {
33
+ const esc = String.fromCharCode(39, 92, 39, 39);
34
+ return `'${String(p).replace(/'/g, esc)}'`;
35
+ }
36
+
37
+ /**
38
+ * Build body for aifabrix-shell-env.sh (exports only; no secrets).
39
+ * @param {string|null} homeAbs - Resolved home or null
40
+ * @param {string|null} workAbs - Resolved work or null
41
+ * @returns {string} File content
42
+ */
43
+ function buildPosixShellEnvBody(homeAbs, workAbs) {
44
+ const lines = ['# Managed by aifabrix dev set-home / set-work. Do not edit.'];
45
+ if (homeAbs) {
46
+ lines.push(`export AIFABRIX_HOME=${shSingleQuoted(homeAbs)}`);
47
+ }
48
+ if (workAbs) {
49
+ lines.push(`export AIFABRIX_WORK=${shSingleQuoted(workAbs)}`);
50
+ }
51
+ return `${lines.join('\n')}\n`;
52
+ }
53
+
54
+ /**
55
+ * Profile snippet that sources the shell env file.
56
+ * @param {string} envFileAbs - Absolute path to aifabrix-shell-env.sh
57
+ * @returns {string} Block including markers
58
+ */
59
+ function buildProfileBlock(envFileAbs) {
60
+ const q = shSingleQuoted(envFileAbs);
61
+ return `${BLOCK_BEGIN}
62
+ [ -f ${q} ] && . ${q}
63
+ ${BLOCK_END}
64
+ `;
65
+ }
66
+
67
+ /**
68
+ * Resolve YAML path string to absolute or null.
69
+ * @param {*} raw - Config value
70
+ * @returns {string|null}
71
+ */
72
+ function absFromConfigRaw(raw) {
73
+ if (typeof raw !== 'string') return null;
74
+ const t = raw.trim();
75
+ if (!t) return null;
76
+ return path.resolve(t);
77
+ }
78
+
79
+ /**
80
+ * PowerShell to set or remove a user-scoped environment variable.
81
+ * @param {string} name - Var name (caller must pass safe identifiers only)
82
+ * @param {string|null} value - Absolute path or null to remove
83
+ * @returns {string} PowerShell -Command argument body
84
+ */
85
+ function psSetUserEnvStatement(name, value) {
86
+ if (value === null || value === undefined || value === '') {
87
+ return `[System.Environment]::SetEnvironmentVariable('${name}', $null, 'User')`;
88
+ }
89
+ const escaped = String(value).replace(/'/g, '\'\'');
90
+ return `[System.Environment]::SetEnvironmentVariable('${name}', '${escaped}', 'User')`;
91
+ }
92
+
93
+ /**
94
+ * Default rc file to patch (zsh vs bash).
95
+ * @param {string} homedir - User home
96
+ * @returns {string} Absolute profile path
97
+ */
98
+ function defaultProfilePath(homedir) {
99
+ const shell = process.env.SHELL || '';
100
+ if (shell.includes('zsh')) {
101
+ return path.join(homedir, '.zshrc');
102
+ }
103
+ return path.join(homedir, '.bashrc');
104
+ }
105
+
106
+ /**
107
+ * Ensure ~/.zshrc or ~/.bashrc contains a single marked block sourcing envFileAbs.
108
+ * @param {string} envFileAbs - Shell env file
109
+ * @param {object} [overrides] - Test hooks
110
+ * @param {string} [overrides.profilePath] - Profile file to edit
111
+ * @param {string} [overrides.homedir] - Home directory
112
+ * @returns {Promise<void>}
113
+ */
114
+ async function ensureProfileShellBlock(envFileAbs, overrides = {}) {
115
+ const homedir = overrides.homedir ?? os.homedir();
116
+ const profilePath = overrides.profilePath ?? defaultProfilePath(homedir);
117
+ const snippet = buildProfileBlock(envFileAbs);
118
+ let existing = '';
119
+ try {
120
+ existing = await fsp.readFile(profilePath, 'utf8');
121
+ } catch (err) {
122
+ if (err.code !== 'ENOENT') {
123
+ throw err;
124
+ }
125
+ }
126
+ if (PROFILE_BLOCK_RE.test(existing)) {
127
+ const next = existing.replace(PROFILE_BLOCK_RE, `\n${snippet.trimEnd()}\n`);
128
+ if (next !== existing) {
129
+ await fsp.writeFile(profilePath, next, 'utf8');
130
+ }
131
+ return;
132
+ }
133
+ const sep = existing && !existing.endsWith('\n') ? '\n' : '';
134
+ await fsp.writeFile(profilePath, `${existing}${sep}${snippet}`, 'utf8');
135
+ }
136
+
137
+ /**
138
+ * Apply Windows user env for both vars from current config (independent clear per key).
139
+ * @param {string|null} homeAbs
140
+ * @param {string|null} workAbs
141
+ * @param {Function} [execFileImpl] - execFile (for tests)
142
+ * @returns {Promise<void>}
143
+ */
144
+ async function applyWindowsUserEnv(homeAbs, workAbs, execFileImpl = execFile) {
145
+ const script = `${psSetUserEnvStatement('AIFABRIX_HOME', homeAbs)}; ${psSetUserEnvStatement(
146
+ 'AIFABRIX_WORK',
147
+ workAbs
148
+ )}`;
149
+ await execFileImpl(
150
+ 'powershell.exe',
151
+ ['-NoProfile', '-NonInteractive', '-Command', script],
152
+ { windowsHide: true }
153
+ );
154
+ }
155
+
156
+ /**
157
+ * Write POSIX shell env file and ensure profile sources it.
158
+ * @param {string|null} homeAbs
159
+ * @param {string|null} workAbs
160
+ * @param {object} overrides - Test hooks (same as ensureProfileShellBlock + getConfigDirForPaths)
161
+ * @returns {Promise<void>}
162
+ */
163
+ async function applyPosixShellEnv(homeAbs, workAbs, overrides = {}) {
164
+ const configDir = overrides.getConfigDirForPaths ? overrides.getConfigDirForPaths() : getConfigDirForPaths();
165
+ const envFile = path.join(configDir, SHELL_ENV_BASENAME);
166
+ await fsp.mkdir(configDir, { recursive: true });
167
+ const body = buildPosixShellEnvBody(homeAbs, workAbs);
168
+ await fsp.writeFile(envFile, body, { mode: 0o600, flag: 'w' });
169
+ await ensureProfileShellBlock(path.resolve(envFile), overrides);
170
+ }
171
+
172
+ /**
173
+ * Sync user/shell environment from saved config.yaml (after set-home or set-work).
174
+ *
175
+ * @async
176
+ * @param {function(): Promise<object>} getConfigFn - Same as config.getConfig
177
+ * @param {object} [overrides] - Optional { platform, execFile, getConfigDirForPaths, profilePath, homedir }
178
+ * @returns {Promise<void>}
179
+ */
180
+ async function registerAifabrixShellEnvFromConfig(getConfigFn, overrides = {}) {
181
+ const platform = overrides.platform ?? process.platform;
182
+ const config = await getConfigFn();
183
+ const homeAbs = absFromConfigRaw(config['aifabrix-home']);
184
+ const workAbs = absFromConfigRaw(config['aifabrix-work']);
185
+
186
+ if (platform === 'win32') {
187
+ await applyWindowsUserEnv(homeAbs, workAbs, overrides.execFile || execFile);
188
+ return;
189
+ }
190
+
191
+ await applyPosixShellEnv(homeAbs, workAbs, overrides);
192
+ }
193
+
194
+ module.exports = {
195
+ registerAifabrixShellEnvFromConfig,
196
+ buildPosixShellEnvBody,
197
+ buildProfileBlock,
198
+ shSingleQuoted,
199
+ psSetUserEnvStatement,
200
+ ensureProfileShellBlock,
201
+ SHELL_ENV_BASENAME,
202
+ BLOCK_BEGIN,
203
+ BLOCK_END
204
+ };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Remote shared builder vs local Docker Desktop — developer-id rules (plan 018).
3
+ *
4
+ * @fileoverview Fail fast when remote-server is non-localhost but developer-id is not a positive integer
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * @param {string|null|undefined} remoteServer - remote-server from config (URL or host)
13
+ * @returns {boolean} True when hostname is set and not localhost / loopback only
14
+ */
15
+ function remoteServerHostIsNonLocalhost(remoteServer) {
16
+ const raw =
17
+ remoteServer === null || remoteServer === undefined ? '' : String(remoteServer).trim();
18
+ if (!raw) {
19
+ return false;
20
+ }
21
+ try {
22
+ const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(raw) ? raw : `https://${raw}`;
23
+ const u = new URL(withScheme);
24
+ let h = (u.hostname || '').toLowerCase();
25
+ if (h.startsWith('[') && h.endsWith(']')) {
26
+ h = h.slice(1, -1);
27
+ }
28
+ if (!h) {
29
+ return false;
30
+ }
31
+ if (h === 'localhost' || h === '127.0.0.1' || h === '::1') {
32
+ return false;
33
+ }
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Host for user-facing messages (no path, no credentials).
42
+ * @param {string|null|undefined} remoteServer
43
+ * @returns {string}
44
+ */
45
+ function remoteServerDisplayHost(remoteServer) {
46
+ const raw =
47
+ remoteServer === null || remoteServer === undefined ? '' : String(remoteServer).trim();
48
+ if (!raw) {
49
+ return '(remote-server)';
50
+ }
51
+ try {
52
+ const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(raw) ? raw : `https://${raw}`;
53
+ const u = new URL(withScheme);
54
+ return u.host || raw;
55
+ } catch {
56
+ return raw;
57
+ }
58
+ }
59
+
60
+ const REMOTE_DEV_ID_BODY =
61
+ 'Remote builder at %s requires a positive developer-id (1, 2, 3, …).\n' +
62
+ 'Developer-id 0 is only supported for local Docker Desktop (localhost).\n' +
63
+ 'Set developer-id in ~/.aifabrix/config.yaml to match your dev account (e.g. 1 for dev01), ' +
64
+ 'or run onboarding against the builder: aifabrix dev init …';
65
+
66
+ const REMOTE_DEV_ID_TAIL =
67
+ '\n\nIf you meant to work only on this machine, set remote-server to use localhost or remove the shared builder URL.';
68
+
69
+ /**
70
+ * When remote-server points at a shared (non-local) builder, require developer-id ≥ 1.
71
+ * @param {string|null|undefined} remoteServer
72
+ * @param {string|number|null|undefined} developerIdRaw
73
+ * @throws {Error} When remote builder and id missing, non-numeric, or < 1
74
+ */
75
+ function assertRemoteBuilderDeveloperId(remoteServer, developerIdRaw) {
76
+ if (!remoteServerHostIsNonLocalhost(remoteServer)) {
77
+ return;
78
+ }
79
+ const host = remoteServerDisplayHost(remoteServer);
80
+ const head = REMOTE_DEV_ID_BODY.replace('%s', host);
81
+
82
+ if (developerIdRaw === null || developerIdRaw === undefined || developerIdRaw === '') {
83
+ throw new Error(head + REMOTE_DEV_ID_TAIL);
84
+ }
85
+ const s = String(developerIdRaw).trim();
86
+ if (!/^\d+$/.test(s)) {
87
+ throw new Error(head + REMOTE_DEV_ID_TAIL);
88
+ }
89
+ const n = parseInt(s, 10);
90
+ if (n < 1) {
91
+ throw new Error(head + REMOTE_DEV_ID_TAIL);
92
+ }
93
+ }
94
+
95
+ module.exports = {
96
+ remoteServerHostIsNonLocalhost,
97
+ remoteServerDisplayHost,
98
+ assertRemoteBuilderDeveloperId
99
+ };
@@ -4,35 +4,131 @@
4
4
  * @version 2.0.0
5
5
  */
6
6
 
7
+ const path = require('path');
7
8
  const config = require('../core/config');
8
- const { getCertDir, readClientCertPem } = require('./dev-cert-helper');
9
- const { getConfigDirForPaths } = require('./paths');
9
+ const { getCertDir, readClientCertPem, readServerCaPem } = require('./dev-cert-helper');
10
+ const { getConfigDirForPaths, getAifabrixHome, getAifabrixWork } = require('./paths');
10
11
 
11
12
  /**
12
- * Check if a string is an http(s) URL (for aifabrix-secrets remote mode).
13
- * @param {string} value - Config value
13
+ * Single API object so resolveSharedSecretsEndpoint and callers share one getRemoteDevAuth
14
+ * (Jest spies and partial mocks work reliably).
15
+ */
16
+ const remoteDevAuth = {
17
+ /**
18
+ * Check if a string is an http(s) URL (for aifabrix-secrets remote mode).
19
+ * @param {string} value - Config value
20
+ * @returns {boolean}
21
+ */
22
+ isRemoteSecretsUrl(value) {
23
+ return typeof value === 'string' && (value.startsWith('http://') || value.startsWith('https://'));
24
+ },
25
+
26
+ /**
27
+ * Get Builder Server URL, client cert PEM, and optional dev root CA when remote is configured; otherwise null.
28
+ * serverCaPem comes from ca.pem (saved at dev init); required for Node TLS to private-CA servers.
29
+ * Use for cert-authenticated dev API calls (settings, users, ssh-keys, secrets).
30
+ * @returns {Promise<{ serverUrl: string, clientCertPem: string, serverCaPem: string|null }|null>}
31
+ */
32
+ async getRemoteDevAuth() {
33
+ const serverUrl = await config.getRemoteServer();
34
+ if (!serverUrl) return null;
35
+ const devId = await config.getDeveloperId();
36
+ const certDir = getCertDir(getConfigDirForPaths(), devId);
37
+ const clientCertPem = readClientCertPem(certDir);
38
+ if (!clientCertPem) return null;
39
+ const serverCaPem = readServerCaPem(certDir);
40
+ return { serverUrl, clientCertPem, serverCaPem };
41
+ },
42
+
43
+ /**
44
+ * Resolve where shared secrets read/write should go. After dev init, config may still hold a
45
+ * server-side filesystem path from GET /api/dev/settings (not an http URL) if merge did not rewrite it.
46
+ * Those paths are not writable on the developer machine; use the Builder secrets API instead when
47
+ * remote-server + client cert are configured and the path is not under aifabrix-home or aifabrix-work.
48
+ *
49
+ * @param {string} configuredPath - config.yaml aifabrix-secrets
50
+ * @returns {Promise<string>} Same string for local file targets, or https?://…/api/dev/secrets for remote API
51
+ */
52
+ async resolveSharedSecretsEndpoint(configuredPath) {
53
+ if (!configuredPath || typeof configuredPath !== 'string') return configuredPath;
54
+ const trimmed = configuredPath.trim();
55
+ if (!trimmed) return configuredPath;
56
+ if (remoteDevAuth.isRemoteSecretsUrl(trimmed)) return trimmed.replace(/\/+$/, '');
57
+ const auth = await remoteDevAuth.getRemoteDevAuth();
58
+ if (!auth) return configuredPath;
59
+ const abs = normalizeSharedSecretsFilePath(trimmed);
60
+ if (!abs) return configuredPath;
61
+ const home = path.normalize(getAifabrixHome());
62
+ if (isPathUnderDir(abs, home)) return configuredPath;
63
+ const work = getAifabrixWork();
64
+ if (work) {
65
+ const w = path.normalize(work);
66
+ if (isPathUnderDir(abs, w)) return configuredPath;
67
+ }
68
+ const base = String(auth.serverUrl).replace(/\/+$/, '');
69
+ return `${base}/api/dev/secrets`;
70
+ },
71
+
72
+ /**
73
+ * Hostname from a resolved shared-secrets API URL (for CLI labels).
74
+ * @param {string} secretsEndpointUrl - e.g. https://builder02.local/api/dev/secrets
75
+ * @returns {string|null} Hostname, or null if the URL cannot be parsed
76
+ */
77
+ getSharedSecretsRemoteHostname(secretsEndpointUrl) {
78
+ try {
79
+ const u = new URL(String(secretsEndpointUrl));
80
+ return u.hostname || null;
81
+ } catch {
82
+ return null;
83
+ }
84
+ },
85
+
86
+ /**
87
+ * Titles/messages for `secret list --shared` when the store is remote.
88
+ * @param {string} secretsEndpointUrl - Resolved secrets API URL
89
+ * @returns {{ title: string, emptyMessage: string }}
90
+ */
91
+ getSharedSecretsRemoteListLabels(secretsEndpointUrl) {
92
+ const host = remoteDevAuth.getSharedSecretsRemoteHostname(secretsEndpointUrl);
93
+ if (host) {
94
+ return {
95
+ title: `Shared secrets (remote - ${host})`,
96
+ emptyMessage: `No shared secrets (remote - ${host}).`
97
+ };
98
+ }
99
+ return {
100
+ title: 'Shared secrets (remote)',
101
+ emptyMessage: 'No shared secrets (remote).'
102
+ };
103
+ }
104
+ };
105
+
106
+ /**
107
+ * True when normalized file path is dir or a descendant of base (same volume semantics as path relative checks).
108
+ * @param {string} filePath - Normalized absolute path
109
+ * @param {string} baseDir - Normalized absolute directory
14
110
  * @returns {boolean}
15
111
  */
16
- function isRemoteSecretsUrl(value) {
17
- return typeof value === 'string' && (value.startsWith('http://') || value.startsWith('https://'));
112
+ function isPathUnderDir(filePath, baseDir) {
113
+ if (!filePath || !baseDir) return false;
114
+ const f = filePath;
115
+ const d = baseDir;
116
+ if (f === d) return true;
117
+ const prefix = d.endsWith(path.sep) ? d : d + path.sep;
118
+ return f.startsWith(prefix);
18
119
  }
19
120
 
20
121
  /**
21
- * Get Builder Server URL and client cert PEM when remote is configured; otherwise null.
22
- * Use for cert-authenticated dev API calls (settings, users, ssh-keys, secrets).
23
- * @returns {Promise<{ serverUrl: string, clientCertPem: string }|null>}
122
+ * Normalize configured shared-secrets file path for "is this on the laptop?" checks.
123
+ * @param {string} configured - Raw config value
124
+ * @returns {string} Absolute normalized path
24
125
  */
25
- async function getRemoteDevAuth() {
26
- const serverUrl = await config.getRemoteServer();
27
- if (!serverUrl) return null;
28
- const devId = await config.getDeveloperId();
29
- const certDir = getCertDir(getConfigDirForPaths(), devId);
30
- const clientCertPem = readClientCertPem(certDir);
31
- if (!clientCertPem) return null;
32
- return { serverUrl, clientCertPem };
126
+ function normalizeSharedSecretsFilePath(configured) {
127
+ const trimmed = String(configured || '').trim();
128
+ if (!trimmed) return '';
129
+ return path.isAbsolute(trimmed)
130
+ ? path.normalize(trimmed)
131
+ : path.normalize(path.resolve(process.cwd(), trimmed));
33
132
  }
34
133
 
35
- module.exports = {
36
- isRemoteSecretsUrl,
37
- getRemoteDevAuth
38
- };
134
+ module.exports = remoteDevAuth;