@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
@@ -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;
@@ -11,33 +11,85 @@ const config = require('../core/config');
11
11
  const { getCertDir } = require('./dev-cert-helper');
12
12
  const { getConfigDirForPaths } = require('./paths');
13
13
 
14
+ function devTlsCertPaths(certDir) {
15
+ return {
16
+ certPath: path.join(certDir, 'cert.pem'),
17
+ keyPath: path.join(certDir, 'key.pem'),
18
+ caPath: path.join(certDir, 'ca.pem')
19
+ };
20
+ }
21
+
22
+ function missingClientTlsError(trimmed, certDir) {
23
+ return new Error(
24
+ `docker-endpoint is set (${trimmed}) but client TLS material is missing in ${certDir}. ` +
25
+ 'Place cert.pem and key.pem there (from Builder Server issue-cert or `AIFABRIX_DEV_ISSUE_PIN`), ' +
26
+ 'or enable TLS skip-verify (`docker-tls-skip-verify: true` or `AIFABRIX_DOCKER_TLS_SKIP_VERIFY=1`) ' +
27
+ 'if the daemon does not require client certificates. With skip-verify and no ca.pem, DOCKER_TLS_VERIFY=0. ' +
28
+ 'Clear docker-endpoint only if you intend to use the local Docker daemon.'
29
+ );
30
+ }
31
+
32
+ function missingCaError(trimmed, certDir) {
33
+ return new Error(
34
+ `docker-endpoint is set (${trimmed}) but ca.pem is missing in ${certDir} and docker-tls-skip-verify is not enabled. ` +
35
+ 'Add ca.pem (daemon/CA PEM), or for a self-signed Docker API set docker-tls-skip-verify: true in ~/.aifabrix/config.yaml ' +
36
+ '(or AIFABRIX_DOCKER_TLS_SKIP_VERIFY=1). Skip-verify uses TLS but does not verify the daemon certificate — use only on trusted networks.'
37
+ );
38
+ }
39
+
14
40
  /**
15
- * If remote Docker is configured (docker-endpoint + cert.pem, key.pem, and ca.pem present),
16
- * returns env vars for Docker CLI: DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH.
17
- * Docker requires ca.pem in DOCKER_CERT_PATH for TLS; if it is missing we return {} so
18
- * the CLI uses local Docker and avoids "open ca.pem: no such file or directory".
41
+ * If remote Docker is configured (docker-endpoint set), returns env vars for Docker CLI:
42
+ * DOCKER_HOST, DOCKER_TLS_VERIFY, and optionally DOCKER_CERT_PATH when client certs exist.
43
+ * When docker-endpoint is set, we do not fall back to the local daemon without that endpoint
44
+ * (avoids accidentally using Docker Desktop while the dev profile targets a remote engine).
45
+ *
46
+ * When **TLS skip-verify** is enabled (config or env) and **ca.pem is missing**, client cert/key are
47
+ * optional: Docker can use DOCKER_TLS_VERIFY=0 with no client certs if the daemon allows it.
48
+ * If **ca.pem is present** (e.g. from Builder Server issue-cert), the daemon certificate is always
49
+ * verified (DOCKER_TLS_VERIFY=1) even when skip-verify is set — better security once a trust anchor exists.
19
50
  *
20
- * @returns {Promise<Object>} Env overlay (may be empty)
51
+ * Without skip-verify, cert.pem, key.pem, and ca.pem are required in the dev cert directory.
52
+ *
53
+ * @returns {Promise<Object>} Env overlay (empty when docker-endpoint is not set)
54
+ * @throws {Error} When docker-endpoint is set but required TLS material is missing
21
55
  */
