@aifabrix/builder 2.43.0 → 2.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/README.md +1 -1
  3. package/anchor-docs/README.md +10 -0
  4. package/anchor-docs/_TEMPLATE +24 -0
  5. package/bin/aifabrix.js +13 -4
  6. package/integration/hubspot-test/README.md +31 -0
  7. package/integration/hubspot-test/create-hubspot.js +5 -5
  8. package/integration/hubspot-test/hubspot-test-datasource-company.json +58 -462
  9. package/integration/hubspot-test/hubspot-test-datasource-contact.json +61 -555
  10. package/integration/hubspot-test/hubspot-test-datasource-deal.json +63 -506
  11. package/integration/hubspot-test/hubspot-test-datasource-users.json +42 -83
  12. package/integration/hubspot-test/hubspot-test-deploy.json +3 -3
  13. package/integration/hubspot-test/test-dataplane-down-tests.js +1 -7
  14. package/integration/hubspot-test/test-dataplane-down.js +3 -3
  15. package/integration/hubspot-test/test.js +35 -43
  16. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  17. package/integration/roundtrip-test-local/README.md +144 -0
  18. package/integration/roundtrip-test-local/application.yaml +13 -0
  19. package/integration/roundtrip-test-local/env.template +15 -0
  20. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  21. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  22. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  23. package/integration/roundtrip-test-local2/README.md +144 -0
  24. package/integration/roundtrip-test-local2/application.yaml +13 -0
  25. package/integration/roundtrip-test-local2/env.template +15 -0
  26. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  27. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  28. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  29. package/integration/test/wizard.yaml +8 -0
  30. package/jest.config.default.js +10 -0
  31. package/jest.config.integration.fixtures.js +22 -0
  32. package/jest.config.integration.js +21 -18
  33. package/jest.config.isolated.js +10 -0
  34. package/jest.projects.js +288 -0
  35. package/lib/api/datasources-core.api.js +3 -3
  36. package/lib/api/dev-mtls-request.js +110 -0
  37. package/lib/api/dev-server-https.js +145 -0
  38. package/lib/api/dev.api.js +133 -144
  39. package/lib/api/index.js +0 -1
  40. package/lib/api/pipeline.api.js +67 -20
  41. package/lib/api/types/dev.types.js +4 -3
  42. package/lib/api/types/pipeline.types.js +8 -5
  43. package/lib/api/types/validation-run.types.js +56 -0
  44. package/lib/api/validation-run.api.js +99 -0
  45. package/lib/api/validation-runner.js +99 -0
  46. package/lib/app/config.js +1 -1
  47. package/lib/app/deploy-status-display.js +2 -2
  48. package/lib/app/deploy.js +7 -6
  49. package/lib/app/display.js +2 -1
  50. package/lib/app/dockerfile.js +3 -2
  51. package/lib/app/down.js +2 -1
  52. package/lib/app/helpers.js +6 -5
  53. package/lib/app/index.js +27 -8
  54. package/lib/app/list.js +7 -6
  55. package/lib/app/push.js +4 -3
  56. package/lib/app/register.js +16 -7
  57. package/lib/app/rotate-secret.js +14 -13
  58. package/lib/app/run-container-start.js +184 -0
  59. package/lib/app/run-docker-fallback.js +108 -0
  60. package/lib/app/run-env-compose.js +30 -42
  61. package/lib/app/run-helpers.js +49 -126
  62. package/lib/app/run-infra-requirements.js +30 -0
  63. package/lib/app/run-resolve-image.js +21 -0
  64. package/lib/app/run.js +74 -21
  65. package/lib/app/show-display.js +1 -1
  66. package/lib/app/show.js +1 -1
  67. package/lib/build/index.js +13 -10
  68. package/lib/cli/index.js +2 -0
  69. package/lib/cli/setup-app.help.js +67 -0
  70. package/lib/cli/setup-app.js +57 -121
  71. package/lib/cli/setup-app.test-commands.js +179 -0
  72. package/lib/cli/setup-auth.js +19 -5
  73. package/lib/cli/setup-credential-deployment.js +22 -8
  74. package/lib/cli/setup-dev-path-commands.js +124 -0
  75. package/lib/cli/setup-dev.js +170 -113
  76. package/lib/cli/setup-environment.js +7 -1
  77. package/lib/cli/setup-external-system.js +62 -22
  78. package/lib/cli/setup-infra.js +126 -47
  79. package/lib/cli/setup-parameters.js +32 -0
  80. package/lib/cli/setup-secrets.js +106 -8
  81. package/lib/cli/setup-service-user.js +1 -1
  82. package/lib/cli/setup-utility.js +36 -20
  83. package/lib/commands/app-down.js +5 -7
  84. package/lib/commands/app-install.js +14 -7
  85. package/lib/commands/app-logs.js +13 -10
  86. package/lib/commands/app-shell.js +4 -1
  87. package/lib/commands/app-test.js +25 -19
  88. package/lib/commands/app.js +22 -10
  89. package/lib/commands/auth-config.js +6 -6
  90. package/lib/commands/auth-status.js +4 -3
  91. package/lib/commands/credential-env.js +4 -3
  92. package/lib/commands/credential-list.js +5 -4
  93. package/lib/commands/credential-push.js +4 -3
  94. package/lib/commands/datasource-unified-test-cli.js +495 -0
  95. package/lib/commands/datasource-unified-test-cli.options.js +149 -0
  96. package/lib/commands/datasource-validation-cli.js +129 -0
  97. package/lib/commands/datasource.js +105 -98
  98. package/lib/commands/deployment-list.js +6 -5
  99. package/lib/commands/dev-cli-handlers.js +122 -18
  100. package/lib/commands/dev-down.js +4 -3
  101. package/lib/commands/dev-init.js +231 -116
  102. package/lib/commands/dev-show-display.js +473 -0
  103. package/lib/commands/login-credentials.js +3 -2
  104. package/lib/commands/login-device.js +4 -3
  105. package/lib/commands/login.js +5 -4
  106. package/lib/commands/logout.js +8 -7
  107. package/lib/commands/parameters-validate.js +54 -0
  108. package/lib/commands/repair-datasource.js +314 -68
  109. package/lib/commands/repair-env-template.js +2 -2
  110. package/lib/commands/repair.js +21 -3
  111. package/lib/commands/secrets-list.js +23 -12
  112. package/lib/commands/secrets-remove-all.js +220 -0
  113. package/lib/commands/secrets-remove.js +21 -12
  114. package/lib/commands/secrets-set.js +21 -12
  115. package/lib/commands/secrets-validate.js +4 -4
  116. package/lib/commands/secure.js +10 -9
  117. package/lib/commands/service-user.js +26 -25
  118. package/lib/commands/test-e2e-external.js +27 -1
  119. package/lib/commands/up-common.js +3 -2
  120. package/lib/commands/up-dataplane.js +29 -16
  121. package/lib/commands/up-miso.js +19 -29
  122. package/lib/commands/upload.js +138 -39
  123. package/lib/commands/wizard-core-helpers.js +1 -1
  124. package/lib/commands/wizard-dataplane.js +4 -3
  125. package/lib/commands/wizard-helpers.js +3 -3
  126. package/lib/commands/wizard.js +2 -2
  127. package/lib/core/admin-secrets.js +14 -5
  128. package/lib/core/audit-logger.js +12 -4
  129. package/lib/core/config-attach-extensions.js +46 -0
  130. package/lib/core/config-runtime-paths.js +29 -0
  131. package/lib/core/config.js +55 -56
  132. package/lib/core/diff.js +3 -2
  133. package/lib/core/ensure-encryption-key.js +1 -1
  134. package/lib/core/secrets-ensure-infra.js +77 -0
  135. package/lib/core/secrets-ensure.js +120 -64
  136. package/lib/core/secrets-env-write.js +35 -7
  137. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  138. package/lib/core/secrets.js +200 -37
  139. package/lib/core/templates-env.js +4 -3
  140. package/lib/datasource/abac-validator.js +1 -10
  141. package/lib/datasource/deploy.js +75 -53
  142. package/lib/datasource/field-reference-validator.js +9 -6
  143. package/lib/datasource/integration-context.js +63 -0
  144. package/lib/datasource/list.js +8 -7
  145. package/lib/datasource/log-viewer.js +84 -53
  146. package/lib/datasource/resolve-app.js +4 -4
  147. package/lib/datasource/test-e2e.js +95 -146
  148. package/lib/datasource/test-integration.js +114 -122
  149. package/lib/datasource/unified-validation-run-body.js +65 -0
  150. package/lib/datasource/unified-validation-run-post.js +23 -0
  151. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  152. package/lib/datasource/unified-validation-run.js +92 -0
  153. package/lib/datasource/validate.js +157 -13
  154. package/lib/deployment/deployer.js +4 -3
  155. package/lib/deployment/environment.js +7 -6
  156. package/lib/deployment/push.js +17 -8
  157. package/lib/external-system/delete.js +4 -3
  158. package/lib/external-system/deploy.js +131 -53
  159. package/lib/external-system/download-helpers.js +1 -1
  160. package/lib/external-system/download.js +7 -6
  161. package/lib/external-system/generator.js +92 -6
  162. package/lib/external-system/integration-test-dispatch.js +26 -0
  163. package/lib/external-system/test-execution.js +5 -1
  164. package/lib/external-system/test-helpers.js +0 -4
  165. package/lib/external-system/test-system-level-helpers.js +110 -0
  166. package/lib/external-system/test-system-level.js +83 -44
  167. package/lib/external-system/test.js +59 -8
  168. package/lib/generator/builders.js +23 -11
  169. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  170. package/lib/generator/external.js +16 -4
  171. package/lib/generator/helpers.js +58 -3
  172. package/lib/generator/index.js +4 -0
  173. package/lib/generator/split-readme.js +12 -7
  174. package/lib/generator/split-variables.js +2 -1
  175. package/lib/generator/split.js +1 -1
  176. package/lib/generator/wizard-readme.js +3 -3
  177. package/lib/generator/wizard.js +8 -8
  178. package/lib/infrastructure/compose.js +60 -6
  179. package/lib/infrastructure/helpers.js +201 -29
  180. package/lib/infrastructure/index.js +28 -17
  181. package/lib/infrastructure/services.js +21 -15
  182. package/lib/internal/fs-real-sync.js +104 -0
  183. package/lib/internal/node-fs.js +98 -0
  184. package/lib/parameters/database-secret-values.js +173 -0
  185. package/lib/parameters/infra-kv-discovery.js +121 -0
  186. package/lib/parameters/infra-parameter-catalog.js +458 -0
  187. package/lib/parameters/infra-parameter-validate.js +64 -0
  188. package/lib/schema/application-schema.json +37 -17
  189. package/lib/schema/datasource-test-run.schema.json +493 -0
  190. package/lib/schema/deployment-rules.yaml +102 -63
  191. package/lib/schema/external-datasource.schema.json +1200 -442
  192. package/lib/schema/external-system.schema.json +181 -5
  193. package/lib/schema/flag-map-validation-run.json +31 -0
  194. package/lib/schema/infra-parameter.schema.json +106 -0
  195. package/lib/schema/infra.parameter.yaml +421 -0
  196. package/lib/schema/type/credential-auth-templates.json +40 -0
  197. package/lib/schema/type/document-storage.json +213 -0
  198. package/lib/schema/type/message-service.json +123 -0
  199. package/lib/schema/type/vector-store.json +88 -0
  200. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  201. package/lib/utils/api-error-handler.js +2 -2
  202. package/lib/utils/api.js +49 -14
  203. package/lib/utils/app-register-api.js +3 -2
  204. package/lib/utils/app-register-auth.js +1 -1
  205. package/lib/utils/app-register-config.js +4 -4
  206. package/lib/utils/app-register-display.js +3 -2
  207. package/lib/utils/app-register-validator.js +3 -2
  208. package/lib/utils/app-run-containers.js +26 -22
  209. package/lib/utils/app-scoped-config.js +31 -0
  210. package/lib/utils/app-service-env-from-builder.js +164 -0
  211. package/lib/utils/build-copy.js +1 -1
  212. package/lib/utils/build-helpers.js +20 -20
  213. package/lib/utils/build-resolve-image.js +165 -0
  214. package/lib/utils/cli-layout-chalk.js +8 -0
  215. package/lib/utils/cli-test-layout-chalk.js +267 -0
  216. package/lib/utils/cli-utils.js +88 -11
  217. package/lib/utils/compose-db-passwords.js +138 -0
  218. package/lib/utils/compose-generate-docker-compose.js +216 -0
  219. package/lib/utils/compose-generator.js +197 -291
  220. package/lib/utils/compose-miso-env.js +18 -0
  221. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  222. package/lib/utils/config-paths.js +166 -7
  223. package/lib/utils/config-scoped-resources-preference.js +41 -0
  224. package/lib/utils/controller-deployment-outcome.js +68 -0
  225. package/lib/utils/credential-display.js +2 -2
  226. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  227. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  228. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  229. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  230. package/lib/utils/datasource-test-run-display.js +442 -0
  231. package/lib/utils/datasource-test-run-exit.js +58 -0
  232. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  233. package/lib/utils/datasource-test-run-report-version.js +51 -0
  234. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  235. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  236. package/lib/utils/datasource-validation-watch.js +266 -0
  237. package/lib/utils/declarative-url-ports.js +47 -0
  238. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  239. package/lib/utils/dev-ca-install.js +185 -23
  240. package/lib/utils/dev-cert-helper.js +266 -17
  241. package/lib/utils/dev-hosts-helper.js +307 -0
  242. package/lib/utils/dev-init-cert-hints.js +37 -0
  243. package/lib/utils/dev-init-health-messages.js +52 -0
  244. package/lib/utils/dev-init-resolve.js +86 -0
  245. package/lib/utils/dev-init-ssh-merge.js +65 -0
  246. package/lib/utils/dev-ssh-config-helper.js +196 -0
  247. package/lib/utils/dev-user-groups.js +93 -0
  248. package/lib/utils/docker-build.js +42 -17
  249. package/lib/utils/docker-exec.js +28 -0
  250. package/lib/utils/docker-manifest-public-port.js +116 -0
  251. package/lib/utils/docker-not-running-hint.js +52 -0
  252. package/lib/utils/docker.js +98 -11
  253. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  254. package/lib/utils/env-config-loader.js +10 -91
  255. package/lib/utils/env-copy.js +19 -10
  256. package/lib/utils/env-map.js +35 -8
  257. package/lib/utils/env-template.js +2 -2
  258. package/lib/utils/environment-scoped-resources.js +144 -0
  259. package/lib/utils/error-formatter.js +92 -13
  260. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  261. package/lib/utils/error-formatters/network-errors.js +2 -1
  262. package/lib/utils/error-formatters/permission-errors.js +2 -1
  263. package/lib/utils/error-formatters/validation-errors.js +2 -1
  264. package/lib/utils/external-readme.js +8 -1
  265. package/lib/utils/external-system-display.js +234 -136
  266. package/lib/utils/external-system-local-test-tty.js +389 -0
  267. package/lib/utils/external-system-readiness-core.js +377 -0
  268. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  269. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  270. package/lib/utils/external-system-readiness-display.js +186 -0
  271. package/lib/utils/external-system-test-helpers.js +24 -6
  272. package/lib/utils/external-system-validators.js +30 -12
  273. package/lib/utils/health-check-url.js +119 -0
  274. package/lib/utils/health-check.js +59 -25
  275. package/lib/utils/help-builder.js +11 -8
  276. package/lib/utils/image-version.js +4 -8
  277. package/lib/utils/infra-containers.js +4 -7
  278. package/lib/utils/infra-env-defaults.js +162 -0
  279. package/lib/utils/infra-status-display.js +167 -0
  280. package/lib/utils/infra-status.js +16 -8
  281. package/lib/utils/local-secrets.js +3 -4
  282. package/lib/utils/paths.js +134 -47
  283. package/lib/utils/port-resolver.js +10 -23
  284. package/lib/utils/redis-env-scope.js +62 -0
  285. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  286. package/lib/utils/remote-builder-validation.js +99 -0
  287. package/lib/utils/remote-dev-auth.js +117 -21
  288. package/lib/utils/remote-docker-env.js +67 -15
  289. package/lib/utils/remote-secrets-loader.js +13 -4
  290. package/lib/utils/resolve-docker-image-ref.js +124 -0
  291. package/lib/utils/schema-loader.js +22 -9
  292. package/lib/utils/secrets-bash-kv.js +25 -0
  293. package/lib/utils/secrets-generator.js +169 -49
  294. package/lib/utils/secrets-helpers.js +70 -59
  295. package/lib/utils/secrets-kv-scope.js +60 -0
  296. package/lib/utils/secrets-utils.js +32 -38
  297. package/lib/utils/secrets-validation.js +3 -1
  298. package/lib/utils/secrets-yaml-preserve.js +109 -0
  299. package/lib/utils/ssh-key-helper.js +4 -2
  300. package/lib/utils/template-helpers.js +2 -2
  301. package/lib/utils/test-log-writer.js +3 -3
  302. package/lib/utils/token-manager.js +1 -2
  303. package/lib/utils/url-declarative-public-base.js +188 -0
  304. package/lib/utils/url-declarative-resolve-build.js +493 -0
  305. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  306. package/lib/utils/url-declarative-resolve.js +220 -0
  307. package/lib/utils/url-declarative-token-parse.js +74 -0
  308. package/lib/utils/url-declarative-url-flags.js +50 -0
  309. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  310. package/lib/utils/url-public-path-prefix.js +34 -0
  311. package/lib/utils/urls-local-registry.js +220 -0
  312. package/lib/utils/validation-report-tty-kit.js +77 -0
  313. package/lib/utils/validation-run-poll.js +89 -0
  314. package/lib/utils/validation-run-post-retry.js +73 -0
  315. package/lib/utils/validation-run-request.js +98 -0
  316. package/lib/utils/variable-transformer.js +21 -4
  317. package/lib/utils/yaml-preserve.js +33 -14
  318. package/lib/validation/datasource-warnings.js +56 -0
  319. package/lib/validation/env-template-auth.js +1 -1
  320. package/lib/validation/external-manifest-validator.js +27 -7
  321. package/lib/validation/validate-display.js +37 -31
  322. package/lib/validation/validate.js +4 -13
  323. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  324. package/lib/validation/validator.js +22 -65
  325. package/lib/validation/wizard-config-validator.js +2 -1
  326. package/package.json +7 -3
  327. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  328. package/scripts/diagnose-cli.js +150 -0
  329. package/scripts/install-local.js +304 -55
  330. package/templates/README.md +15 -2
  331. package/templates/applications/dataplane/application.yaml +52 -2
  332. package/templates/applications/dataplane/env.template +75 -17
  333. package/templates/applications/dataplane/rbac.yaml +8 -0
  334. package/templates/applications/keycloak/application.yaml +9 -1
  335. package/templates/applications/keycloak/env.template +15 -6
  336. package/templates/applications/miso-controller/application.yaml +10 -2
  337. package/templates/applications/miso-controller/env.template +42 -12
  338. package/templates/applications/miso-controller/rbac.yaml +5 -0
  339. package/templates/external-system/README.md.hbs +20 -7
  340. package/templates/external-system/deploy.js.hbs +5 -5
  341. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  342. package/templates/infra/compose.yaml.hbs +20 -4
  343. package/templates/python/docker-compose.hbs +16 -0
  344. package/templates/typescript/docker-compose.hbs +16 -0
  345. package/lib/api/external-test.api.js +0 -111
  346. package/lib/schema/env-config.yaml +0 -60
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * @fileoverview Builder Server (dev) API – issue-cert, settings, users, SSH keys, secrets.
3
3
  * First call: issue-cert is public (no client cert). All other routes require client certificate:
