@aifabrix/builder 2.42.1 → 2.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/README.md +2 -2
  3. package/anchor-docs/README.md +10 -0
  4. package/anchor-docs/_TEMPLATE +24 -0
  5. package/bin/aifabrix.js +13 -4
  6. package/integration/hubspot-test/README.md +157 -0
  7. package/integration/{hubspot → hubspot-test}/application.json +6 -6
  8. package/integration/{hubspot → hubspot-test}/create-hubspot.js +10 -10
  9. package/integration/hubspot-test/env.template +4 -0
  10. package/integration/hubspot-test/hubspot-test-datasource-company.json +138 -0
  11. package/integration/hubspot-test/hubspot-test-datasource-contact.json +146 -0
  12. package/integration/hubspot-test/hubspot-test-datasource-deal.json +146 -0
  13. package/integration/hubspot-test/hubspot-test-datasource-users.json +76 -0
  14. package/integration/{hubspot/hubspot-deploy.json → hubspot-test/hubspot-test-deploy.json} +201 -24
  15. package/integration/{hubspot/hubspot-system.json → hubspot-test/hubspot-test-system.json} +8 -7
  16. package/integration/hubspot-test/rbac.json +166 -0
  17. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-credential-real.yaml +3 -3
  18. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-env-vars.yaml +2 -2
  19. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-add-datasource.yaml +1 -1
  20. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-create.yaml +1 -1
  21. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-select.yaml +1 -1
  22. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-known-platform.yaml +1 -1
  23. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
  24. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-mode.yaml +1 -1
  25. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-file.yaml +1 -1
  26. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-url.yaml +1 -1
  27. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-source.yaml +1 -1
  28. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-array-test.yaml +1 -1
  29. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
  30. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
  31. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-test.yaml +1 -1
  32. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-test.yaml +1 -1
  33. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +1 -1
  34. package/integration/{hubspot → hubspot-test}/test-dataplane-down-tests.js +1 -7
  35. package/integration/{hubspot → hubspot-test}/test-dataplane-down.js +3 -3
  36. package/integration/{hubspot → hubspot-test}/test.js +137 -102
  37. package/integration/{hubspot → hubspot-test}/wizard-hubspot-e2e.yaml +2 -2
  38. package/integration/{hubspot → hubspot-test}/wizard-hubspot-platform.yaml +1 -1
  39. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  40. package/integration/roundtrip-test-local/README.md +144 -0
  41. package/integration/roundtrip-test-local/application.yaml +13 -0
  42. package/integration/roundtrip-test-local/env.template +15 -0
  43. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  44. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  45. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  46. package/integration/roundtrip-test-local2/README.md +144 -0
  47. package/integration/roundtrip-test-local2/application.yaml +13 -0
  48. package/integration/roundtrip-test-local2/env.template +15 -0
  49. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  50. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  51. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  52. package/integration/test/wizard.yaml +8 -0
  53. package/jest.config.default.js +10 -0
  54. package/jest.config.integration.fixtures.js +22 -0
  55. package/jest.config.integration.js +21 -18
  56. package/jest.config.isolated.js +10 -0
  57. package/jest.projects.js +288 -0
  58. package/lib/api/datasources-core.api.js +3 -3
  59. package/lib/api/dev-mtls-request.js +110 -0
  60. package/lib/api/dev-server-https.js +145 -0
  61. package/lib/api/dev.api.js +133 -144
  62. package/lib/api/index.js +0 -1
  63. package/lib/api/pipeline.api.js +67 -20
  64. package/lib/api/service-users.api.js +111 -2
  65. package/lib/api/types/dev.types.js +4 -3
  66. package/lib/api/types/pipeline.types.js +8 -5
  67. package/lib/api/types/service-users.types.js +41 -0
  68. package/lib/api/types/validation-run.types.js +56 -0
  69. package/lib/api/validation-run.api.js +99 -0
  70. package/lib/api/validation-runner.js +99 -0
  71. package/lib/app/config.js +1 -1
  72. package/lib/app/deploy-status-display.js +2 -2
  73. package/lib/app/deploy.js +7 -6
  74. package/lib/app/display.js +2 -1
  75. package/lib/app/dockerfile.js +3 -2
  76. package/lib/app/down.js +2 -1
  77. package/lib/app/helpers.js +6 -5
  78. package/lib/app/index.js +27 -8
  79. package/lib/app/list.js +7 -6
  80. package/lib/app/push.js +4 -3
  81. package/lib/app/register.js +19 -8
  82. package/lib/app/rotate-secret.js +17 -13
  83. package/lib/app/run-container-start.js +184 -0
  84. package/lib/app/run-docker-fallback.js +108 -0
  85. package/lib/app/run-env-compose.js +30 -42
  86. package/lib/app/run-helpers.js +49 -126
  87. package/lib/app/run-infra-requirements.js +30 -0
  88. package/lib/app/run-resolve-image.js +21 -0
  89. package/lib/app/run.js +74 -21
  90. package/lib/app/show-display.js +1 -1
  91. package/lib/app/show.js +1 -1
  92. package/lib/build/index.js +13 -10
  93. package/lib/cli/index.js +2 -0
  94. package/lib/cli/setup-app.help.js +67 -0
  95. package/lib/cli/setup-app.js +59 -123
  96. package/lib/cli/setup-app.test-commands.js +179 -0
  97. package/lib/cli/setup-auth.js +36 -14
  98. package/lib/cli/setup-credential-deployment.js +22 -8
  99. package/lib/cli/setup-dev-path-commands.js +124 -0
  100. package/lib/cli/setup-dev.js +190 -103
  101. package/lib/cli/setup-environment.js +11 -20
  102. package/lib/cli/setup-external-system.js +62 -22
  103. package/lib/cli/setup-infra.js +139 -47
  104. package/lib/cli/setup-parameters.js +32 -0
  105. package/lib/cli/setup-secrets.js +147 -10
  106. package/lib/cli/setup-service-user.js +146 -20
  107. package/lib/cli/setup-utility.js +47 -19
  108. package/lib/commands/app-down.js +5 -7
  109. package/lib/commands/app-install.js +14 -7
  110. package/lib/commands/app-logs.js +13 -10
  111. package/lib/commands/app-shell.js +4 -1
  112. package/lib/commands/app-test.js +25 -19
  113. package/lib/commands/app.js +22 -10
  114. package/lib/commands/auth-config.js +10 -14
  115. package/lib/commands/auth-status.js +4 -3
  116. package/lib/commands/credential-env.js +4 -3
  117. package/lib/commands/credential-list.js +5 -4
  118. package/lib/commands/credential-push.js +4 -3
  119. package/lib/commands/datasource-unified-test-cli.js +495 -0
  120. package/lib/commands/datasource-unified-test-cli.options.js +149 -0
  121. package/lib/commands/datasource-validation-cli.js +129 -0
  122. package/lib/commands/datasource.js +123 -71
  123. package/lib/commands/deployment-list.js +6 -5
  124. package/lib/commands/dev-cli-handlers.js +122 -18
  125. package/lib/commands/dev-down.js +4 -3
  126. package/lib/commands/dev-init.js +231 -116
  127. package/lib/commands/dev-show-display.js +473 -0
  128. package/lib/commands/login-credentials.js +3 -2
  129. package/lib/commands/login-device.js +4 -3
  130. package/lib/commands/login.js +5 -4
  131. package/lib/commands/logout.js +8 -7
  132. package/lib/commands/parameters-validate.js +54 -0
  133. package/lib/commands/repair-datasource.js +314 -68
  134. package/lib/commands/repair-env-template.js +16 -10
  135. package/lib/commands/repair-rbac.js +25 -19
  136. package/lib/commands/repair.js +116 -32
  137. package/lib/commands/secrets-list.js +23 -12
  138. package/lib/commands/secrets-remove-all.js +220 -0
  139. package/lib/commands/secrets-remove.js +22 -13
  140. package/lib/commands/secrets-set.js +21 -12
  141. package/lib/commands/secrets-validate.js +20 -7
  142. package/lib/commands/secure.js +10 -9
  143. package/lib/commands/service-user.js +243 -13
  144. package/lib/commands/test-e2e-external.js +27 -1
  145. package/lib/commands/up-common.js +28 -2
  146. package/lib/commands/up-dataplane.js +31 -18
  147. package/lib/commands/up-miso.js +19 -29
  148. package/lib/commands/upload.js +138 -39
  149. package/lib/commands/wizard-core-helpers.js +1 -1
  150. package/lib/commands/wizard-dataplane.js +4 -3
  151. package/lib/commands/wizard-helpers.js +3 -3
  152. package/lib/commands/wizard.js +2 -2
  153. package/lib/core/admin-secrets.js +16 -5
  154. package/lib/core/audit-logger.js +12 -4
  155. package/lib/core/config-attach-extensions.js +46 -0
  156. package/lib/core/config-runtime-paths.js +29 -0
  157. package/lib/core/config.js +59 -58
  158. package/lib/core/diff.js +3 -2
  159. package/lib/core/ensure-encryption-key.js +2 -4
  160. package/lib/core/secrets-ensure-infra.js +77 -0
  161. package/lib/core/secrets-ensure.js +120 -64
  162. package/lib/core/secrets-env-write.js +35 -7
  163. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  164. package/lib/core/secrets.js +228 -42
  165. package/lib/core/templates-env.js +4 -3
  166. package/lib/core/templates.js +1 -1
  167. package/lib/datasource/abac-validator.js +148 -0
  168. package/lib/datasource/deploy.js +75 -53
  169. package/lib/datasource/field-reference-validator.js +77 -36
  170. package/lib/datasource/integration-context.js +63 -0
  171. package/lib/datasource/list.js +8 -7
  172. package/lib/datasource/log-viewer.js +252 -0
  173. package/lib/datasource/resolve-app.js +109 -0
  174. package/lib/datasource/test-e2e.js +95 -155
  175. package/lib/datasource/test-integration.js +121 -109
  176. package/lib/datasource/unified-validation-run-body.js +65 -0
  177. package/lib/datasource/unified-validation-run-post.js +23 -0
  178. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  179. package/lib/datasource/unified-validation-run.js +92 -0
  180. package/lib/datasource/validate.js +162 -15
  181. package/lib/deployment/deployer.js +4 -3
  182. package/lib/deployment/environment.js +7 -6
  183. package/lib/deployment/push.js +17 -8
  184. package/lib/external-system/delete.js +4 -3
  185. package/lib/external-system/deploy.js +131 -53
  186. package/lib/external-system/download-helpers.js +1 -1
  187. package/lib/external-system/download.js +7 -6
  188. package/lib/external-system/generator.js +104 -14
  189. package/lib/external-system/integration-test-dispatch.js +26 -0
  190. package/lib/external-system/test-execution.js +5 -1
  191. package/lib/external-system/test-helpers.js +0 -4
  192. package/lib/external-system/test-system-level-helpers.js +110 -0
  193. package/lib/external-system/test-system-level.js +83 -44
  194. package/lib/external-system/test.js +59 -8
  195. package/lib/generator/builders.js +23 -11
  196. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  197. package/lib/generator/external-controller-manifest.js +3 -3
  198. package/lib/generator/external.js +23 -11
  199. package/lib/generator/helpers.js +71 -12
  200. package/lib/generator/index.js +8 -4
  201. package/lib/generator/split-readme.js +12 -7
  202. package/lib/generator/split-variables.js +2 -1
  203. package/lib/generator/split.js +46 -11
  204. package/lib/generator/wizard-readme.js +3 -3
  205. package/lib/generator/wizard.js +16 -13
  206. package/lib/infrastructure/compose.js +60 -6
  207. package/lib/infrastructure/helpers.js +238 -51
  208. package/lib/infrastructure/index.js +64 -37
  209. package/lib/infrastructure/services.js +21 -15
  210. package/lib/internal/fs-real-sync.js +104 -0
  211. package/lib/internal/node-fs.js +98 -0
  212. package/lib/parameters/database-secret-values.js +173 -0
  213. package/lib/parameters/infra-kv-discovery.js +121 -0
  214. package/lib/parameters/infra-parameter-catalog.js +458 -0
  215. package/lib/parameters/infra-parameter-validate.js +64 -0
  216. package/lib/schema/application-schema.json +37 -17
  217. package/lib/schema/datasource-test-run.schema.json +493 -0
  218. package/lib/schema/deployment-rules.yaml +102 -63
  219. package/lib/schema/external-datasource.schema.json +1201 -433
  220. package/lib/schema/external-system.schema.json +181 -5
  221. package/lib/schema/flag-map-validation-run.json +31 -0
  222. package/lib/schema/infra-parameter.schema.json +106 -0
  223. package/lib/schema/infra.parameter.yaml +421 -0
  224. package/lib/schema/type/credential-auth-templates.json +40 -0
  225. package/lib/schema/type/document-storage.json +213 -0
  226. package/lib/schema/type/message-service.json +123 -0
  227. package/lib/schema/type/vector-store.json +88 -0
  228. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  229. package/lib/utils/api-error-handler.js +2 -2
  230. package/lib/utils/api.js +49 -14
  231. package/lib/utils/app-config-resolver.js +23 -1
  232. package/lib/utils/app-register-api.js +3 -2
  233. package/lib/utils/app-register-auth.js +1 -1
  234. package/lib/utils/app-register-config.js +4 -4
  235. package/lib/utils/app-register-display.js +3 -2
  236. package/lib/utils/app-register-validator.js +3 -2
  237. package/lib/utils/app-run-containers.js +26 -22
  238. package/lib/utils/app-scoped-config.js +31 -0
  239. package/lib/utils/app-service-env-from-builder.js +164 -0
  240. package/lib/utils/build-copy.js +1 -1
  241. package/lib/utils/build-helpers.js +20 -20
  242. package/lib/utils/build-resolve-image.js +165 -0
  243. package/lib/utils/cli-layout-chalk.js +8 -0
  244. package/lib/utils/cli-test-layout-chalk.js +267 -0
  245. package/lib/utils/cli-utils.js +88 -11
  246. package/lib/utils/compose-db-passwords.js +138 -0
  247. package/lib/utils/compose-generate-docker-compose.js +216 -0
  248. package/lib/utils/compose-generator.js +197 -291
  249. package/lib/utils/compose-miso-env.js +18 -0
  250. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  251. package/lib/utils/config-paths.js +209 -6
  252. package/lib/utils/config-scoped-resources-preference.js +41 -0
  253. package/lib/utils/controller-deployment-outcome.js +68 -0
  254. package/lib/utils/credential-display.js +2 -2
  255. package/lib/utils/credential-secrets-env.js +16 -1
  256. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  257. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  258. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  259. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  260. package/lib/utils/datasource-test-run-display.js +442 -0
  261. package/lib/utils/datasource-test-run-exit.js +58 -0
  262. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  263. package/lib/utils/datasource-test-run-report-version.js +51 -0
  264. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  265. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  266. package/lib/utils/datasource-validation-watch.js +266 -0
  267. package/lib/utils/declarative-url-ports.js +47 -0
  268. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  269. package/lib/utils/dev-ca-install.js +185 -23
  270. package/lib/utils/dev-cert-helper.js +266 -17
  271. package/lib/utils/dev-hosts-helper.js +307 -0
  272. package/lib/utils/dev-init-cert-hints.js +37 -0
  273. package/lib/utils/dev-init-health-messages.js +52 -0
  274. package/lib/utils/dev-init-resolve.js +86 -0
  275. package/lib/utils/dev-init-ssh-merge.js +65 -0
  276. package/lib/utils/dev-ssh-config-helper.js +196 -0
  277. package/lib/utils/dev-user-groups.js +93 -0
  278. package/lib/utils/docker-build.js +42 -17
  279. package/lib/utils/docker-exec.js +28 -0
  280. package/lib/utils/docker-manifest-public-port.js +116 -0
  281. package/lib/utils/docker-not-running-hint.js +52 -0
  282. package/lib/utils/docker.js +98 -11
  283. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  284. package/lib/utils/env-config-loader.js +10 -91
  285. package/lib/utils/env-copy.js +19 -10
  286. package/lib/utils/env-map.js +42 -11
  287. package/lib/utils/env-template.js +2 -2
  288. package/lib/utils/environment-scoped-resources.js +144 -0
  289. package/lib/utils/error-formatter.js +125 -9
  290. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  291. package/lib/utils/error-formatters/network-errors.js +2 -1
  292. package/lib/utils/error-formatters/permission-errors.js +2 -1
  293. package/lib/utils/error-formatters/validation-errors.js +2 -1
  294. package/lib/utils/external-env-template.js +180 -0
  295. package/lib/utils/external-readme.js +8 -1
  296. package/lib/utils/external-system-display.js +277 -136
  297. package/lib/utils/external-system-local-test-tty.js +389 -0
  298. package/lib/utils/external-system-readiness-core.js +377 -0
  299. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  300. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  301. package/lib/utils/external-system-readiness-display.js +186 -0
  302. package/lib/utils/external-system-test-helpers.js +24 -6
  303. package/lib/utils/external-system-validators.js +32 -14
  304. package/lib/utils/health-check-url.js +119 -0
  305. package/lib/utils/health-check.js +59 -25
  306. package/lib/utils/help-builder.js +14 -13
  307. package/lib/utils/image-version.js +4 -8
  308. package/lib/utils/infra-containers.js +4 -7
  309. package/lib/utils/infra-env-defaults.js +162 -0
  310. package/lib/utils/infra-status-display.js +167 -0
  311. package/lib/utils/infra-status.js +16 -8
  312. package/lib/utils/local-secrets.js +29 -7
  313. package/lib/utils/paths.js +136 -48
  314. package/lib/utils/port-resolver.js +10 -23
  315. package/lib/utils/redis-env-scope.js +62 -0
  316. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  317. package/lib/utils/remote-builder-validation.js +99 -0
  318. package/lib/utils/remote-dev-auth.js +117 -21
  319. package/lib/utils/remote-docker-env.js +67 -15
  320. package/lib/utils/remote-secrets-loader.js +13 -4
  321. package/lib/utils/resolve-docker-image-ref.js +124 -0
  322. package/lib/utils/schema-loader.js +22 -9
  323. package/lib/utils/secrets-bash-kv.js +25 -0
  324. package/lib/utils/secrets-generator.js +171 -51
  325. package/lib/utils/secrets-helpers.js +70 -59
  326. package/lib/utils/secrets-kv-scope.js +60 -0
  327. package/lib/utils/secrets-utils.js +35 -37
  328. package/lib/utils/secrets-validation.js +3 -1
  329. package/lib/utils/secrets-yaml-preserve.js +109 -0
  330. package/lib/utils/secure-file-permissions.js +91 -0
  331. package/lib/utils/ssh-key-helper.js +4 -2
  332. package/lib/utils/template-helpers.js +2 -2
  333. package/lib/utils/test-log-writer.js +3 -3
  334. package/lib/utils/token-manager.js +37 -5
  335. package/lib/utils/url-declarative-public-base.js +188 -0
  336. package/lib/utils/url-declarative-resolve-build.js +493 -0
  337. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  338. package/lib/utils/url-declarative-resolve.js +220 -0
  339. package/lib/utils/url-declarative-token-parse.js +74 -0
  340. package/lib/utils/url-declarative-url-flags.js +50 -0
  341. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  342. package/lib/utils/url-public-path-prefix.js +34 -0
  343. package/lib/utils/urls-local-registry.js +220 -0
  344. package/lib/utils/validation-report-tty-kit.js +77 -0
  345. package/lib/utils/validation-run-poll.js +89 -0
  346. package/lib/utils/validation-run-post-retry.js +73 -0
  347. package/lib/utils/validation-run-request.js +98 -0
  348. package/lib/utils/variable-transformer.js +21 -4
  349. package/lib/utils/yaml-preserve.js +78 -1
  350. package/lib/validation/datasource-warnings.js +56 -0
  351. package/lib/validation/env-template-auth.js +50 -2
  352. package/lib/validation/external-manifest-validator.js +35 -7
  353. package/lib/validation/validate-display.js +37 -31
  354. package/lib/validation/validate.js +9 -10
  355. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  356. package/lib/validation/validator.js +32 -78
  357. package/lib/validation/wizard-config-validator.js +2 -1
  358. package/package.json +11 -3
  359. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  360. package/scripts/diagnose-cli.js +150 -0
  361. package/scripts/install-local.js +304 -55
  362. package/templates/README.md +15 -2
  363. package/templates/applications/dataplane/application.yaml +52 -2
  364. package/templates/applications/dataplane/env.template +80 -18
  365. package/templates/applications/dataplane/rbac.yaml +8 -0
  366. package/templates/applications/keycloak/application.yaml +9 -1
  367. package/templates/applications/keycloak/env.template +15 -6
  368. package/templates/applications/miso-controller/application.yaml +10 -2
  369. package/templates/applications/miso-controller/env.template +55 -14
  370. package/templates/applications/miso-controller/rbac.yaml +5 -0
  371. package/templates/external-system/README.md.hbs +20 -7
  372. package/templates/external-system/deploy.js.hbs +5 -5
  373. package/templates/external-system/env.template.hbs +22 -0
  374. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  375. package/templates/infra/compose.yaml.hbs +20 -4
  376. package/templates/python/docker-compose.hbs +16 -0
  377. package/templates/typescript/docker-compose.hbs +16 -0
  378. package/integration/hubspot/README.md +0 -102
  379. package/integration/hubspot/env.template +0 -4
  380. package/integration/hubspot/hubspot-datasource-company.json +0 -541
  381. package/integration/hubspot/hubspot-datasource-contact.json +0 -639
  382. package/integration/hubspot/hubspot-datasource-deal.json +0 -588
  383. package/integration/hubspot/hubspot-datasource-users.json +0 -116
  384. package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  385. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  386. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  387. package/lib/api/external-test.api.js +0 -111
  388. package/lib/schema/env-config.yaml +0 -43
  389. /package/integration/{hubspot → hubspot-test}/companies.json +0 -0
  390. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-app-name.yaml +0 -0
  391. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-missing-app.yaml +0 -0
  392. /package/integration/{hubspot → hubspot-test}/test-dataplane-down-helpers.js +0 -0
