@aifabrix/builder 2.43.0 → 2.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/.cursor/rules/cli-layout.mdc +75 -0
  3. package/.cursor/rules/project-rules.mdc +8 -0
  4. package/.npmrc.token +1 -0
  5. package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
  6. package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
  7. package/.nyc_output/processinfo/index.json +1 -0
  8. package/README.md +1 -1
  9. package/anchor-docs/README.md +10 -0
  10. package/anchor-docs/_TEMPLATE +24 -0
  11. package/bin/aifabrix.js +13 -4
  12. package/integration/hubspot-test/README.md +31 -0
  13. package/integration/hubspot-test/create-hubspot.js +5 -5
  14. package/integration/hubspot-test/hubspot-test-datasource-company.json +58 -462
  15. package/integration/hubspot-test/hubspot-test-datasource-contact.json +61 -555
  16. package/integration/hubspot-test/hubspot-test-datasource-deal.json +63 -506
  17. package/integration/hubspot-test/hubspot-test-datasource-users.json +42 -83
  18. package/integration/hubspot-test/hubspot-test-deploy.json +3 -3
  19. package/integration/hubspot-test/test-dataplane-down-tests.js +1 -7
  20. package/integration/hubspot-test/test-dataplane-down.js +3 -3
  21. package/integration/hubspot-test/test.js +35 -43
  22. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  23. package/integration/roundtrip-test-local/README.md +144 -0
  24. package/integration/roundtrip-test-local/application.yaml +13 -0
  25. package/integration/roundtrip-test-local/env.template +15 -0
  26. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  27. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  28. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  29. package/integration/roundtrip-test-local2/README.md +144 -0
  30. package/integration/roundtrip-test-local2/application.yaml +13 -0
  31. package/integration/roundtrip-test-local2/env.template +15 -0
  32. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  33. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  34. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  35. package/integration/test/wizard.yaml +8 -0
  36. package/jest.config.default.js +10 -0
  37. package/jest.config.integration.fixtures.js +22 -0
  38. package/jest.config.integration.js +21 -18
  39. package/jest.config.isolated.js +10 -0
  40. package/jest.projects.js +301 -0
  41. package/lib/api/certificates.api.js +62 -0
  42. package/lib/api/datasources-core.api.js +3 -3
  43. package/lib/api/dev-mtls-request.js +110 -0
  44. package/lib/api/dev-server-https.js +145 -0
  45. package/lib/api/dev.api.js +133 -144
  46. package/lib/api/index.js +11 -3
  47. package/lib/api/pipeline.api.js +67 -20
  48. package/lib/api/types/certificates.types.js +48 -0
  49. package/lib/api/types/dev.types.js +4 -3
  50. package/lib/api/types/pipeline.types.js +8 -5
  51. package/lib/api/types/validation-run.types.js +56 -0
  52. package/lib/api/validation-run.api.js +111 -0
  53. package/lib/api/validation-runner.js +109 -0
  54. package/lib/app/certification-show-enrich.js +129 -0
  55. package/lib/app/certification-verify-rows.js +60 -0
  56. package/lib/app/config.js +1 -1
  57. package/lib/app/deploy-status-display.js +2 -2
  58. package/lib/app/deploy.js +7 -6
  59. package/lib/app/display.js +2 -1
  60. package/lib/app/dockerfile.js +3 -2
  61. package/lib/app/down.js +2 -1
  62. package/lib/app/helpers.js +6 -5
  63. package/lib/app/index.js +27 -8
  64. package/lib/app/list.js +7 -6
  65. package/lib/app/push.js +4 -3
  66. package/lib/app/register.js +16 -7
  67. package/lib/app/rotate-secret.js +14 -13
  68. package/lib/app/run-container-start.js +184 -0
  69. package/lib/app/run-docker-fallback.js +108 -0
  70. package/lib/app/run-env-compose.js +30 -42
  71. package/lib/app/run-helpers.js +49 -126
  72. package/lib/app/run-infra-requirements.js +30 -0
  73. package/lib/app/run-resolve-image.js +21 -0
  74. package/lib/app/run.js +74 -21
  75. package/lib/app/show-display.js +44 -1
  76. package/lib/app/show.js +93 -9
  77. package/lib/build/index.js +13 -10
  78. package/lib/certification/cli-cert-sync-skip.js +21 -0
  79. package/lib/certification/merge-certification-from-artifact.js +185 -0
  80. package/lib/certification/post-unified-cert-sync.js +33 -0
  81. package/lib/certification/sync-after-external-command.js +52 -0
  82. package/lib/certification/sync-system-certification.js +197 -0
  83. package/lib/cli/index.js +2 -0
  84. package/lib/cli/setup-app.help.js +67 -0
  85. package/lib/cli/setup-app.js +61 -121
  86. package/lib/cli/setup-app.test-commands.js +195 -0
  87. package/lib/cli/setup-auth.js +19 -5
  88. package/lib/cli/setup-credential-deployment.js +22 -8
  89. package/lib/cli/setup-dev-path-commands.js +124 -0
  90. package/lib/cli/setup-dev.js +170 -113
  91. package/lib/cli/setup-environment.js +7 -1
  92. package/lib/cli/setup-external-system.js +84 -23
  93. package/lib/cli/setup-infra.js +126 -47
  94. package/lib/cli/setup-parameters.js +32 -0
  95. package/lib/cli/setup-secrets.js +137 -18
  96. package/lib/cli/setup-service-user.js +1 -1
  97. package/lib/cli/setup-utility.js +54 -22
  98. package/lib/commands/app-down.js +5 -7
  99. package/lib/commands/app-install.js +14 -7
  100. package/lib/commands/app-logs.js +13 -10
  101. package/lib/commands/app-shell.js +4 -1
  102. package/lib/commands/app-test.js +25 -19
  103. package/lib/commands/app.js +32 -11
  104. package/lib/commands/auth-config.js +6 -6
  105. package/lib/commands/auth-status.js +4 -3
  106. package/lib/commands/credential-env.js +4 -3
  107. package/lib/commands/credential-list.js +5 -4
  108. package/lib/commands/credential-push.js +4 -3
  109. package/lib/commands/datasource-unified-test-cli.js +428 -0
  110. package/lib/commands/datasource-unified-test-cli.options.js +191 -0
  111. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +106 -0
  112. package/lib/commands/datasource-validation-cli.js +143 -0
  113. package/lib/commands/datasource.js +125 -95
  114. package/lib/commands/deployment-list.js +6 -5
  115. package/lib/commands/dev-cli-handlers.js +122 -18
  116. package/lib/commands/dev-down.js +4 -3
  117. package/lib/commands/dev-init.js +231 -116
  118. package/lib/commands/dev-show-display.js +473 -0
  119. package/lib/commands/login-credentials.js +3 -2
  120. package/lib/commands/login-device.js +4 -3
  121. package/lib/commands/login.js +5 -4
  122. package/lib/commands/logout.js +8 -7
  123. package/lib/commands/parameters-validate.js +54 -0
  124. package/lib/commands/repair-datasource.js +314 -68
  125. package/lib/commands/repair-env-template.js +2 -2
  126. package/lib/commands/repair.js +21 -3
  127. package/lib/commands/secrets-list.js +23 -12
  128. package/lib/commands/secrets-remove-all.js +220 -0
  129. package/lib/commands/secrets-remove.js +21 -12
  130. package/lib/commands/secrets-set.js +21 -12
  131. package/lib/commands/secrets-validate.js +4 -4
  132. package/lib/commands/secure.js +10 -9
  133. package/lib/commands/service-user.js +26 -25
  134. package/lib/commands/test-e2e-external.js +27 -1
  135. package/lib/commands/up-common.js +3 -2
  136. package/lib/commands/up-dataplane.js +29 -16
  137. package/lib/commands/up-miso.js +19 -29
  138. package/lib/commands/upload.js +149 -39
  139. package/lib/commands/wizard-core-helpers.js +1 -1
  140. package/lib/commands/wizard-dataplane.js +4 -3
  141. package/lib/commands/wizard-helpers.js +3 -3
  142. package/lib/commands/wizard.js +2 -2
  143. package/lib/core/admin-secrets.js +14 -5
  144. package/lib/core/audit-logger.js +12 -4
  145. package/lib/core/config-attach-extensions.js +46 -0
  146. package/lib/core/config-runtime-paths.js +29 -0
  147. package/lib/core/config.js +55 -56
  148. package/lib/core/diff.js +3 -2
  149. package/lib/core/ensure-encryption-key.js +1 -1
  150. package/lib/core/secrets-ensure-infra.js +77 -0
  151. package/lib/core/secrets-ensure.js +120 -64
  152. package/lib/core/secrets-env-write.js +35 -7
  153. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  154. package/lib/core/secrets.js +200 -37
  155. package/lib/core/templates-env.js +4 -3
  156. package/lib/datasource/abac-validator.js +1 -10
  157. package/lib/datasource/deploy.js +75 -53
  158. package/lib/datasource/field-reference-validator.js +9 -6
  159. package/lib/datasource/integration-context.js +63 -0
  160. package/lib/datasource/list.js +8 -7
  161. package/lib/datasource/log-viewer.js +189 -67
  162. package/lib/datasource/resolve-app.js +4 -4
  163. package/lib/datasource/test-e2e.js +113 -146
  164. package/lib/datasource/test-integration.js +114 -122
  165. package/lib/datasource/unified-validation-run-body.js +68 -0
  166. package/lib/datasource/unified-validation-run-post.js +23 -0
  167. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  168. package/lib/datasource/unified-validation-run.js +93 -0
  169. package/lib/datasource/validate.js +157 -13
  170. package/lib/deployment/deployer.js +4 -3
  171. package/lib/deployment/environment.js +7 -6
  172. package/lib/deployment/push.js +17 -8
  173. package/lib/external-system/delete.js +4 -3
  174. package/lib/external-system/deploy.js +166 -53
  175. package/lib/external-system/download-helpers.js +1 -1
  176. package/lib/external-system/download.js +7 -6
  177. package/lib/external-system/generator.js +92 -6
  178. package/lib/external-system/integration-test-dispatch.js +26 -0
  179. package/lib/external-system/test-execution.js +5 -1
  180. package/lib/external-system/test-helpers.js +0 -4
  181. package/lib/external-system/test-system-level-helpers.js +110 -0
  182. package/lib/external-system/test-system-level.js +83 -44
  183. package/lib/external-system/test.js +59 -8
  184. package/lib/generator/builders.js +23 -11
  185. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  186. package/lib/generator/external.js +16 -4
  187. package/lib/generator/helpers.js +58 -3
  188. package/lib/generator/index.js +4 -0
  189. package/lib/generator/split-readme.js +12 -7
  190. package/lib/generator/split-variables.js +2 -1
  191. package/lib/generator/split.js +1 -1
  192. package/lib/generator/wizard-readme.js +3 -3
  193. package/lib/generator/wizard.js +8 -8
  194. package/lib/infrastructure/compose.js +70 -7
  195. package/lib/infrastructure/helpers-docker-check.js +67 -0
  196. package/lib/infrastructure/helpers.js +203 -42
  197. package/lib/infrastructure/index.js +31 -18
  198. package/lib/infrastructure/services.js +21 -67
  199. package/lib/internal/fs-real-sync.js +104 -0
  200. package/lib/internal/node-fs.js +98 -0
  201. package/lib/parameters/database-secret-values.js +173 -0
  202. package/lib/parameters/infra-kv-discovery.js +121 -0
  203. package/lib/parameters/infra-parameter-catalog.js +458 -0
  204. package/lib/parameters/infra-parameter-validate.js +64 -0
  205. package/lib/schema/application-schema.json +37 -17
  206. package/lib/schema/datasource-test-run.schema.json +493 -0
  207. package/lib/schema/deployment-rules.yaml +102 -63
  208. package/lib/schema/external-datasource.schema.json +1200 -442
  209. package/lib/schema/external-system.schema.json +203 -5
  210. package/lib/schema/flag-map-validation-run.json +31 -0
  211. package/lib/schema/infra-parameter.schema.json +106 -0
  212. package/lib/schema/infra.parameter.yaml +421 -0
  213. package/lib/schema/type/credential-auth-templates.json +40 -0
  214. package/lib/schema/type/document-storage.json +226 -0
  215. package/lib/schema/type/message-service.json +123 -0
  216. package/lib/schema/type/vector-store.json +88 -0
  217. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  218. package/lib/utils/api-error-handler.js +2 -2
  219. package/lib/utils/api.js +77 -17
  220. package/lib/utils/app-register-api.js +3 -2
  221. package/lib/utils/app-register-auth.js +1 -1
  222. package/lib/utils/app-register-config.js +4 -4
  223. package/lib/utils/app-register-display.js +3 -2
  224. package/lib/utils/app-register-validator.js +3 -2
  225. package/lib/utils/app-run-containers.js +26 -22
  226. package/lib/utils/app-scoped-config.js +31 -0
  227. package/lib/utils/app-service-env-from-builder.js +164 -0
  228. package/lib/utils/build-copy.js +1 -1
  229. package/lib/utils/build-helpers.js +20 -20
  230. package/lib/utils/build-resolve-image.js +165 -0
  231. package/lib/utils/cli-layout-chalk.js +8 -0
  232. package/lib/utils/cli-test-layout-chalk.js +267 -0
  233. package/lib/utils/cli-utils.js +88 -11
  234. package/lib/utils/compose-db-passwords.js +138 -0
  235. package/lib/utils/compose-generate-docker-compose.js +216 -0
  236. package/lib/utils/compose-generator.js +197 -291
  237. package/lib/utils/compose-miso-env.js +18 -0
  238. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  239. package/lib/utils/config-paths.js +166 -7
  240. package/lib/utils/config-scoped-resources-preference.js +41 -0
  241. package/lib/utils/configuration-env-resolver.js +11 -8
  242. package/lib/utils/controller-deployment-outcome.js +68 -0
  243. package/lib/utils/credential-display.js +2 -2
  244. package/lib/utils/credential-secrets-env.js +5 -5
  245. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  246. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  247. package/lib/utils/datasource-test-run-certificate-tty.js +82 -0
  248. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  249. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  250. package/lib/utils/datasource-test-run-display.js +459 -0
  251. package/lib/utils/datasource-test-run-exit.js +83 -0
  252. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  253. package/lib/utils/datasource-test-run-report-version.js +51 -0
  254. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  255. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  256. package/lib/utils/datasource-validation-watch.js +266 -0
  257. package/lib/utils/declarative-url-ports.js +47 -0
  258. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  259. package/lib/utils/dev-ca-install.js +185 -23
  260. package/lib/utils/dev-cert-helper.js +266 -17
  261. package/lib/utils/dev-hosts-helper.js +307 -0
  262. package/lib/utils/dev-init-cert-hints.js +37 -0
  263. package/lib/utils/dev-init-health-messages.js +52 -0
  264. package/lib/utils/dev-init-resolve.js +86 -0
  265. package/lib/utils/dev-init-ssh-merge.js +65 -0
  266. package/lib/utils/dev-ssh-config-helper.js +196 -0
  267. package/lib/utils/dev-user-groups.js +93 -0
  268. package/lib/utils/docker-build.js +42 -17
  269. package/lib/utils/docker-exec.js +28 -0
  270. package/lib/utils/docker-manifest-public-port.js +116 -0
  271. package/lib/utils/docker-not-running-hint.js +52 -0
  272. package/lib/utils/docker.js +98 -11
  273. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  274. package/lib/utils/env-config-loader.js +10 -91
  275. package/lib/utils/env-copy.js +19 -10
  276. package/lib/utils/env-map.js +35 -8
  277. package/lib/utils/env-template.js +2 -2
  278. package/lib/utils/environment-scoped-resources.js +144 -0
  279. package/lib/utils/error-formatter.js +92 -13
  280. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  281. package/lib/utils/error-formatters/network-errors.js +2 -1
  282. package/lib/utils/error-formatters/permission-errors.js +2 -1
  283. package/lib/utils/error-formatters/validation-errors.js +2 -1
  284. package/lib/utils/external-readme.js +8 -1
  285. package/lib/utils/external-system-display.js +242 -136
  286. package/lib/utils/external-system-local-test-tty.js +389 -0
  287. package/lib/utils/external-system-readiness-core.js +377 -0
  288. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  289. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  290. package/lib/utils/external-system-readiness-display.js +186 -0
  291. package/lib/utils/external-system-system-test-tty-overview.js +120 -0
  292. package/lib/utils/external-system-system-test-tty.js +417 -0
  293. package/lib/utils/external-system-test-helpers.js +24 -6
  294. package/lib/utils/external-system-validators.js +30 -12
  295. package/lib/utils/health-check-url.js +119 -0
  296. package/lib/utils/health-check.js +59 -25
  297. package/lib/utils/help-builder.js +11 -8
  298. package/lib/utils/image-version.js +4 -8
  299. package/lib/utils/infra-containers.js +4 -7
  300. package/lib/utils/infra-env-defaults.js +162 -0
  301. package/lib/utils/infra-status-display.js +167 -0
  302. package/lib/utils/infra-status.js +16 -8
  303. package/lib/utils/local-secrets.js +3 -4
  304. package/lib/utils/paths.js +148 -47
  305. package/lib/utils/port-resolver.js +10 -23
  306. package/lib/utils/redis-env-scope.js +62 -0
  307. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  308. package/lib/utils/remote-builder-validation.js +99 -0
  309. package/lib/utils/remote-dev-auth.js +117 -21
  310. package/lib/utils/remote-docker-env.js +67 -15
  311. package/lib/utils/remote-secrets-loader.js +13 -4
  312. package/lib/utils/resolve-docker-image-ref.js +124 -0
  313. package/lib/utils/schema-loader.js +22 -9
  314. package/lib/utils/secrets-bash-kv.js +25 -0
  315. package/lib/utils/secrets-generator.js +169 -49
  316. package/lib/utils/secrets-helpers.js +70 -59
  317. package/lib/utils/secrets-kv-scope.js +60 -0
  318. package/lib/utils/secrets-utils.js +32 -38
  319. package/lib/utils/secrets-validation.js +3 -1
  320. package/lib/utils/secrets-yaml-preserve.js +109 -0
  321. package/lib/utils/ssh-key-helper.js +4 -2
  322. package/lib/utils/template-helpers.js +2 -2
  323. package/lib/utils/test-log-writer.js +3 -3
  324. package/lib/utils/token-manager.js +1 -2
  325. package/lib/utils/url-declarative-public-base.js +188 -0
  326. package/lib/utils/url-declarative-resolve-build.js +493 -0
  327. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  328. package/lib/utils/url-declarative-resolve.js +220 -0
  329. package/lib/utils/url-declarative-token-parse.js +74 -0
  330. package/lib/utils/url-declarative-url-flags.js +50 -0
  331. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  332. package/lib/utils/url-public-path-prefix.js +34 -0
  333. package/lib/utils/urls-local-registry.js +220 -0
  334. package/lib/utils/validation-report-tty-kit.js +77 -0
  335. package/lib/utils/validation-run-poll.js +112 -0
  336. package/lib/utils/validation-run-post-retry.js +85 -0
  337. package/lib/utils/validation-run-request.js +116 -0
  338. package/lib/utils/variable-transformer.js +21 -4
  339. package/lib/utils/yaml-preserve.js +33 -14
  340. package/lib/validation/datasource-warnings.js +56 -0
  341. package/lib/validation/env-template-auth.js +1 -1
  342. package/lib/validation/external-manifest-validator.js +27 -7
  343. package/lib/validation/validate-display.js +37 -31
  344. package/lib/validation/validate-external-cert-sync.js +23 -0
  345. package/lib/validation/validate.js +8 -14
  346. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  347. package/lib/validation/validator.js +22 -65
  348. package/lib/validation/wizard-config-validator.js +2 -1
  349. package/package.json +9 -4
  350. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  351. package/scripts/diagnose-cli.js +150 -0
  352. package/scripts/install-local.js +307 -55
  353. package/scripts/pnpm-global-remove.js +48 -0
  354. package/templates/README.md +15 -2
  355. package/templates/applications/dataplane/application.yaml +52 -2
  356. package/templates/applications/dataplane/env.template +79 -17
  357. package/templates/applications/dataplane/rbac.yaml +8 -0
  358. package/templates/applications/keycloak/application.yaml +9 -1
  359. package/templates/applications/keycloak/env.template +15 -6
  360. package/templates/applications/miso-controller/application.yaml +10 -2
  361. package/templates/applications/miso-controller/env.template +42 -12
  362. package/templates/applications/miso-controller/rbac.yaml +5 -0
  363. package/templates/external-system/README.md.hbs +20 -7
  364. package/templates/external-system/deploy.js.hbs +5 -5
  365. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  366. package/templates/infra/compose.yaml.hbs +33 -16
  367. package/templates/infra/servers.json.hbs +3 -1
  368. package/templates/python/docker-compose.hbs +16 -0
  369. package/templates/typescript/docker-compose.hbs +16 -0
  370. package/lib/api/external-test.api.js +0 -111
  371. package/lib/schema/env-config.yaml +0 -60