4
- * when clientKeyPem is provided, requests use mTLS (TLS client cert); otherwise X-Client-Cert header only.
4
+ * when clientKeyPem is provided and the URL is https, requests use mTLS (TLS client cert).
5
+ * For http:// URLs, mTLS is impossible; the same routes use X-Client-Cert only (same as when no key is passed).
5
6
  * @author AI Fabrix Team
6
7
  * @version 2.0.0
7
8
  */
8
9
 
9
- const https = require('https');
10
10
  const { makeApiCall } = require('../utils/api');
11
-
12
- const DEFAULT_TIMEOUT_MS = 15000;
11
+ const { httpsJsonRequest, DEFAULT_TIMEOUT_MS, shouldUseMtlsForUrl } = require('./dev-server-https');
12
+ const { requestWithClientCert } = require('./dev-mtls-request');
13
13
 
14
14
  /**
15
15
  * Encode PEM for use in X-Client-Cert header. HTTP header values must not contain newlines;
@@ -46,20 +46,48 @@ function buildUrl(baseUrl, path) {
46
46
  return `${baseUrl}${p}`;
47
47
  }
48
48
 
49
+ function isHttpOrHttpsUrl(value) {
50
+ return typeof value === 'string' && (value.startsWith('http://') || value.startsWith('https://'));
51
+ }
52
+
49
53
  /**
50
- * Make request to Builder Server. Throws on !success with message from response or error.
54
+ * Full URL for shared-secrets collection (GET list / POST add). DELETE uses this base + /:key.
55
+ * When secretsEndpointUrl is set (config aifabrix-secrets), uses it; if it is only a host base (path /), appends /api/dev/secrets.
56
+ * @param {string} serverUrl - Builder Server base URL
57
+ * @param {string} [secretsEndpointUrl] - Optional secrets API URL from config
58
+ * @returns {string}
59
+ */
60
+ function resolveSecretsEndpoint(serverUrl, secretsEndpointUrl) {
61
+ if (secretsEndpointUrl && isHttpOrHttpsUrl(secretsEndpointUrl)) {
62
+ const normalized = normalizeBaseUrl(secretsEndpointUrl);
63
+ let u;
64
+ try {
65
+ u = new URL(normalized);
66
+ } catch {
67
+ return buildUrl(normalizeBaseUrl(serverUrl), '/api/dev/secrets');
68
+ }
69
+ if (u.pathname === '/' || u.pathname === '') {
70
+ return buildUrl(normalized, '/api/dev/secrets');
71
+ }
72
+ return normalized;
73
+ }
74
+ return buildUrl(normalizeBaseUrl(serverUrl), '/api/dev/secrets');
75
+ }
76
+
77
+ /**
78
+ * Builder Server request via global fetch (public CAs only).
51
79
  * @param {string} url - Full URL
52
- * @param {Object} options - Fetch options (method, headers, body)
53
- * @returns {Promise<Object>} result.data when success
80
+ * @param {Object} rest - method, headers, body
81
+ * @returns {Promise<Object>}
54
82
  */