@@ -12,18 +12,19 @@
12
12
  /* eslint-disable max-lines -- Repair flow with auth, normalization, and steps */
13
13
 
14
14
  'use strict';
15
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
15
16
 
16
17
  const path = require('path');
17
18
  const fs = require('fs');
18
19
  const chalk = require('chalk');
19
- const yaml = require('js-yaml');
20
- const { detectAppType } = require('../utils/paths');
21
- const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
20
+ const { detectAppType, getDeployJsonPath } = require('../utils/paths');
21
+ const { resolveApplicationConfigPath, resolveRbacPath } = require('../utils/app-config-resolver');
22
22
  const { loadConfigFile, writeConfigFile, writeYamlPreservingComments, isYamlPath } = require('../utils/config-format');
23
23
  const { systemKeyToKvPrefix, securityKeyToVar } = require('../utils/credential-secrets-env');
24
24
  const logger = require('../utils/logger');
25
25
  const generator = require('../generator');
26
26
  const { repairEnvTemplate, normalizeSystemFileAuthAndConfig } = require('./repair-env-template');
27
+ const { generateReadmeFromDeployJson } = require('../generator/split-readme');
27
28
  const { repairDatasourceFile } = require('./repair-datasource');
28
29
  const { mergeRbacFromDatasources } = require('./repair-rbac');