@@ -11,6 +11,7 @@
11
11
 
12
12
  const chalk = require('chalk');
13
13
  const logger = require('../utils/logger');
14
+ const { truncatePublicKeyPreview } = require('./certification-show-enrich');
14
15
 
15
16
  function logSourceAndHeader(summary) {
16
17
  const isOffline = summary.source === 'offline';
@@ -81,7 +82,7 @@ function logApplicationExternalIntegration(ei, options = {}) {
81
82
  logger.log(` systems: [${(ei.systems || []).join(', ')}]`);
82
83
  logger.log(` dataSources: [${(ei.dataSources || []).join(', ')}]`);
83
84
  if (!options.skipHint) {
84
- logger.log(chalk.gray('\n For external system data as on dataplane, run: aifabrix show <appKey> --online or aifabrix app show <appKey>.'));
85
+ logger.log(chalk.gray('\n For external system data as on dataplane, run: aifabrix show <app> --online or aifabrix app show <app>.'));
85
86
  }
86
87
  }
87
88
 
@@ -261,6 +262,47 @@ function displayExternalAppBlock(summary) {
261
262
  }
262
263
  }
263
264
 
265
+ /**
266
+ * Local certification + optional verify rows (external integrations).
267
+ * @param {Object} summary
268
+ */
269
+ function logCertificationSection(summary) {
270
+ if (!summary.isExternal) return;
271
+ logger.log('');
272
+ logger.log('🪪 Certification (local system file)');
273
+ const c = summary.localCertification;
274
+ if (!c || typeof c !== 'object') {
275
+ logger.log(' (none or unreadable)');
276
+ } else {
277
+ logger.log(` enabled: ${c.enabled}`);
278
+ logger.log(` algorithm: ${c.algorithm ?? '—'}`);
279
+ logger.log(` issuer: ${c.issuer ?? '—'}`);
280
+ logger.log(` version: ${c.version ?? '—'}`);
281
+ logger.log(` publicKey: ${truncatePublicKeyPreview(c.publicKey, 64)}`);
282
+ }
283
+ if (summary.certificationVerifyRows && summary.certificationVerifyRows.length > 0) {
284
+ logger.log('');
285
+ logger.log('🪪 Certification verify (dataplane)');
286
+ summary.certificationVerifyRows.forEach((row) => {
287
+ if (row.error) {
288
+ logger.log(` • ${row.datasourceKey}: ${row.error}`);
289
+ return;
290
+ }
291
+ const ok = row.overallValid ? chalk.green('ok') : chalk.yellow('not ok');
292
+ logger.log(
293
+ ` • ${row.datasourceKey}: ${ok} signature=${row.validSignature} hash=${row.validHash}`
294
+ );
295
+ if (row.reasons && row.reasons.length) {
296
+ logger.log(chalk.gray(` reasons: ${row.reasons.join('; ')}`));
297
+ }
298
+ });
299
+ } else if (summary.certificationVerifySkipped) {
300
+ logger.log(chalk.gray('\n Certification verify skipped (not logged in or no controller URL).'));
301
+ } else if (summary.certificationVerifyError) {
302
+ logger.log(chalk.yellow(`\n Certification verify error: ${summary.certificationVerifyError}`));
303
+ }
304
+ }
305
+
264
306
  /**
265
307
  * Format and print human-readable show output (offline or online).
266
308
  * @param {Object} summary - Unified summary (buildOfflineSummaryFromDeployJson or buildOnlineSummary)
@@ -278,6 +320,7 @@ function display(summary, options = {}) {
278
320
  }
279
321
  logApplicationSection(ctx.a, summary);
280
322
  if (summary.isExternal) displayExternalAppBlock(summary);
323
+ if (summary.isExternal) logCertificationSection(summary);
281
324
  logRolesSection(ctx.roles);
282
325
  logAuthSection(ctx.authentication);
283
326
  logConfigurationsSection(ctx.portalInputConfigurations);
package/lib/app/show.js CHANGED
@@ -32,6 +32,11 @@ const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
32
32
  const { formatApiError } = require('../utils/api-error-handler');
33
33
  const { formatAuthenticationError } = require('../utils/error-formatters/http-status-errors');
34
34
  const { display: displayShow } = require('./show-display');
35
+ const {
36
+ attachLocalCertification,
37
+ attachCertificationVerifyFromDataplane,
38
+ sanitizeCertificationForJson
39
+ } = require('./certification-show-enrich');
35
40
 
36
41
  /** Truncate deployment key for display */