55
- async function request(url, options = {}) {
83
+ async function requestViaFetchApi(url, rest) {
56
84
  const fetchOptions = {
57
- method: options.method || 'GET',
58
- headers: { 'Content-Type': 'application/json', ...options.headers },
85
+ method: rest.method || 'GET',
86
+ headers: { 'Content-Type': 'application/json', ...rest.headers },
59
87
  signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS)
60
88
  };
61
- if (options.body !== undefined) {
62
- fetchOptions.body = typeof options.body === 'string' ? options.body : JSON.stringify(options.body);
89
+ if (rest.body !== undefined) {
90
+ fetchOptions.body = typeof rest.body === 'string' ? rest.body : JSON.stringify(rest.body);
63
91
  }
64
92
  const result = await makeApiCall(url, fetchOptions);
65
93
  if (!result.success) {
@@ -67,103 +95,36 @@ async function request(url, options = {}) {
67
95
  const err = new Error(msg);
68
96
  err.status = result.status;
69
97
  err.errorData = result.errorData;
98
+ if (result.originalError) {
99
+ err.cause = result.originalError;
100
+ }
70
101
  throw err;
71
102
  }
72
103
  return result.data;
73
104
  }
74
105
 
75
106
  /**
76
- * Build request options and TLS agent for mTLS request.
107
+ * Make request to Builder Server. Throws on !success with message from response or error.
108
+ * When options.serverCaPem is set, uses Node https with merged CA (Node ignores Windows user ROOT for fetch).
77
109
  * @param {string} url - Full URL
78
- * @param {Object} options - { method, headers, body }
79
- * @param {string} certPem - PEM client certificate
80
- * @param {string} keyPem - PEM client key
81
- * @returns {{ urlObj: URL, method: string, headers: Object, body: string|undefined, agent: https.Agent }}
82
- */
83
- function buildMtlsRequestOptions(url, options, certPem, keyPem) {
84
- const urlObj = new URL(url);
85
- const method = (options.method || 'GET').toUpperCase();
86
- const headers = { 'Content-Type': 'application/json', ...options.headers };
87
- let body = options.body;
88
- if (body !== undefined && typeof body !== 'string') {
89
- body = JSON.stringify(body);
90
- }
91
- if (body) {
92
- headers['Content-Length'] = Buffer.byteLength(body, 'utf8');
93
- }
94
- const tlsOptions = { cert: certPem, key: keyPem, rejectUnauthorized: true };
95
- const agent = new https.Agent(tlsOptions);
96
- return { urlObj, method, headers, body, agent, tlsOptions };
97
- }
98
-
99
- /**
100
- * Handle mTLS response: collect body, parse JSON, resolve or reject.
101
- * @param {import('http').IncomingMessage} res - HTTP response
102
- * @param {Function} resolve - Promise resolve
103
- * @param {Function} reject - Promise reject
110
+ * @param {Object} options - Fetch options (method, headers, body, serverCaPem)
111
+ * @returns {Promise<Object>} result.data when success
104
112
  */
105
- function handleMtlsResponse(res, resolve, reject) {
106
- const chunks = [];
107
- res.on('data', (c) => chunks.push(c));
108
- res.on('end', () => {
109
- const raw = Buffer.concat(chunks).toString('utf8');
110
- let data;
113
+ async function request(url, options = {}) {
114
+ const { serverCaPem, ...rest } = options;
115
+ const pem = serverCaPem && typeof serverCaPem === 'string' && serverCaPem.trim() ? serverCaPem.trim() : null;
116
+ let useMergedCaHttps = false;
117
+ if (pem) {
111
118
  try {
112
- data = raw ? JSON.parse(raw) : {};
119
+ useMergedCaHttps = new URL(url).protocol === 'https:';
113
120
  } catch {
114
- data = raw;
115
- }
116
- if (res.statusCode < 200 || res.statusCode >= 300) {
117
- const msg = (data && (data.message || data.error)) || res.statusMessage || `Request failed (${res.statusCode})`;
118
- const err = new Error(msg);
119
- err.status = res.statusCode;
120
- err.errorData = data;
121
- reject(err);
122
- } else {
123
- resolve(data);
121
+ useMergedCaHttps = false;
124
122
  }
125
- });
126
- }
127
-
128
- /**
129
- * Make request with mTLS (TLS client certificate). Uses Node https module so the client cert
130
- * is presented in the TLS handshake. Also sends X-Client-Cert header for backends that read it.
131
- * @param {string} url - Full URL (https only)
132
- * @param {Object} options - { method, headers, body }
133
- * @param {string} certPem - PEM-encoded client certificate
134
- * @param {string} keyPem - PEM-encoded client private key
135
- * @returns {Promise<Object>} response data when success
136
- */
137
- function requestWithCertImpl(url, options, certPem, keyPem) {
138
- return new Promise((resolve, reject) => {
139
- const urlObj = new URL(url);
140
- if (urlObj.protocol !== 'https:') {
141
- reject(new Error('mTLS request requires https URL'));
142
- return;
143
- }
144
- const { method, headers, body, agent, tlsOptions } = buildMtlsRequestOptions(url, options, certPem, keyPem);
145
- const req = https.request(
146
- {
147
- hostname: urlObj.hostname,
148
- port: urlObj.port || 443,
149
- path: urlObj.pathname + urlObj.search,
150
- method,
151
- headers,
152
- agent,
153
- ...tlsOptions
154
- },
155
- (res) => handleMtlsResponse(res, resolve, reject)
156
- );
157
- req.on('error', reject);
158
- req.setTimeout(DEFAULT_TIMEOUT_MS, () => {
159
- req.destroy();
160
- reject(new Error(`Request timed out after ${DEFAULT_TIMEOUT_MS}ms`));
161
- });
162
- if (body) {
163
- req.write(body, 'utf8');
164
- }
165
- req.end();
166
- });
123
+ }
124
+ if (pem && useMergedCaHttps) {
125
+ return httpsJsonRequest(url, rest, pem);
126
+ }
127
+ return requestViaFetchApi(url, rest);
167
128
  }
168
129
 
169
130
  /**
@@ -171,44 +132,47 @@ function requestWithCertImpl(url, options, certPem, keyPem) {
171
132
  * @requiresPermission {BuilderServer} Public (no auth required)
172
133
  * @param {string} serverUrl - Builder Server base URL
173
134
  * @param {Object} body - IssueCertDto: developerId, pin, csr
135
+ * @param {string} [serverCaPem] - Dev root CA PEM when Node must trust a private CA (e.g. after install-ca)
174
136
  * @returns {Promise<Object>} IssueCertResponseDto: certificate, validDays, validNotAfter
175
137
  */
176
- async function issueCert(serverUrl, body) {
138
+ async function issueCert(serverUrl, body, serverCaPem) {
177
139
  const base = normalizeBaseUrl(serverUrl);
178
- return request(buildUrl(base, '/api/dev/issue-cert'), { method: 'POST', body });
140
+ return request(buildUrl(base, '/api/dev/issue-cert'), { method: 'POST', body, serverCaPem });
179
141
  }
180
142
 
181
143
  /**
182
144
  * Get health. GET /health (public)
183
145
  * @requiresPermission {BuilderServer} Public (no auth required)
184
146
  * @param {string} serverUrl - Builder Server base URL
147
+ * @param {string} [serverCaPem] - Dev root CA PEM when verifying a private CA
185
148
  * @returns {Promise<Object>} HealthResponseDto: status, checks (dataDir, encryptionKey, ca, users, tokens)
186
149
  */
187
- async function getHealth(serverUrl) {
150
+ async function getHealth(serverUrl, serverCaPem) {
188
151
  const base = normalizeBaseUrl(serverUrl);
189
- return request(buildUrl(base, '/health'));
152
+ return request(buildUrl(base, '/health'), { method: 'GET', serverCaPem });
190
153
  }
191
154
 
192
155
  /**
193
156
  * Get developer settings (cert-authenticated). GET /api/dev/settings
194
- * When clientKeyPem is provided, uses mTLS (TLS client cert); otherwise X-Client-Cert header only.
157
+ * When clientKeyPem is provided and server URL is https, uses mTLS; otherwise X-Client-Cert header only.
195
158
  * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert or mTLS)
196
159
  * @param {string} serverUrl - Builder Server base URL
197
160
  * @param {string} clientCertPem - PEM-encoded client certificate
198
161
  * @param {string} [clientKeyPem] - PEM-encoded client private key (enables mTLS when provided)
162
+ * @param {string} [serverCaPem] - Dev root CA PEM for server certificate verification
199
163
  * @returns {Promise<Object>} SettingsResponseDto
200
164
  */
201
- async function getSettings(serverUrl, clientCertPem, clientKeyPem) {
165
+ async function getSettings(serverUrl, clientCertPem, clientKeyPem, serverCaPem) {
202
166
  if (!clientCertPem || typeof clientCertPem !== 'string') {
203
167
  throw new Error('Client certificate PEM is required for getSettings');
204
168
  }
205
169
  const base = normalizeBaseUrl(serverUrl);
206
170
  const url = buildUrl(base, '/api/dev/settings');
207
171
  const reqOptions = { method: 'GET', headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) } };
208
- if (clientKeyPem && typeof clientKeyPem === 'string') {
209
- return requestWithCertImpl(url, reqOptions, clientCertPem, clientKeyPem);
172
+ if (shouldUseMtlsForUrl(url, clientKeyPem)) {
173
+ return requestWithClientCert(url, reqOptions, clientCertPem, clientKeyPem, serverCaPem);
210
174
  }
211
- return request(url, reqOptions);
175
+ return request(url, { ...reqOptions, serverCaPem });
212
176
  }
213
177
 
214
178
  /**
@@ -216,13 +180,15 @@ async function getSettings(serverUrl, clientCertPem, clientKeyPem) {
216
180
  * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
217
181
  * @param {string} serverUrl - Builder Server base URL
218
182
  * @param {string} clientCertPem - PEM client certificate
183
+ * @param {string} [serverCaPem] - Dev root CA PEM
219
184
  * @returns {Promise<Object[]>} Array of UserResponseDto (empty when none)
220
185
  */
221
- async function listUsers(serverUrl, clientCertPem) {
186
+ async function listUsers(serverUrl, clientCertPem, serverCaPem) {
222
187
  const base = normalizeBaseUrl(serverUrl);
223
188
  const data = await request(buildUrl(base, '/api/dev/users'), {
224
189
  method: 'GET',
225
- headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
190
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
191
+ serverCaPem
226
192
  });
227
193
  return Array.isArray(data) ? data : [];
228
194
  }
@@ -232,15 +198,17 @@ async function listUsers(serverUrl, clientCertPem) {
232
198
  * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
233
199
  * @param {string} serverUrl - Builder Server base URL
234
200
  * @param {string} clientCertPem - PEM client certificate
235
- * @param {Object} body - CreateUserDto: developerId, name, email, optional groups
201
+ * @param {Object} body - CreateUserDto: developerId, name, email, optional groups (admin | secret-manager | developer | docker)
202
+ * @param {string} [serverCaPem] - Dev root CA PEM
236
203
  * @returns {Promise<Object>} UserResponseDto
237
204
  */
238
- async function createUser(serverUrl, clientCertPem, body) {
205
+ async function createUser(serverUrl, clientCertPem, body, serverCaPem) {
239
206
  const base = normalizeBaseUrl(serverUrl);
240
207
  return request(buildUrl(base, '/api/dev/users'), {
241
208
  method: 'POST',
242
209
  headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
243
- body
210
+ body,
211
+ serverCaPem
244
212
  });
245
213
  }
246
214
 
@@ -250,15 +218,17 @@ async function createUser(serverUrl, clientCertPem, body) {
250
218
  * @param {string} serverUrl - Builder Server base URL
251
219
  * @param {string} clientCertPem - PEM client certificate
252
220
  * @param {string} id - Developer ID
253
- * @param {Object} body - UpdateUserDto: at least one of name, email, groups
221
+ * @param {Object} body - UpdateUserDto: at least one of name, email, groups (same group tokens as create)
222
+ * @param {string} [serverCaPem] - Dev root CA PEM
254
223
  * @returns {Promise<Object>} UserResponseDto
255
224
  */
256
- async function updateUser(serverUrl, clientCertPem, id, body) {
225
+ async function updateUser(serverUrl, clientCertPem, id, body, serverCaPem) {
257
226
  const base = normalizeBaseUrl(serverUrl);
258
227
  return request(buildUrl(base, `/api/dev/users/${encodeURIComponent(id)}`), {
259
228
  method: 'PATCH',
260
229
  headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
261
- body
230
+ body,
231
+ serverCaPem
262
232
  });
263
233
  }
264
234
 
@@ -268,13 +238,15 @@ async function updateUser(serverUrl, clientCertPem, id, body) {
268
238
  * @param {string} serverUrl - Builder Server base URL
269
239
  * @param {string} clientCertPem - PEM client certificate
270
240
  * @param {string} id - Developer ID
241
+ * @param {string} [serverCaPem] - Dev root CA PEM
271
242
  * @returns {Promise<Object>} DeletedResponseDto
272
243
  */
273
- async function deleteUser(serverUrl, clientCertPem, id) {
244
+ async function deleteUser(serverUrl, clientCertPem, id, serverCaPem) {
274
245
  const base = normalizeBaseUrl(serverUrl);
275
246
  return request(buildUrl(base, `/api/dev/users/${encodeURIComponent(id)}`), {
276
247
  method: 'DELETE',
277
- headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
248
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
249
+ serverCaPem
278
250
  });
279
251
  }
280
252
 
@@ -284,13 +256,15 @@ async function deleteUser(serverUrl, clientCertPem, id) {
284
256
  * @param {string} serverUrl - Builder Server base URL
285
257
  * @param {string} clientCertPem - PEM client certificate
286
258
  * @param {string} id - Developer ID
259
+ * @param {string} [serverCaPem] - Dev root CA PEM
287
260
  * @returns {Promise<Object>} CreatePinResponseDto: pin, expiresAt
288
261
  */
289
- async function createPin(serverUrl, clientCertPem, id) {
262
+ async function createPin(serverUrl, clientCertPem, id, serverCaPem) {
290
263
  const base = normalizeBaseUrl(serverUrl);
291
264
  return request(buildUrl(base, `/api/dev/users/${encodeURIComponent(id)}/pin`), {
292
265
  method: 'POST',
293
- headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
266
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
267
+ serverCaPem
294
268
  });
295
269
  }
296
270
 
@@ -300,29 +274,32 @@ async function createPin(serverUrl, clientCertPem, id) {
300
274
  * @param {string} serverUrl - Builder Server base URL
301
275
  * @param {string} clientCertPem - PEM client certificate
302
276
  * @param {string} id - Developer ID
277
+ * @param {string} [serverCaPem] - Dev root CA PEM
303
278
  * @returns {Promise<Object[]>} Array of SshKeyItemDto
304
279
  */
305
- async function listSshKeys(serverUrl, clientCertPem, id) {
280
+ async function listSshKeys(serverUrl, clientCertPem, id, serverCaPem) {
306
281
  const base = normalizeBaseUrl(serverUrl);
307
282
  const data = await request(buildUrl(base, `/api/dev/users/${encodeURIComponent(id)}/ssh-keys`), {
308
283
  method: 'GET',
309
- headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
284
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
285
+ serverCaPem
310
286
  });
311
287
  return Array.isArray(data) ? data : [];
312
288
  }
313
289
 
314
290
  /**
315
291
  * Add SSH public key for developer. POST /api/dev/users/:id/ssh-keys
316
- * When clientKeyPem is provided, uses mTLS (TLS client cert); otherwise X-Client-Cert header only.
292
+ * When clientKeyPem is provided and server URL is https, uses mTLS; otherwise X-Client-Cert header only.
317
293
  * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert or mTLS)
318
294
  * @param {string} serverUrl - Builder Server base URL
319
295
  * @param {string} clientCertPem - PEM client certificate
320
296
  * @param {string} id - Developer ID
321
297
  * @param {Object} body - AddSshKeyDto: publicKey, optional label
322
298
  * @param {string} [clientKeyPem] - PEM-encoded client private key (enables mTLS when provided)
299
+ * @param {string} [serverCaPem] - Dev root CA PEM
323
300
  * @returns {Promise<Object>} SshKeyItemDto
324
301
  */
325
- async function addSshKey(serverUrl, clientCertPem, id, body, clientKeyPem) {
302
+ async function addSshKey(serverUrl, clientCertPem, id, body, clientKeyPem, serverCaPem) {
326
303
  const base = normalizeBaseUrl(serverUrl);
327
304
  const url = buildUrl(base, `/api/dev/users/${encodeURIComponent(id)}/ssh-keys`);
328
305
  const reqOptions = {
@@ -330,10 +307,10 @@ async function addSshKey(serverUrl, clientCertPem, id, body, clientKeyPem) {
330
307
  headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
331
308
  body
332
309
  };
333
- if (clientKeyPem && typeof clientKeyPem === 'string') {
334
- return requestWithCertImpl(url, reqOptions, clientCertPem, clientKeyPem);
310
+ if (shouldUseMtlsForUrl(url, clientKeyPem)) {
311
+ return requestWithClientCert(url, reqOptions, clientCertPem, clientKeyPem, serverCaPem);
335
312
  }
336
- return request(url, reqOptions);
313
+ return request(url, { ...reqOptions, serverCaPem });
337
314
  }
338
315
 
339
316
  /**
@@ -343,62 +320,73 @@ async function addSshKey(serverUrl, clientCertPem, id, body, clientKeyPem) {
343
320
  * @param {string} clientCertPem - PEM client certificate
344
321
  * @param {string} id - Developer ID
345
322
  * @param {string} fingerprint - Key fingerprint
323
+ * @param {string} [serverCaPem] - Dev root CA PEM
346
324
  * @returns {Promise<Object>} DeletedResponseDto
347
325
  */
348
- async function removeSshKey(serverUrl, clientCertPem, id, fingerprint) {
326
+ async function removeSshKey(serverUrl, clientCertPem, id, fingerprint, serverCaPem) {
349
327
  const base = normalizeBaseUrl(serverUrl);
350
328
  return request(buildUrl(base, `/api/dev/users/${encodeURIComponent(id)}/ssh-keys/${encodeURIComponent(fingerprint)}`), {
351
329
  method: 'DELETE',
352
- headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
330
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
331
+ serverCaPem
353
332
  });
354
333
  }
355
334
 
356
335
  /**
357
- * List secrets. GET /api/dev/secrets
336
+ * List secrets. GET shared-secrets endpoint (default {serverUrl}/api/dev/secrets, or secretsEndpointUrl from config).
358
337
  * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
359
338
  * @param {string} serverUrl - Builder Server base URL
360
339
  * @param {string} clientCertPem - PEM client certificate
340
+ * @param {string} [serverCaPem] - Dev root CA PEM
341
+ * @param {string} [secretsEndpointUrl] - Optional full or base secrets URL (aifabrix-secrets when https)
361
342
  * @returns {Promise<Object[]>} Array of SecretItemDto: name, value
362
343
  */
363
- async function listSecrets(serverUrl, clientCertPem) {
364
- const base = normalizeBaseUrl(serverUrl);
365
- const data = await request(buildUrl(base, '/api/dev/secrets'), {
344
+ async function listSecrets(serverUrl, clientCertPem, serverCaPem, secretsEndpointUrl) {
345
+ const endpoint = resolveSecretsEndpoint(serverUrl, secretsEndpointUrl);
346
+ const data = await request(endpoint, {
366
347
  method: 'GET',
367
- headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
348
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
349
+ serverCaPem
368
350
  });
369
351
  return Array.isArray(data) ? data : [];
370
352
  }
371
353
 
372
354
  /**
373
- * Add or update secret. POST /api/dev/secrets
355
+ * Add or update secret. POST shared-secrets endpoint.
374
356
  * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
375
357
  * @param {string} serverUrl - Builder Server base URL
376
358
  * @param {string} clientCertPem - PEM client certificate
377
359
  * @param {Object} body - AddSecretDto: key, value
360
+ * @param {string} [serverCaPem] - Dev root CA PEM
361
+ * @param {string} [secretsEndpointUrl] - Optional secrets URL from config (aifabrix-secrets when https)
378
362
  * @returns {Promise<Object>} AddSecretResponseDto
379
363
  */
380
- async function addSecret(serverUrl, clientCertPem, body) {
381
- const base = normalizeBaseUrl(serverUrl);
382
- return request(buildUrl(base, '/api/dev/secrets'), {
364
+ async function addSecret(serverUrl, clientCertPem, body, serverCaPem, secretsEndpointUrl) {
365
+ const endpoint = resolveSecretsEndpoint(serverUrl, secretsEndpointUrl);
366
+ return request(endpoint, {
383
367
  method: 'POST',
384
368
  headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
385
- body
369
+ body,
370
+ serverCaPem
386
371
  });
387
372
  }
388
373
 
389
374
  /**
390
- * Delete secret by key. DELETE /api/dev/secrets/:key
375
+ * Delete secret by key. DELETE {endpoint}/:key
391
376
  * @requiresPermission {BuilderServer} Client certificate (X-Client-Cert)
392
377
  * @param {string} serverUrl - Builder Server base URL
393
378
  * @param {string} clientCertPem - PEM client certificate
394
379
  * @param {string} key - Secret key
380
+ * @param {string} [serverCaPem] - Dev root CA PEM
381
+ * @param {string} [secretsEndpointUrl] - Optional secrets URL from config (aifabrix-secrets when https)
395
382
  * @returns {Promise<Object>} DeleteSecretResponseDto
396
383
  */
397
- async function deleteSecret(serverUrl, clientCertPem, key) {
398
- const base = normalizeBaseUrl(serverUrl);
399
- return request(buildUrl(base, `/api/dev/secrets/${encodeURIComponent(key)}`), {
384
+ async function deleteSecret(serverUrl, clientCertPem, key, serverCaPem, secretsEndpointUrl) {
385
+ const endpoint = resolveSecretsEndpoint(serverUrl, secretsEndpointUrl);
386
+ return request(buildUrl(endpoint, `/${encodeURIComponent(key)}`), {
400
387
  method: 'DELETE',
401
- headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) }
388
+ headers: { 'X-Client-Cert': encodeCertForHeader(clientCertPem) },
389
+ serverCaPem
402
390
  });
403
391
  }
404
392
 
@@ -419,5 +407,6 @@ module.exports = {
419
407
  deleteSecret,
420
408
  normalizeBaseUrl,
421
409
  buildUrl,
422
- encodeCertForHeader
410
+ encodeCertForHeader,
411
+ resolveSecretsEndpoint
423
412
  };
package/lib/api/index.js CHANGED
@@ -65,7 +65,6 @@ class ApiClient {
65
65
  headers['Authorization'] = `Bearer ${this.authConfig.token}`;
66
66
  }
67
67
  }
68
-
69
68
  return headers;
70
69
  }
71
70
 
@@ -5,6 +5,33 @@
5
5
  */
6
6
 
7
7
  const { ApiClient } = require('./index');
8
+ const { postValidationRunAndOptionalPoll } = require('./validation-runner');
9
+
10
+ /**
11
+ * @param {Object} opts
12
+ * @param {'externalSystem'|'externalDataSource'} opts.validationScope
13
+ * @param {string} opts.systemKey
14
+ * @param {string} [opts.datasourceKey]
15
+ * @param {Object} [opts.testData]
16
+ * @returns {Object}
17
+ */
18
+ function buildValidationRunPayloadTestBody({ validationScope, systemKey, datasourceKey, testData = {} }) {
19
+ const body = {
20
+ validationScope,
21
+ runType: 'test',
22
+ systemIdOrKey: systemKey
23
+ };
24
+ if (testData.payloadTemplate !== undefined && testData.payloadTemplate !== null) {
25
+ body.payloadTemplate = testData.payloadTemplate;
26
+ }
27
+ if (testData.includeDebug === true) {
28
+ body.includeDebug = true;
29
+ }
30
+ if (validationScope === 'externalDataSource') {
31
+ body.datasourceKey = datasourceKey;
32
+ }
33
+ return body;
34
+ }
8
35
 
9
36
  /**
10
37
  * Validate deployment configuration
@@ -127,8 +154,10 @@ async function validatePipelineConfig(dataplaneUrl, authConfig, { config }) {
127
154
  }
128
155
 
129
156
  /**
130
- * Test external system (all datasources) via dataplane pipeline endpoint
131
- * POST /api/v1/pipeline/{systemKey}/test
157
+ * Test external system (all datasources) via unified validation API
158
+ * POST /api/v1/validation/run (validationScope=externalSystem, runType=test).
159
+ * Omits **payloadTemplate** unless `testData.payloadTemplate` is set so the dataplane runs the
160
+ * validation-engine path (not payload-template-only). Pass `payloadTemplate` explicitly for template tests.
132
161
  * @requiresPermission {Dataplane} external-system:publish. Auth: Bearer or x-client-token only.
133
162
  * @async
134
163
  * @function testSystemViaPipeline
@@ -144,17 +173,27 @@ async function validatePipelineConfig(dataplaneUrl, authConfig, { config }) {
144
173
  * @throws {Error} If test fails
145
174
  */
146
175
  async function testSystemViaPipeline(dataplaneUrl, systemKey, authConfig, testData = {}, options = {}) {
147
- const client = new ApiClient(dataplaneUrl, authConfig);
148
- const requestOptions = { body: testData };
149
- if (options.timeout) {
150
- requestOptions.timeout = options.timeout;
151
- }
152
- return await client.post(`/api/v1/pipeline/${systemKey}/test`, requestOptions);
176
+ const body = buildValidationRunPayloadTestBody({
177
+ validationScope: 'externalSystem',
178
+ systemKey,
179
+ testData
180
+ });
181
+ const timeoutMs = Number.isFinite(options.timeout) ? options.timeout : 30000;
182
+ const res = await postValidationRunAndOptionalPoll({
183
+ dataplaneUrl,
184
+ authConfig,
185
+ body,
186
+ timeoutMs,
187
+ useAsync: true,
188
+ noAsync: false
189
+ });
190
+ if (res.apiError) return res.apiError;
191
+ return { success: true, data: res.envelope, status: 200 };
153
192
  }
154
193
 
155
194
  /**
156
- * Test datasource via dataplane pipeline endpoint
157
- * POST /api/v1/pipeline/{systemKey}/{datasourceKey}/test (Dataplane OpenAPI operationId: testExternalDataSourceViaPipeline)
195
+ * Test datasource via unified validation API
196
+ * POST /api/v1/validation/run (validationScope=externalDataSource, datasourceKeys, payloadTemplate)
158
197
  * Supports client credentials for CI/CD.
159
198
  * @requiresPermission {Dataplane} external-system:publish or external-data-source:read. Auth: Bearer or x-client-token only.
160
199
  * @async
@@ -172,15 +211,23 @@ async function testSystemViaPipeline(dataplaneUrl, systemKey, authConfig, testDa
172
211
  * @throws {Error} If test fails
173
212
  */
174
213
  async function testDatasourceViaPipeline({ dataplaneUrl, systemKey, datasourceKey, authConfig, testData, options = {} }) {
175
- const client = new ApiClient(dataplaneUrl, authConfig);
176
- const requestOptions = {
177
- body: testData
178
- };
179
- // Pass through timeout if provided (will be handled by underlying fetch implementation)
180
- if (options.timeout) {
181
- requestOptions.timeout = options.timeout;
182
- }
183
- return await client.post(`/api/v1/pipeline/${systemKey}/${datasourceKey}/test`, requestOptions);
214
+ const body = buildValidationRunPayloadTestBody({
215
+ validationScope: 'externalDataSource',
216
+ systemKey,
217
+ datasourceKey,
218
+ testData
219
+ });
220
+ const timeoutMs = Number.isFinite(options.timeout) ? options.timeout : 30000;
221
+ const res = await postValidationRunAndOptionalPoll({
222
+ dataplaneUrl,
223
+ authConfig,
224
+ body,
225
+ timeoutMs,
226
+ useAsync: true,
227
+ noAsync: false
228
+ });
229
+ if (res.apiError) return res.apiError;
230
+ return { success: true, data: res.envelope, status: 200 };
184
231
  }
185
232
 
186
233
  /**
@@ -195,7 +242,7 @@ async function testDatasourceViaPipeline({ dataplaneUrl, systemKey, datasourceKe
195
242
  * @param {Object} authConfig - Authentication configuration (must include token for Bearer; client id/secret rejected)
196
243
  * @param {Object} payload - { version, application, dataSources }; optional status (default "draft")
197
244
  * @param {string} [payload.status="draft"] - "draft" or "published"; Builder uses "draft"
198
- * @returns {Promise<Object>} Publication result (system, datasources, warnings); no uploadId
245
+ * @returns {Promise<Object>} API envelope `{ success, data }` where `data` is PublicationResult (uploadId, system, datasources, generateMcpContract, …)
199
246
  * @throws {Error} If upload fails
200
247
  */
201
248
  async function uploadApplicationViaPipeline(dataplaneUrl, authConfig, payload) {
@@ -27,10 +27,11 @@
27
27
  * @typedef {Object} SettingsResponseDto
28
28
  * @property {string} user-mutagen-folder - Server path to workspace root (no app segment)
29
29
  * @property {string} secrets-encryption - Encryption key (hex)
30
- * @property {string} aifabrix-secrets - Path or URL for secrets
30
+ * @property {string} aifabrix-secrets - Local filesystem path or https URL for shared secrets (file vs remote API)
31
31
  * @property {string} aifabrix-env-config - Env config path
32
32
  * @property {string} remote-server - Builder-server base URL
33
33
  * @property {string} docker-endpoint - Docker API endpoint
34
+ * @property {boolean} [docker-tls-skip-verify] - When true and ca.pem is absent, Docker uses DOCKER_TLS_VERIFY=0; if ca.pem exists, daemon is always verified
34
35
  * @property {string} sync-ssh-user - SSH user for Mutagen
35
36
  * @property {string} sync-ssh-host - SSH host for Mutagen
36
37
  */
@@ -44,7 +45,7 @@
44
45
  * @property {string} createdAt - ISO 8601
45
46
  * @property {boolean} certificateIssued - Whether cert was issued
46
47
  * @property {string} [certificateValidNotAfter] - Cert validity end (optional)
47
- * @property {string[]} groups - Access groups (admin, secret-manager, developer)
48
+ * @property {string[]} groups - Access groups (admin, secret-manager, developer, docker)
48
49
  */
49
50
 
50
51
  /**
@@ -53,7 +54,7 @@
53
54
  * @property {string} developerId - Unique developer ID (numeric string)
54
55
  * @property {string} name - Display name
55
56
  * @property {string} email - Email
56
- * @property {string[]} [groups] - Default [developer]
57
+ * @property {string[]} [groups] - Default [developer]; tokens: admin, secret-manager, developer, docker
57
58
  */
58
59
 
59
60
  /**