29
30
  const { discoverIntegrationFiles, buildEffectiveDatasourceFiles } = require('./repair-internal');
@@ -32,6 +33,22 @@ const { normalizeDatasourceKeysAndFilenames } = require('./repair-datasource-key
32
33
  /** Allowed authentication methods for repair --auth (matches external-system schema) */
33
34
  const ALLOWED_AUTH = ['oauth2', 'aad', 'apikey', 'basic', 'queryParam', 'oidc', 'hmac', 'none'];
34
35
 
36
+ /**
37
+ * README "Files" section should match integration config format on disk (YAML vs JSON).
38
+ * @param {string} appPath - Integration directory
39
+ * @returns {string} '.yaml' or '.json'
40
+ */
41
+ function inferExternalReadmeFileExt(appPath) {
42
+ try {
43
+ const configPath = resolveApplicationConfigPath(appPath);
44
+ const ext = path.extname(configPath).toLowerCase();
45
+ if (ext === '.yaml' || ext === '.yml') return '.yaml';
46
+ } catch {
47
+ /* use default */
48
+ }
49
+ return '.json';
50
+ }
51
+
35
52
  /**
36
53
  * Extracts roles and permissions from system object for rbac.yaml
37
54
  * @param {Object} system - Parsed system config
@@ -249,6 +266,11 @@ function normalizedAuthPartFromConfigName(name, systemKey) {
249
266
  const rest = n.slice(kvPrefix.length);
250
267
  return rest.toUpperCase().replace(/_/g, '');
251
268
  }
269
+ // Old-style or other KV_* names: treat last segment as var (e.g. KV_HUBSPOT_CLIENTID → CLIENTID)
270
+ if (n.toUpperCase().startsWith('KV_')) {
271
+ const parts = n.split('_').filter(Boolean);
272
+ if (parts.length >= 2) return parts[parts.length - 1].toUpperCase();
273
+ }
252
274
  return n.toUpperCase().replace(/_/g, '');
253
275
  }
254
276
 
@@ -273,26 +295,25 @@ function removeAuthVarsFromConfiguration(systemParsed, systemKey, dryRun, change
273
295
  return true;
274
296
  }
275
297
 
276
- function createRbacFromSystemIfNeeded(appPath, systemFilePath, systemParsed, dryRun, changes) {
277
- const rbacPath = path.join(appPath, 'rbac.yaml');
278
- const rbacYmlPath = path.join(appPath, 'rbac.yml');
279
- if (fs.existsSync(rbacPath) || fs.existsSync(rbacYmlPath)) return false;
298
+ function createRbacFromSystemIfNeeded(appPath, systemFilePath, systemParsed, dryRun, changes, format) {
299
+ if (resolveRbacPath(appPath)) return false;
280
300
  const rbacFromSystem = extractRbacFromSystem(systemParsed);
281
301
  if (!rbacFromSystem) return false;
282
302
  if (!dryRun) {
283
- const rbacYaml = yaml.dump(rbacFromSystem, { indent: 2, lineWidth: -1 });
284
- fs.writeFileSync(rbacPath, rbacYaml, { mode: 0o644, encoding: 'utf8' });
303
+ const rbacFormat = format === 'json' ? 'json' : 'yaml';
304
+ const defaultRbacPath = path.join(appPath, rbacFormat === 'json' ? 'rbac.json' : 'rbac.yaml');
305
+ writeConfigFile(defaultRbacPath, rbacFromSystem, rbacFormat);
285
306
  delete systemParsed.roles;
286
307
  delete systemParsed.permissions;
287
308
  writeConfigFile(systemFilePath, systemParsed);
288
309
  }
289
310
  changes.push('Created rbac.yaml from system roles/permissions');
290
- changes.push('Removed roles/permissions from system file (now in rbac.yaml)');
311
+ changes.push('Removed roles/permissions from system file (now in rbac file)');
291
312
  return true;
292
313
  }
293
314
 
294
315
  /**
295
- * Runs datasource repair for each file (dimensions, metadataSchema, optional expose/sync/test).
316
+ * Runs datasource repair for each file (v2.4 root dimensions, metadataSchema; optional expose/sync/test).
296
317
  * @param {string} appPath - Application path
297
318
  * @param {string[]} datasourceFiles - Datasource file names
298
319
  * @param {Object} options - { expose?: boolean, sync?: boolean, test?: boolean }
@@ -338,13 +359,44 @@ async function regenerateManifest(appName, appPath, changes) {
338
359
  }
339
360
  }
340
361
 
362
+ /**
363
+ * Regenerates README.md from deployment manifest when options.doc is set.
364
+ * @param {string} appName - Application name
365
+ * @param {string} appPath - Application path
366
+ * @param {Object} options - Options (doc, dryRun)
367
+ * @param {string[]} changes - Array to append change messages to
368
+ * @returns {Promise<boolean>} True if README was regenerated
369
+ */
370
+ async function regenerateReadmeIfRequested(appName, appPath, options, changes) {
371
+ if (!options.doc) return false;
372
+ const deployJsonPath = getDeployJsonPath(appName, 'external', true);
373
+ if (!fs.existsSync(deployJsonPath) && !options.dryRun) {
374
+ await regenerateManifest(appName, appPath, changes);
375
+ }
376
+ if (!fs.existsSync(deployJsonPath)) return false;
377
+ try {
378
+ const deployment = JSON.parse(fs.readFileSync(deployJsonPath, 'utf8'));
379
+ const fileExt = inferExternalReadmeFileExt(appPath);
380
+ const readmeContent = generateReadmeFromDeployJson(deployment, { fileExt });
381
+ const readmePath = path.join(appPath, 'README.md');
382
+ if (!options.dryRun) {
383
+ fs.writeFileSync(readmePath, readmeContent, { mode: 0o644, encoding: 'utf8' });
384
+ }
385
+ changes.push('Regenerated README.md from deployment manifest');
386
+ return true;
387
+ } catch (err) {
388
+ logger.log(chalk.yellow(`⚠ Could not regenerate README: ${err.message}`));
389
+ return false;
390
+ }
391
+ }
392
+
341
393
  function persistChangesAndRegenerate(configPath, variables, appName, appPath, changes, originalYamlContent) {
342
394
  if (originalYamlContent !== null && originalYamlContent !== undefined && typeof originalYamlContent === 'string' && isYamlPath(configPath)) {
343
395
  writeYamlPreservingComments(configPath, originalYamlContent, variables);
344
396
  } else {
345
397
  writeConfigFile(configPath, variables);
346
398
  }
347
- logger.log(chalk.green(`✓ Updated ${path.basename(configPath)}`));
399
+ logger.log(formatSuccessLine(`Updated ${path.basename(configPath)}`));
348
400
  changes.forEach(c => logger.log(chalk.gray(` ${c}`)));
349
401
  return regenerateManifest(appName, appPath, changes);
350
402
  }
@@ -410,7 +462,7 @@ function runRepairSteps(ctx) {
410
462
  ctx.appPath, ctx.datasourceFiles, ctx.systemKey, ctx.dryRun, ctx.changes
411
463
  );
412
464
  const rbacFileCreated = createRbacFromSystemIfNeeded(
413
- ctx.appPath, ctx.systemFilePath, ctx.systemParsed, ctx.dryRun, ctx.changes
465
+ ctx.appPath, ctx.systemFilePath, ctx.systemParsed, ctx.dryRun, ctx.changes, ctx.format
414
466
  );
415
467
  const envTemplateRepaired = repairEnvTemplate(
416
468
  ctx.appPath, ctx.systemParsed, ctx.systemKey, ctx.dryRun, ctx.changes
@@ -431,7 +483,8 @@ function runRepairSteps(ctx) {
431
483
  * @param {string} appName - Application/integration name
432
484
  * @param {Object} [options] - Options
433
485
  * @param {boolean} [options.dryRun] - If true, only report changes; do not write
434
- * @returns {Promise<{ updated: boolean, changes: string[], systemFiles: string[], datasourceFiles: string[], appKeyFixed?: boolean, datasourceKeysFixed?: boolean, rbacFileCreated?: boolean, envTemplateRepaired?: boolean, manifestRegenerated?: boolean }>}
486
+ * @param {boolean} [options.doc] - If true, regenerate README.md from deployment manifest
487
+ * @returns {Promise<{ updated: boolean, changes: string[], systemFiles: string[], datasourceFiles: string[], appKeyFixed?: boolean, datasourceKeysFixed?: boolean, rbacFileCreated?: boolean, envTemplateRepaired?: boolean, manifestRegenerated?: boolean, readmeRegenerated?: boolean }>}
435
488
  */
436
489
  /**
437
490
  * Loads application config and discovers integration files; validates at least one system file exists.
@@ -453,7 +506,36 @@ function loadConfigAndDiscover(appPath, configPath) {
453
506
  return { variables, originalYamlContent, systemFiles, datasourceFiles };
454
507
  }
455
508
 
456
- async function repairExternalIntegration(appName, options = {}) {
509
+ /**
510
+ * Builds the repair result object from steps and flags.
511
+ * @param {Object} steps - Result of runRepairSteps
512
+ * @param {boolean} anyUpdated - Whether any repair made changes
513
+ * @param {boolean} manifestRegenerated - Whether manifest was regenerated
514
+ * @param {boolean} readmeRegenerated - Whether README was regenerated
515
+ * @param {{ changes: string[], systemFiles: string[], datasourceFiles: string[] }} ctx - changes and file lists
516
+ * @returns {Object} Combined result object
517
+ */
518
+ function buildRepairResult(steps, anyUpdated, manifestRegenerated, readmeRegenerated, ctx) {
519
+ return Object.assign(
520
+ { updated: anyUpdated, changes: ctx.changes, systemFiles: ctx.systemFiles, datasourceFiles: ctx.datasourceFiles },
521
+ {
522
+ appKeyFixed: steps.appKeyFixed,
523
+ datasourceKeysFixed: steps.datasourceKeysFixed,
524
+ rbacFileCreated: steps.rbacFileCreated,
525
+ envTemplateRepaired: steps.envTemplateRepaired,
526
+ manifestRegenerated,
527
+ readmeRegenerated
528
+ }
529
+ );
530
+ }
531
+
532
+ /**
533
+ * Validates repair inputs and resolves app path and config path.
534
+ * @param {string} appName - Application name
535
+ * @param {Object} options - Command options (auth)
536
+ * @returns {Promise<{ appPath: string, configPath: string, dryRun: boolean, authOption: string|undefined }>}
537
+ */
538
+ async function validateAndResolveRepairPaths(appName, options) {
457
539
  if (!appName || typeof appName !== 'string') throw new Error('App name is required');
458
540
  const { dryRun = false, auth: authOption } = options;
459
541
  if (authOption !== undefined && authOption !== null && typeof authOption !== 'string') {
@@ -463,6 +545,11 @@ async function repairExternalIntegration(appName, options = {}) {
463
545
  if (!isExternal) throw new Error(`App '${appName}' is not an external integration`);
464
546
  const configPath = resolveApplicationConfigPath(appPath);
465
547
  if (!fs.existsSync(configPath)) throw new Error(`Application config not found: ${configPath}`);
548
+ return { appPath, configPath, dryRun, authOption };
549
+ }
550
+
551
+ async function repairExternalIntegration(appName, options = {}) {
552
+ const { appPath, configPath, dryRun, authOption } = await validateAndResolveRepairPaths(appName, options);
466
553
 
467
554
  const { variables, originalYamlContent, systemFiles, datasourceFiles: initialDatasourceFiles } = loadConfigAndDiscover(appPath, configPath);
468
555
  const changes = [];
@@ -486,30 +573,27 @@ async function repairExternalIntegration(appName, options = {}) {
486
573
  datasourceFiles,
487
574
  dryRun,
488
575
  changes,
489
- auth: authOption
576
+ auth: authOption,
577
+ format: options.format === 'json' ? 'json' : 'yaml'
490
578
  });
491
-
492
- const opts = { expose: Boolean(options.expose), sync: Boolean(options.sync), test: Boolean(options.test) };
493
- const datasourceRepairUpdated = runDatasourceRepairs(appPath, datasourceFiles, opts, dryRun, changes);
579
+ const datasourceRepairUpdated = runDatasourceRepairs(appPath, datasourceFiles, {
580
+ expose: Boolean(options.expose),
581
+ sync: Boolean(options.sync),
582
+ test: Boolean(options.test)
583
+ }, dryRun, changes);
494
584
  const rbacMergeUpdated = options.rbac
495
- ? mergeRbacFromDatasources(appPath, systemParsed, datasourceFiles, extractRbacFromSystem, dryRun, changes)
585
+ ? mergeRbacFromDatasources(appPath, systemParsed, datasourceFiles, extractRbacFromSystem, {
586
+ format: options.format === 'json' ? 'json' : 'yaml',
587
+ dryRun,
588
+ changes
589
+ })
496
590
  : false;
497
591
  const anyUpdated = keysNormalized || steps.updated || datasourceRepairUpdated || rbacMergeUpdated;
498
592
  const manifestRegenerated = (anyUpdated && !dryRun)
499
593
  ? await persistChangesAndRegenerate(configPath, variables, appName, appPath, changes, originalYamlContent)
500
594
  : false;
501
-
502
- return {
503
- updated: anyUpdated,
504
- changes,
505
- systemFiles,
506
- datasourceFiles,
507
- appKeyFixed: steps.appKeyFixed,
508
- datasourceKeysFixed: steps.datasourceKeysFixed,
509
- rbacFileCreated: steps.rbacFileCreated,
510
- envTemplateRepaired: steps.envTemplateRepaired,
511
- manifestRegenerated
512
- };
595
+ const readmeRegenerated = await regenerateReadmeIfRequested(appName, appPath, options, changes);
596
+ return buildRepairResult(steps, anyUpdated, manifestRegenerated, readmeRegenerated, { changes, systemFiles, datasourceFiles });
513
597
  }
514
598
 
515
599
  module.exports = {
@@ -11,7 +11,7 @@ const chalk = require('chalk');
11
11
  const logger = require('../utils/logger');
12
12
  const { getAifabrixSecretsPath } = require('../core/config');
13
13
  const pathsUtil = require('../utils/paths');
14
- const { isRemoteSecretsUrl, getRemoteDevAuth } = require('../utils/remote-dev-auth');
14
+ const remoteDevAuth = require('../utils/remote-dev-auth');
15
15
  const devApi = require('../api/dev.api');
16
16
 
17
17
  const REMOTE_NOT_CONFIGURED_MSG = 'Remote server is not configured. Set remote-server and run "aifabrix dev init" first.';
@@ -38,8 +38,8 @@ function listKeysAndValuesFromFile(filePath) {
38
38
  }
39
39
  }
40
40
 
41
- const KEY_COL_WIDTH = 45;
42
- const TABLE_SEPARATOR_LENGTH = 120;
41
+ const KEY_COL_WIDTH = 55;
42
+ const TABLE_SEPARATOR_LENGTH = 130;
43
43
 
44
44
  /**
45
45
  * Log a list of secret keys and values as a table (header, column headers, separator, rows).
@@ -70,19 +70,26 @@ function logKeyValueList(emptyMessage, title, items) {
70
70
  * @returns {Promise<void>}
71
71
  */
72
72
  async function listSharedSecrets(generalSecretsPath) {
73
- if (isRemoteSecretsUrl(generalSecretsPath)) {
74
- const auth = await getRemoteDevAuth();
73
+ const target = await remoteDevAuth.resolveSharedSecretsEndpoint(generalSecretsPath);
74
+ if (remoteDevAuth.isRemoteSecretsUrl(target)) {
75
+ const auth = await remoteDevAuth.getRemoteDevAuth();
75
76
  if (!auth) {
76
77
  throw new Error(REMOTE_NOT_CONFIGURED_MSG);
77
78
  }
78
- const items = await devApi.listSecrets(auth.serverUrl, auth.clientCertPem);
79
+ const items = await devApi.listSecrets(
80
+ auth.serverUrl,
81
+ auth.clientCertPem,
82
+ auth.serverCaPem || undefined,
83
+ target
84
+ );
79
85
  const keyValues = items.map(i => ({ key: i.name || i.key || '', value: (i.value !== null && i.value !== undefined) ? String(i.value) : '' }));
80
- logKeyValueList('No shared secrets (remote).', 'Shared secrets (remote)', keyValues);
86
+ const { title, emptyMessage } = remoteDevAuth.getSharedSecretsRemoteListLabels(target);
87
+ logKeyValueList(emptyMessage, title, keyValues);
81
88
  return;
82
89
  }
83
- const resolvedPath = path.isAbsolute(generalSecretsPath)
84
- ? generalSecretsPath
85
- : path.resolve(process.cwd(), generalSecretsPath);
90
+ const resolvedPath = path.isAbsolute(target)
91
+ ? target
92
+ : path.resolve(process.cwd(), target);
86
93
  const keyValues = listKeysAndValuesFromFile(resolvedPath);
87
94
  const fileTitle = `Shared secrets (file: ${resolvedPath})`;
88
95
  logKeyValueList('No shared secrets in file.', fileTitle, keyValues);
@@ -90,9 +97,13 @@ async function listSharedSecrets(generalSecretsPath) {
90
97
 
91
98
  /** List user secrets and log key and value. */
92
99
  function listUserSecrets() {
93
- const userSecretsPath = path.join(pathsUtil.getAifabrixHome(), 'secrets.local.yaml');
100
+ const userSecretsPath = pathsUtil.getPrimaryUserSecretsLocalPath();
94
101
  const keyValues = listKeysAndValuesFromFile(userSecretsPath);
95
- logKeyValueList('No user secrets.', 'User secrets', keyValues);
102
+ logKeyValueList(
103
+ `No user secrets (file: ${userSecretsPath}). Use "aifabrix secret set <KEY> <value>" or resolve/up-infra to populate.`,
104
+ 'User secrets',
105
+ keyValues
106
+ );
96
107
  }
97
108
 
98
109
  /**
@@ -0,0 +1,220 @@
1
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
2
+ /**
3
+ * @fileoverview aifabrix secret remove-all – remove every secret (user file, shared file, or remote API)
4
+ * @author AI Fabrix Team
5
+ * @version 2.0.0
6
+ */
7
+
8
+ const path = require('path');
9
+ const fs = require('fs');
10
+ const readline = require('readline');
11
+ const yaml = require('js-yaml');
12
+ const chalk = require('chalk');
13
+ const logger = require('../utils/logger');
14
+ const { getAifabrixSecretsPath } = require('../core/config');
15
+ const pathsUtil = require('../utils/paths');
16
+ const remoteDevAuth = require('../utils/remote-dev-auth');
17
+ const devApi = require('../api/dev.api');
18
+
19
+ /**
20
+ * @param {string} question
21
+ * @returns {Promise<string>}
22
+ */
23
+ function promptLine(question) {
24
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
25
+ return new Promise(resolve => {
26
+ rl.question(question, answer => {
27
+ rl.close();
28
+ resolve((answer || '').trim());
29
+ });
30
+ });
31
+ }
32
+
33
+ /**
34
+ * Read secret keys from a YAML secrets file.
35
+ * @param {string} filePath - Absolute path
36
+ * @returns {string[]} Sorted unique keys
37
+ */
38
+ function listKeysFromYamlFile(filePath) {
39
+ if (!fs.existsSync(filePath)) {
40
+ return [];
41
+ }
42
+ try {
43
+ const content = fs.readFileSync(filePath, 'utf8');
44
+ const data = yaml.load(content) || {};
45
+ if (typeof data !== 'object' || Array.isArray(data)) {
46
+ return [];
47
+ }
48
+ return Object.keys(data).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
49
+ } catch {
50
+ return [];
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Write an empty secrets map to file (same options as single-key remove).
56
+ * @param {string} filePath - Absolute path
57
+ */
58
+ function writeEmptySecretsFile(filePath) {
59
+ const yamlContent = yaml.dump({}, { indent: 2, lineWidth: -1, noRefs: true, sortKeys: false });
60
+ fs.writeFileSync(filePath, yamlContent, { mode: 0o600 });
61
+ }
62
+
63
+ /**
64
+ * @param {boolean} skipPrompt - When true (--yes), skip confirmation
65
+ * @param {string} whereLabel - Human-readable target
66
+ * @param {number} count - Number of keys
67
+ * @returns {Promise<boolean>}
68
+ */
69
+ async function confirmRemoveAll(skipPrompt, whereLabel, count) {
70
+ if (skipPrompt) {
71
+ return true;
72
+ }
73
+ logger.log(chalk.yellow(`\nThis will permanently remove all ${count} secret key(s) from ${whereLabel}.`));
74
+ logger.log(chalk.gray('This cannot be undone.\n'));
75
+ const answer = (await promptLine(chalk.bold('Type "yes" to confirm (anything else cancels): '))).toLowerCase();
76
+ return answer === 'yes';
77
+ }
78
+
79
+ /**
80
+ * @param {Object} auth - Remote dev auth (serverUrl, clientCertPem, serverCaPem)
81
+ * @param {string} target - Secrets endpoint URL
82
+ * @returns {Promise<string[]>}
83
+ */
84
+ async function listRemoteSecretKeys(auth, target) {
85
+ const items = await devApi.listSecrets(
86
+ auth.serverUrl,
87
+ auth.clientCertPem,
88
+ auth.serverCaPem || undefined,
89
+ target
90
+ );
91
+ return (Array.isArray(items) ? items : [])
92
+ .map(i => i.name || i.key || '')
93
+ .filter(k => typeof k === 'string' && k.length > 0)
94
+ .sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
95
+ }
96
+
97
+ /**
98
+ * @param {string} target - Secrets endpoint URL
99
+ * @returns {string}
100
+ */
101
+ function labelForRemoteSharedSecrets(target) {
102
+ const host = remoteDevAuth.getSharedSecretsRemoteHostname(target);
103
+ return host ? `shared secrets (remote - ${host})` : 'shared secrets (remote)';
104
+ }
105
+
106
+ /**
107
+ * @param {Object} auth
108
+ * @param {string[]} keys
109
+ * @param {string} target
110
+ * @returns {Promise<void>}
111
+ */
112
+ async function deleteRemoteSecretKeys(auth, keys, target) {
113
+ const ca = auth.serverCaPem || undefined;
114
+ for (const key of keys) {
115
+ await devApi.deleteSecret(auth.serverUrl, auth.clientCertPem, key, ca, target);
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Remove every shared secret via remote API.
121
+ * @param {string} target - Resolved secrets endpoint URL
122
+ * @param {Object} options - { yes?: boolean }
123
+ * @returns {Promise<void>}
124
+ */
125
+ async function removeAllRemoteSharedSecrets(target, options) {
126
+ const auth = await remoteDevAuth.getRemoteDevAuth();
127
+ if (!auth) {
128
+ throw new Error('Remote server not configured or certificate missing. Run "aifabrix dev init" first.');
129
+ }
130
+ const keys = await listRemoteSecretKeys(auth, target);
131
+ if (keys.length === 0) {
132
+ logger.log(chalk.gray('No shared secrets to remove.'));
133
+ return;
134
+ }
135
+ const where = labelForRemoteSharedSecrets(target);
136
+ const ok = await confirmRemoveAll(!!options.yes, where, keys.length);
137
+ if (!ok) {
138
+ logger.log(chalk.gray('Cancelled. No secrets were removed.'));
139
+ return;
140
+ }
141
+ await deleteRemoteSecretKeys(auth, keys, target);
142
+ logger.log(formatSuccessLine(`Removed ${keys.length} secret key(s) from ${where}.`));
143
+ }
144
+
145
+ /**
146
+ * Remove every key from a shared secrets YAML file.
147
+ * @param {string} resolvedPath - Absolute file path
148
+ * @param {Object} options - { yes?: boolean }
149
+ * @returns {Promise<void>}
150
+ */
151
+ async function removeAllSharedFileSecrets(resolvedPath, options) {
152
+ const keys = listKeysFromYamlFile(resolvedPath);
153
+ if (keys.length === 0) {
154
+ logger.log(chalk.gray('No shared secrets to remove.'));
155
+ return;
156
+ }
157
+ const ok = await confirmRemoveAll(!!options.yes, `shared secrets file (${resolvedPath})`, keys.length);
158
+ if (!ok) {
159
+ logger.log(chalk.gray('Cancelled. No secrets were removed.'));
160
+ return;
161
+ }
162
+ writeEmptySecretsFile(resolvedPath);
163
+ logger.log(formatSuccessLine(`Removed ${keys.length} secret key(s) from shared secrets file.`));
164
+ }
165
+
166
+ /**
167
+ * Remove every key from shared store (remote API or file).
168
+ * @param {string} generalSecretsPath - Path or URL from config
169
+ * @param {Object} options - { yes?: boolean }
170
+ * @returns {Promise<void>}
171
+ */
172
+ async function removeAllSharedSecrets(generalSecretsPath, options) {
173
+ const target = await remoteDevAuth.resolveSharedSecretsEndpoint(generalSecretsPath);
174
+ if (remoteDevAuth.isRemoteSecretsUrl(target)) {
175
+ await removeAllRemoteSharedSecrets(target, options);
176
+ return;
177
+ }
178
+ const resolvedPath = path.isAbsolute(target) ? target : path.resolve(process.cwd(), target);
179
+ await removeAllSharedFileSecrets(resolvedPath, options);
180
+ }
181
+
182
+ /**
183
+ * @param {Object} options - { yes?: boolean }
184
+ * @returns {Promise<void>}
185
+ */
186
+ async function removeAllUserSecrets(options) {
187
+ const userSecretsPath = pathsUtil.getPrimaryUserSecretsLocalPath();
188
+ const keys = listKeysFromYamlFile(userSecretsPath);
189
+ if (keys.length === 0) {
190
+ logger.log(chalk.gray('No user secrets to remove.'));
191
+ return;
192
+ }
193
+ const ok = await confirmRemoveAll(!!options.yes, `user secrets (${userSecretsPath})`, keys.length);
194
+ if (!ok) {
195
+ logger.log(chalk.gray('Cancelled. No secrets were removed.'));
196
+ return;
197
+ }
198
+ writeEmptySecretsFile(userSecretsPath);
199
+ logger.log(formatSuccessLine(`Removed ${keys.length} secret key(s) from user secrets.`));
200
+ }
201
+
202
+ /**
203
+ * Handle secret remove-all command.
204
+ * @param {Object} options - { shared?: boolean, yes?: boolean }
205
+ * @returns {Promise<void>}
206
+ */
207
+ async function handleSecretsRemoveAll(options) {
208
+ const isShared = options.shared || options['shared'] || false;
209
+ if (!isShared) {
210
+ await removeAllUserSecrets(options);
211
+ return;
212
+ }
213
+ const generalSecretsPath = await getAifabrixSecretsPath();
214
+ if (!generalSecretsPath) {
215
+ throw new Error('Shared secrets not configured. Set aifabrix-secrets in config.yaml.');
216
+ }
217
+ await removeAllSharedSecrets(generalSecretsPath, options);
218
+ }
219
+
220
+ module.exports = { handleSecretsRemoveAll };
@@ -4,14 +4,14 @@
4
4
  * @version 2.0.0
5
5
  */
6
6
 
7
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
7
8
  const path = require('path');
8
9
  const fs = require('fs');
9
10
  const yaml = require('js-yaml');
10
- const chalk = require('chalk');
11
11
  const logger = require('../utils/logger');
12
12
  const { getAifabrixSecretsPath } = require('../core/config');
13
13
  const pathsUtil = require('../utils/paths');
14
- const { isRemoteSecretsUrl, getRemoteDevAuth } = require('../utils/remote-dev-auth');
14
+ const remoteDevAuth = require('../utils/remote-dev-auth');
15
15
  const devApi = require('../api/dev.api');
16
16
 
17
17
  /**
@@ -33,7 +33,7 @@ function removeKeyFromFile(key, filePath) {
33
33
  throw new Error(`Secret '${key}' not found.`);
34
34
  }
35
35
  delete data[key];
36
- const yamlContent = yaml.dump(data, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false });
36
+ const yamlContent = yaml.dump(data, { indent: 2, lineWidth: -1, noRefs: true, sortKeys: false });
37
37
  fs.writeFileSync(filePath, yamlContent, { mode: 0o600 });
38
38
  }
39
39
 
@@ -44,27 +44,36 @@ function removeKeyFromFile(key, filePath) {
44
44
  * @returns {Promise<void>}
45
45
  */
46
46
  async function removeSharedSecret(key, generalSecretsPath) {
47
- if (isRemoteSecretsUrl(generalSecretsPath)) {
48
- const auth = await getRemoteDevAuth();
47
+ const target = await remoteDevAuth.resolveSharedSecretsEndpoint(generalSecretsPath);
48
+ if (remoteDevAuth.isRemoteSecretsUrl(target)) {
49
+ const auth = await remoteDevAuth.getRemoteDevAuth();
49
50
  if (!auth) {
50
51
  throw new Error('Remote server not configured or certificate missing. Run "aifabrix dev init" first.');
51
52
  }
52
53
  try {
53
- await devApi.deleteSecret(auth.serverUrl, auth.clientCertPem, key);
54
+ await devApi.deleteSecret(
55
+ auth.serverUrl,
56
+ auth.clientCertPem,
57
+ key,
58
+ auth.serverCaPem || undefined,
59
+ target
60
+ );
54
61
  } catch (err) {
55
62
  if (err.status === 404) {
56
63
  throw new Error(`Secret '${key}' not found.`);
57
64
  }
58
65
  throw err;
59
66
  }
60
- logger.log(chalk.green(`✓ Secret '${key}' removed from remote shared secrets.`));
67
+ const host = remoteDevAuth.getSharedSecretsRemoteHostname(target);
68
+ const where = host ? `shared secrets (remote - ${host})` : 'shared secrets (remote)';
69
+ logger.log(formatSuccessLine(`Secret '${key}' removed from ${where}.`));
61
70
  return;
62
71
  }
63
- const resolvedPath = path.isAbsolute(generalSecretsPath)
64
- ? generalSecretsPath
65
- : path.resolve(process.cwd(), generalSecretsPath);
72
+ const resolvedPath = path.isAbsolute(target)
73
+ ? target
74
+ : path.resolve(process.cwd(), target);
66
75
  removeKeyFromFile(key, resolvedPath);
67
- logger.log(chalk.green(`✓ Secret '${key}' removed from shared secrets file.`));
76
+ logger.log(formatSuccessLine(`Secret '${key}' removed from shared secrets file.`));
68
77
  }
69
78
 
70
79
  /**
@@ -88,9 +97,9 @@ async function handleSecretsRemove(key, options) {
88
97
  }
89
98
  await removeSharedSecret(key, generalSecretsPath);
90
99
  } else {
91
- const userSecretsPath = path.join(pathsUtil.getAifabrixHome(), 'secrets.local.yaml');
100
+ const userSecretsPath = pathsUtil.getPrimaryUserSecretsLocalPath();
92
101
  removeKeyFromFile(key, userSecretsPath);
93
- logger.log(chalk.green(`✓ Secret '${key}' removed from user secrets.`));
102
+ logger.log(formatSuccessLine(`Secret '${key}' removed from user secrets.`));
94
103
  }
95
104
  }
96
105