37
42
  const DEPLOYMENT_KEY_TRUNCATE_LEN = 12;
@@ -273,6 +278,69 @@ async function getShowAuthToken(controllerUrl, config) {
273
278
  throw new Error('Authentication required for --online. Run aifabrix login.');
274
279
  }
275
280
 
281
+ /**
282
+ * Optional dataplane certificate verify rows on the show summary (external only).
283
+ * @param {Object} summary
284
+ * @param {string} appKey
285
+ * @param {boolean} verifyCert
286
+ * @param {{ token: string, controllerUrl: string }|null} [authBundleOptional]
287
+ */
288
+ async function maybeAttachCertificationVerify(summary, appKey, verifyCert, authBundleOptional) {
289
+ if (!verifyCert || !summary.isExternal) return;
290
+ if (authBundleOptional && authBundleOptional.token && authBundleOptional.controllerUrl) {
291
+ await attachCertificationVerifyFromDataplane(summary, appKey, {
292
+ token: authBundleOptional.token,
293
+ controllerUrl: authBundleOptional.controllerUrl
294
+ });
295
+ return;
296
+ }
297
+ try {
298
+ const controllerUrl = await resolveControllerUrl();
299
+ if (!controllerUrl) {
300
+ summary.certificationVerifySkipped = true;
301
+ return;
302
+ }
303
+ const config = await getConfig();
304
+ const authResult = await getShowAuthToken(controllerUrl, config);
305
+ await attachCertificationVerifyFromDataplane(summary, appKey, {
306
+ token: authResult.token,
307
+ controllerUrl: authResult.actualControllerUrl
308
+ });
309
+ } catch {
310
+ summary.certificationVerifySkipped = true;
311
+ }
312
+ }
313
+
314
+ /**
315
+ * @param {Object} out - JSON payload (mutated)
316
+ * @param {Object} summary
317
+ */
318
+ function appendCertificationJsonFields(out, summary) {
319
+ if (!summary.isExternal) return;
320
+ out.localCertification = sanitizeCertificationForJson(summary.localCertification);
321
+ if (summary.certificationVerifyRows) {
322
+ out.certificationVerify = summary.certificationVerifyRows;
323
+ }
324
+ if (summary.certificationVerifySkipped) {
325
+ out.certificationVerifySkipped = true;
326
+ }
327
+ if (summary.certificationVerifyError) {
328
+ out.certificationVerifyError = summary.certificationVerifyError;
329
+ }
330
+ }
331
+
332
+ /**
333
+ * @param {Object} summary
334
+ * @param {string} appKey
335
+ * @param {boolean} verifyCert
336
+ * @param {{ token: string, controllerUrl: string }|null} [authBundleOptional]
337
+ */
338
+ async function enrichExternalShowSummary(summary, appKey, verifyCert, authBundleOptional) {
339
+ if (!summary.isExternal) return;
340
+ attachLocalCertification(summary, appKey);
341
+ await maybeAttachCertificationVerify(summary, appKey, verifyCert, authBundleOptional);
342
+ }
343
+
276
344
  async function fetchOpenApiLists(dataplaneUrl, appKey, authConfig) {
277
345
  let openapiFiles = [];
278
346
  let openapiEndpoints = [];
@@ -440,7 +508,7 @@ function formatBuildForDisplay(build) {
440
508
  if (!build) return '—';
441
509
  const parts = [];
442
510
  if (build.language) parts.push(build.language);
443
- const port = build.port ?? build.localPort;
511
+ const port = build.port;
444
512
  if (port !== undefined && port !== null) parts.push(`port ${port}`);
445
513
  if (build.dockerfile) parts.push('dockerfile');
446
514
  if (build.envOutputPath) parts.push(`envOutputPath: ${build.envOutputPath}`);
@@ -566,22 +634,25 @@ function buildOnlineSummary(apiApp, controllerUrl, externalSystem) {
566
634
  * @param {string} appKey - Application key
567
635
  * @param {boolean} json - Output as JSON
568
636
  * @param {boolean} [permissionsOnly] - When true, output only permissions
637
+ * @param {boolean} [verifyCert] - When true, attempt dataplane verify for external apps
569
638
  * @throws {Error} If application config not found or invalid
570
639
  */
571
- async function runOffline(appKey, json, permissionsOnly) {
572
- let summary;
573
-
640
+ async function loadOfflineShowSummary(appKey) {
574
641
  try {
575
642
  const { deployment, appPath } = await generator.buildDeploymentManifestInMemory(appKey);
576
643
  const sourcePath = path.relative(process.cwd(), appPath) || appPath;
577
- summary = buildOfflineSummaryFromDeployJson(deployment, sourcePath);
644
+ return buildOfflineSummaryFromDeployJson(deployment, sourcePath);
578
645
  } catch (_err) {
579
646
  const { appPath } = await detectAppType(appKey);
580
647
  const configPath = resolveApplicationConfigPath(appPath);
581
648
  const variables = loadVariablesFromPath(appPath);
582
649
  const sourcePath = path.relative(process.cwd(), configPath) || configPath;
583
- summary = buildOfflineSummary(variables, sourcePath);
650
+ return buildOfflineSummary(variables, sourcePath);
584
651
  }
652
+ }
653
+
654
+ async function runOffline(appKey, json, permissionsOnly, verifyCert = false) {
655
+ const summary = await loadOfflineShowSummary(appKey);
585
656
 
586
657
  if (json) {
587
658
  if (permissionsOnly) {
@@ -607,9 +678,14 @@ async function runOffline(appKey, json, permissionsOnly) {
607
678
  databases: summary.databases
608
679
  }
609
680
  };
681
+ if (summary.isExternal) {
682
+ await enrichExternalShowSummary(summary, appKey, verifyCert, null);
683
+ appendCertificationJsonFields(out, summary);
684
+ }
610
685
  logger.log(JSON.stringify(out, null, 2));
611
686
  return;
612
687
  }
688
+ await enrichExternalShowSummary(summary, appKey, verifyCert, null);
613
689
  displayShow(summary, { permissionsOnly: !!permissionsOnly });
614
690
  }
615
691
 
@@ -680,6 +756,7 @@ function outputOnlineJson(summary, permissionsOnly) {
680
756
  ? { error: summary.externalSystem.error }
681
757
  : summary.externalSystem;
682
758
  }
759
+ appendCertificationJsonFields(out, summary);
683
760
  logger.log(JSON.stringify(out, null, 2));
684
761
  }
685
762
 
@@ -688,9 +765,10 @@ function outputOnlineJson(summary, permissionsOnly) {
688
765
  * @param {string} appKey - Application key
689
766
  * @param {boolean} json - Output as JSON
690
767
  * @param {boolean} [permissionsOnly] - When true, output only permissions
768
+ * @param {boolean} [verifyCert] - When true, attempt dataplane verify for external apps
691
769
  * @throws {Error} On auth failure, 404, or API error
692
770
  */
693
- async function runOnline(appKey, json, permissionsOnly) {
771
+ async function runOnline(appKey, json, permissionsOnly, verifyCert = false) {
694
772
  const controllerUrl = await resolveControllerUrl();
695
773
  if (!controllerUrl) {
696
774
  throw new Error('Controller URL is required for --online. Run aifabrix login to set the controller URL in config.yaml.');
@@ -705,6 +783,10 @@ async function runOnline(appKey, json, permissionsOnly) {
705
783
  ? await fetchExternalSystemForOnline(controllerUrl, appKey, authConfig)
706
784
  : null;
707
785
  const summary = buildOnlineSummary(apiApp, authResult.actualControllerUrl, externalSystem);
786
+ await enrichExternalShowSummary(summary, appKey, verifyCert, {
787
+ token: authConfig.token,
788
+ controllerUrl: authResult.actualControllerUrl
789
+ });
708
790
  if (json) {
709
791
  outputOnlineJson(summary, permissionsOnly);
710
792
  return;
@@ -720,6 +802,7 @@ async function runOnline(appKey, json, permissionsOnly) {
720
802
  * @param {boolean} [options.online] - Fetch from controller
721
803
  * @param {boolean} [options.json] - Output as JSON
722
804
  * @param {boolean} [options.permissions] - When true, output only permissions (app show --permissions)
805
+ * @param {boolean} [options.verifyCert] - When true, attach dataplane certificate verify rows (external)
723
806
  * @throws {Error} If file missing/invalid (offline) or API/auth error (online)
724
807
  */
725
808
  async function showApp(appKey, options = {}) {
@@ -730,11 +813,12 @@ async function showApp(appKey, options = {}) {
730
813
  const online = Boolean(options.online);
731
814
  const json = Boolean(options.json);
732
815
  const permissions = Boolean(options.permissions);
816
+ const verifyCert = Boolean(options.verifyCert);
733
817
 
734
818
  if (online) {
735
- await runOnline(appKey, json, permissions);
819
+ await runOnline(appKey, json, permissions, verifyCert);
736
820
  } else {
737
- await runOffline(appKey, json, permissions);
821
+ await runOffline(appKey, json, permissions, verifyCert);
738
822
  }
739
823
  }
740
824
 
@@ -1,3 +1,4 @@
1
+ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * AI Fabrix Builder Build Functions
3
4
  *
@@ -127,7 +128,9 @@ async function generateDockerfile(appNameOrPath, language, config, buildConfig =
127
128
 
128
129
  const template = dockerfileUtils.loadDockerfileTemplate(language);
129
130
  const isAppFlag = buildConfig.context === '../..';
130
- const appSourcePath = isAppFlag ? `apps/${appName}/` : '.';
131
+ // Use "./" (not ".") so Dockerfile lines are "COPY ./requirements*.txt" — a bare "."
132
+ // concatenates with "requirements" and becomes ".requirements*.txt", which matches nothing.
133
+ const appSourcePath = isAppFlag ? `apps/${appName}/` : './';
131
134
 
132
135
  const templateVars = {
133
136
  port: config.port || 3000,
@@ -185,11 +188,11 @@ async function generateDockerfile(appNameOrPath, language, config, buildConfig =
185
188
  async function postBuildTasks(appName, buildConfig) {
186
189
  try {
187
190
  const envPath = await secrets.generateEnvFile(appName, buildConfig.secrets, 'docker');
188
- logger.log(chalk.green(`✓ Generated .env file: ${envPath}`));
191
+ logger.log(formatSuccessLine(`Generated .env file: ${envPath}`));
189
192
  // Note: processEnvVariables is already called by generateEnvFile to generate local .env
190
193
  // at the envOutputPath, so we don't need to manually copy the docker .env file
191
194
  } catch (error) {
192
- logger.log(chalk.yellow(`⚠️ Warning: Could not generate .env file: ${error.message}`));
195
+ logger.log(chalk.yellow(`⚠ Warning: Could not generate .env file: ${error.message}`));
193
196
  }
194
197
  }
195
198
 
@@ -229,7 +232,7 @@ async function copyApplicationSourceFiles(appName, devDir) {
229
232
  const appsPath = path.join(process.cwd(), 'apps', appName);
230
233
  if (fsSync.existsSync(appsPath)) {
231
234
  await buildCopy.copyAppSourceFiles(appsPath, devDir);
232
- logger.log(chalk.green(`✓ Copied application source files from apps/${appName}`));
235
+ logger.log(formatSuccessLine(`Copied application source files from apps/${appName}`));
233
236
  return true;
234
237
  }
235
238
  return false;
@@ -253,12 +256,12 @@ async function copyTemplateFilesIfNeeded(devDir, language, buildConfig, options)
253
256
  const projectRoot = getProjectRoot();
254
257
  const templatePath = path.join(projectRoot, 'templates', 'typescript');
255
258
  await buildCopy.copyTemplateFilesToDevDir(templatePath, devDir, detectedLanguage);
256
- logger.log(chalk.green(`✓ Generated application files from ${detectedLanguage} template`));
259
+ logger.log(formatSuccessLine(`Generated application files from ${detectedLanguage} template`));
257
260
  } else if (detectedLanguage === 'python' && !fsSync.existsSync(requirementsPath)) {
258
261
  const projectRoot = getProjectRoot();
259
262
  const templatePath = path.join(projectRoot, 'templates', 'python');
260
263
  await buildCopy.copyTemplateFilesToDevDir(templatePath, devDir, detectedLanguage);
261
- logger.log(chalk.green(`✓ Generated application files from ${detectedLanguage} template`));
264
+ logger.log(formatSuccessLine(`Generated application files from ${detectedLanguage} template`));
262
265
  }
263
266
  }
264
267
 
@@ -268,7 +271,7 @@ async function prepareDevDirectory(appName, buildConfig, options) {
268
271
  const directoryName = idNum === 0 ? 'applications' : `dev-${developerId}`;
269
272
  logger.log(chalk.blue(`Copying files to developer-specific directory (${directoryName})...`));
270
273
  const devDir = await buildCopy.copyBuilderToDevDirectory(appName, developerId);
271
- logger.log(chalk.green(`✓ Files copied to: ${devDir}`));
274
+ logger.log(formatSuccessLine(`Files copied to: ${devDir}`));
272
275
 
273
276
  const { config: appConfig, imageName } = await buildHelpers.loadAndValidateConfig(appName);
274
277
  const effectiveImageName = buildDevImageName(imageName, developerId);
@@ -294,7 +297,7 @@ function prepareBuildContext(buildConfig, devDir) {
294
297
  // Check if context is using old format (../appName) - these are incompatible with dev directory structure
295
298
  if (buildConfig.context && buildConfig.context.startsWith('../') && buildConfig.context !== '../..') {
296
299
  // Old format detected - always use devDir instead
297
- logger.log(chalk.yellow(`⚠️ Warning: Build context uses old format: ${buildConfig.context}`));
300
+ logger.log(chalk.yellow(`⚠ Warning: Build context uses old format: ${buildConfig.context}`));
298
301
  logger.log(chalk.yellow(` Using dev directory instead: ${devDir}`));
299
302
  contextPath = devDir;
300
303
  } else if (buildConfig.context && buildConfig.context !== '../..') {
@@ -353,7 +356,7 @@ async function handleDockerfileGeneration(appName, params, options, buildHelpers
353
356
  language = 'typescript';
354
357
  }
355
358
  if (!hasExistingDockerfile) {
356
- logger.log(chalk.green(`✓ Detected language: ${language}`));
359
+ logger.log(formatSuccessLine(`Detected language: ${language}`));
357
360
  }
358
361
 
359
362
  // Determine Dockerfile (needs context path to generate in correct location)
@@ -425,7 +428,7 @@ async function buildApp(appName, options = {}) {
425
428
  // 7. Post-build tasks
426
429
  await postBuildTasks(appName, buildConfig);
427
430
 
428
- logger.log(chalk.green('\n✅ Build completed successfully!'));
431
+ logger.log(formatSuccessParagraph('Build completed successfully!'));
429
432
  return `${imageName}:${tag}`;
430
433
 
431
434
  } catch (error) {
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fileoverview Detect skip-cert-sync from Commander + legacy option shapes.
3
+ * Commander registers `--no-cert-sync` as `certSync` defaulting to true; `--no-cert-sync` sets `certSync: false`.
4
+ * @author AI Fabrix Team
5
+ * @version 2.0.0
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ /**
11
+ * @param {Object|null|undefined} options
12
+ * @returns {boolean}
13
+ */
14
+ function cliOptsSkipCertSync(options) {
15
+ if (!options || typeof options !== 'object') return false;
16
+ if (options.noCertSync === true) return true;
17
+ if (options.certSync === false) return true;
18
+ return false;
19
+ }
20
+
21
+ module.exports = { cliOptsSkipCertSync };
@@ -0,0 +1,185 @@
1
+ /**
2
+ * @fileoverview Map dataplane certificate artifacts into **certification** (external-system.schema.json).
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ /**
10
+ * @param {unknown} id - certificateId field (string or FK-shaped object)
11
+ * @returns {string}
12
+ */
13
+ function certificateIdToString(id) {
14
+ if (id === undefined || id === null) return '';
15
+ if (typeof id === 'string') return id.trim();
16
+ if (typeof id === 'object' && id !== null && typeof id.id === 'string') return String(id.id).trim();
17
+ return String(id).trim();
18
+ }
19
+
20
+ /**
21
+ * @param {string|undefined|null} s
22
+ * @returns {string}
23
+ */
24
+ function trimOrEmpty(s) {
25
+ return s !== undefined && s !== null ? String(s).trim() : '';
26
+ }
27
+
28
+ /**
29
+ * Prefer an artifact that includes PEM/JWK **publicKey** material for verify-publish.
30
+ * @param {import('../api/types/certificates.types').CertificateArtifactResponse[]} artifacts
31
+ * @returns {import('../api/types/certificates.types').CertificateArtifactResponse|null}
32
+ */
33
+ function pickArtifactForCertificationMerge(artifacts) {
34
+ const list = Array.isArray(artifacts) ? artifacts.filter((a) => a && typeof a === 'object') : [];
35
+ if (list.length === 0) return null;
36
+ const withKey = list.find((a) => a.publicKey && String(a.publicKey).trim());
37
+ return withKey || list[0];
38
+ }
39
+
40
+ /**
41
+ * @param {string} algorithmUpper
42
+ * @param {import('../api/types/certificates.types').CertificateArtifactResponse} art
43
+ * @returns {string}
44
+ */
45
+ function hs256DevPublicKeyPlaceholder(algorithmUpper, art) {
46
+ if (algorithmUpper !== 'HS256') return '';
47
+ const cid = certificateIdToString(art.certificateId);
48
+ return `HS256-DEV-NO-PEM:${cid || 'integration-certificate'}`;
49
+ }
50
+
51
+ /**
52
+ * @param {import('../api/types/certificates.types').CertificateArtifactResponse} art
53
+ * @param {Object} ex
54
+ * @returns {string}
55
+ */
56
+ function resolvePublicKey(art, ex) {
57
+ const fromArt = trimOrEmpty(art.publicKey);
58
+ if (fromArt) return fromArt;
59
+ const fromExisting = trimOrEmpty(ex.publicKey);
60
+ if (fromExisting) return fromExisting;
61
+ const algorithmUpper = trimOrEmpty(art.algorithm).toUpperCase();
62
+ return hs256DevPublicKeyPlaceholder(algorithmUpper, art);
63
+ }
64
+
65
+ /**
66
+ * @param {import('../api/types/certificates.types').CertificateArtifactResponse} art
67
+ * @param {Object} ex
68
+ * @returns {string}
69
+ */
70
+ function resolveIssuer(art, ex) {
71
+ return (
72
+ trimOrEmpty(art.licenseLevelIssuer) ||
73
+ trimOrEmpty(art.issuedBy) ||
74
+ trimOrEmpty(ex.issuer) ||
75
+ 'dataplane'
76
+ );
77
+ }
78
+
79
+ /**
80
+ * @param {import('../api/types/certificates.types').CertificateArtifactResponse} art
81
+ * @param {Object} ex
82
+ * @returns {string}
83
+ */
84
+ function resolveVersion(art, ex) {
85
+ return (
86
+ trimOrEmpty(art.version) ||
87
+ trimOrEmpty(art.certificateVersion) ||
88
+ trimOrEmpty(ex.version) ||
89
+ certificateIdToString(art.certificateId)
90
+ );
91
+ }
92
+
93
+ const CERTIFICATION_LEVELS = new Set(['BRONZE', 'SILVER', 'GOLD', 'PLATINUM']);
94
+ const CERTIFICATION_STATUSES = new Set(['passed', 'not_passed', 'pending']);
95
+
96
+ /**
97
+ * @param {string} raw
98
+ * @returns {string}
99
+ */
100
+ function normalizeCertificationLevel(raw) {
101
+ const s = trimOrEmpty(raw).toUpperCase();
102
+ return CERTIFICATION_LEVELS.has(s) ? s : '';
103
+ }
104
+
105
+ /**
106
+ * @param {string} raw
107
+ * @returns {string}
108
+ */
109
+ function normalizeCertificationStatus(raw) {
110
+ const s = trimOrEmpty(raw).toLowerCase();
111
+ return CERTIFICATION_STATUSES.has(s) ? s : '';
112
+ }
113
+
114
+ /**
115
+ * @param {import('../api/types/certificates.types').CertificateArtifactResponse} art
116
+ * @param {Object} ex
117
+ * @returns {string}
118
+ */
119
+ function resolveLevel(art, ex) {
120
+ return (
121
+ normalizeCertificationLevel(ex.level) ||
122
+ normalizeCertificationLevel(art.certificationLevel) ||
123
+ ''
124
+ );
125
+ }
126
+
127
+ /**
128
+ * Prefer existing file status when valid; otherwise **passed** for an active dataplane artifact.
129
+ *
130
+ * @param {import('../api/types/certificates.types').CertificateArtifactResponse} art
131
+ * @param {Object} ex
132
+ * @returns {string}
133
+ */
134
+ function resolveStatus(_art, ex) {
135
+ const fromEx = normalizeCertificationStatus(ex.status);
136
+ if (fromEx) return fromEx;
137
+ return 'passed';
138
+ }
139
+
140
+ /**
141
+ * Build `certification` object matching **external-system.schema.json** (required: enabled, publicKey, algorithm, issuer, version; optional status, level).
142
+ * Fills gaps from `existingCertification` when the artifact omits publishable fields (common when dataplane redacts `publicKey`).
143
+ * For **HS256** dev certificates with no PEM, uses a non-secret placeholder `publicKey` so the system file stays schema-valid.
144
+ *
145
+ * @param {import('../api/types/certificates.types').CertificateArtifactResponse|null} artifact
146
+ * @param {Object|null|undefined} existingCertification - Current `system.certification`
147
+ * @returns {Object|null} Full certification object, or null if **publicKey** or **version** cannot be satisfied
148
+ */
149
+ function buildCertificationFromArtifact(artifact, existingCertification) {
150
+ const ex = existingCertification && typeof existingCertification === 'object' ? existingCertification : {};
151
+ const art = artifact && typeof artifact === 'object' ? artifact : null;
152
+ if (!art) return null;
153
+
154
+ const publicKey = resolvePublicKey(art, ex);
155
+ if (!publicKey) return null;
156
+
157
+ const issuer = resolveIssuer(art, ex);
158
+ if (!issuer) return null;
159
+
160
+ const versionStr = resolveVersion(art, ex);
161
+ if (!versionStr) return null;
162
+
163
+ const algorithmUpper = trimOrEmpty(art.algorithm).toUpperCase();
164
+ const algorithm = algorithmUpper === 'HS256' ? 'HS256' : 'RS256';
165
+
166
+ const out = {
167
+ enabled: true,
168
+ publicKey,
169
+ algorithm,
170
+ issuer,
171
+ version: versionStr,
172
+ status: resolveStatus(art, ex)
173
+ };
174
+ const level = resolveLevel(art, ex);
175
+ if (level) {
176
+ out.level = level;
177
+ }
178
+ return out;
179
+ }
180
+
181
+ module.exports = {
182
+ buildCertificationFromArtifact,
183
+ pickArtifactForCertificationMerge,
184
+ certificateIdToString
185
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @fileoverview After successful unified datasource validation, optionally sync system certification.
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const chalk = require('chalk');
10
+ const logger = require('../utils/logger');
11
+ const { trySyncCertificationFromDataplaneForExternalApp } = require('./sync-after-external-command');
12
+ const { cliOptsSkipCertSync } = require('./cli-cert-sync-skip');
13
+
14
+ /**
15
+ * @async
16
+ * @param {number} exitCode
17
+ * @param {string} datasourceKey
18
+ * @param {Object} options - CLI flags (app, noCertSync)
19
+ * @param {string} label - Log label
20
+ * @returns {Promise<void>}
21
+ */
22
+ async function afterUnifiedValidationCertSync(exitCode, datasourceKey, options, label) {
23
+ if (exitCode !== 0 || cliOptsSkipCertSync(options)) return;
24
+ try {
25
+ const { resolveAppKeyForDatasource } = require('../datasource/resolve-app');
26
+ const { appKey } = await resolveAppKeyForDatasource(datasourceKey, options.app);
27
+ await trySyncCertificationFromDataplaneForExternalApp(appKey, label);
28
+ } catch (e) {
29
+ logger.log(chalk.yellow(`⚠ Certification sync (${label}) skipped: ${e.message}`));
30
+ }
31
+ }
32
+
33
+ module.exports = { afterUnifiedValidationCertSync };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @fileoverview Optional certification sync after external flows (validate, tests).
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const chalk = require('chalk');
10
+ const logger = require('../utils/logger');
11
+
12
+ /**
13
+ * After a successful external integration flow, refresh `certification` on the primary system file from dataplane.
14
+ * Best-effort: skips non-external apps, missing Bearer token, or resolver errors (warn only).
15
+ *
16
+ * @async
17
+ * @param {string} appKey - Integration / system key
18
+ * @param {string} label - Short label for logs (e.g. "validate", "datasource test")
19
+ * @returns {Promise<void>}
20
+ */
21
+ async function trySyncCertificationFromDataplaneForExternalApp(appKey, label) {
22
+ try {
23
+ const { detectAppType } = require('../utils/paths');
24
+ const t = await detectAppType(appKey).catch(() => null);
25
+ if (!t || !t.isExternal) return;
26
+
27
+ const { resolveDataplaneAndAuth, validateSystemKeyFormat } = require('../commands/upload');
28
+ const { generateControllerManifest } = require('../generator/external-controller-manifest');
29
+ const { maybeSyncSystemCertificationFromDataplane } = require('./sync-system-certification');
30
+
31
+ validateSystemKeyFormat(appKey);
32
+ const { dataplaneUrl, authConfig } = await resolveDataplaneAndAuth(appKey);
33
+ if (!authConfig.token) {
34
+ logger.log(chalk.gray(`Certification sync (${label}) skipped: no Bearer token (run aifabrix login).`));
35
+ return;
36
+ }
37
+ const manifest = await generateControllerManifest(appKey, { type: 'external' });
38
+ const dsKeys = (manifest.dataSources || []).map((ds) => ds && ds.key).filter(Boolean);
39
+ await maybeSyncSystemCertificationFromDataplane({
40
+ label,
41
+ noCertSync: false,
42
+ systemKey: manifest.key,
43
+ dataplaneUrl,
44
+ authConfig,
45
+ datasourceKeys: dsKeys
46
+ });
47
+ } catch (e) {
48
+ logger.log(chalk.yellow(`⚠ Certification sync (${label}) skipped: ${e.message}`));
49
+ }
50
+ }
51
+
52
+ module.exports = { trySyncCertificationFromDataplaneForExternalApp };