22
56
  async function getRemoteDockerEnv() {
23
57
  const endpoint = await config.getDockerEndpoint();
24
58
  if (!endpoint || typeof endpoint !== 'string' || !endpoint.trim()) {
25
59
  return {};
26
60
  }
27
- const devId = await config.getDeveloperId();
28
- const certDir = getCertDir(getConfigDirForPaths(), devId);
29
- const certPath = path.join(certDir, 'cert.pem');
30
- const keyPath = path.join(certDir, 'key.pem');
31
- const caPath = path.join(certDir, 'ca.pem');
61
+ const trimmed = endpoint.trim();
62
+ const certDir = getCertDir(getConfigDirForPaths(), await config.getDeveloperId());
63
+ const { certPath, keyPath, caPath } = devTlsCertPaths(certDir);
32
64
  const fs = require('fs');
33
- if (!fs.existsSync(certPath) || !fs.existsSync(keyPath) || !fs.existsSync(caPath)) {
34
- return {};
65
+ const skipVerify = await config.getDockerTlsSkipVerify();
66
+ const hasClient = fs.existsSync(certPath) && fs.existsSync(keyPath);
67
+ const hasCa = fs.existsSync(caPath);
68
+
69
+ if (!hasClient) {
70
+ if (!skipVerify) throw missingClientTlsError(trimmed, certDir);
71
+ return { DOCKER_HOST: trimmed, DOCKER_TLS_VERIFY: '0' };
35
72
  }
73
+ if (!hasCa && !skipVerify) throw missingCaError(trimmed, certDir);
74
+ const verifyDaemon = hasCa;
36
75
  return {
37
- DOCKER_HOST: endpoint.trim(),
38
- DOCKER_TLS_VERIFY: '1',
76
+ DOCKER_HOST: trimmed,
77
+ DOCKER_TLS_VERIFY: verifyDaemon ? '1' : '0',
39
78
  DOCKER_CERT_PATH: certDir
40
79
  };
41
80
  }
42
81
 
43
- module.exports = { getRemoteDockerEnv };
82
+ /**
83
+ * Full environment for child_process exec/spawn: process.env merged with remote Docker vars when configured.
84
+ * @returns {Promise<Object>}
85
+ */
86
+ async function getDockerExecEnv() {
87
+ const overlay = await getRemoteDockerEnv();
88
+ const merged = { ...process.env };
89
+ if (overlay.DOCKER_HOST && !Object.prototype.hasOwnProperty.call(overlay, 'DOCKER_CERT_PATH')) {
90
+ delete merged.DOCKER_CERT_PATH;
91
+ }
92
+ return { ...merged, ...overlay };
93
+ }
94
+
95
+ module.exports = { getRemoteDockerEnv, getDockerExecEnv };
@@ -14,16 +14,25 @@ const config = require('../core/config');
14
14
  * @returns {Promise<Object|null>} Key-value secrets from API or null
15
15
  */
16
16
  async function loadRemoteSharedSecrets() {
17
- const { isRemoteSecretsUrl, getRemoteDevAuth } = require('./remote-dev-auth');
17
+ const remoteDevAuth = require('./remote-dev-auth');
18
18
  const devApi = require('../api/dev.api');
19
19
  const configSecretsPath = await config.getSecretsPath();
20
- if (!configSecretsPath || !isRemoteSecretsUrl(configSecretsPath)) {
20
+ if (!configSecretsPath) {
21
21
  return null;
22
22
  }
23
- const auth = await getRemoteDevAuth();
23
+ const endpoint = await remoteDevAuth.resolveSharedSecretsEndpoint(configSecretsPath);
24
+ if (!remoteDevAuth.isRemoteSecretsUrl(endpoint)) {
25
+ return null;
26
+ }
27
+ const auth = await remoteDevAuth.getRemoteDevAuth();
24
28
  if (!auth) return null;
25
29
  try {
26
- const items = await devApi.listSecrets(auth.serverUrl, auth.clientCertPem);
30
+ const items = await devApi.listSecrets(
31
+ auth.serverUrl,
32
+ auth.clientCertPem,
33
+ auth.serverCaPem || undefined,
34
+ endpoint
35
+ );
27
36
  if (!Array.isArray(items)) return null;
28
37
  const obj = {};
29
38
  for (const item of items) {