@aifabrix/builder 2.45.5 → 2.47.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 (355) hide show
  1. package/.cursor/rules/cli-layout.mdc +2 -0
  2. package/.cursor/rules/cli-product-neutral.mdc +43 -0
  3. package/.cursor/rules/project-rules.mdc +1 -0
  4. package/.npmrc.token +1 -0
  5. package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
  6. package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
  7. package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
  8. package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
  9. package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
  10. package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
  11. package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
  12. package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
  13. package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
  14. package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
  15. package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
  16. package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
  17. package/.temp/validation/summary.txt +5 -0
  18. package/README.md +43 -17
  19. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  20. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  21. package/jest.config.default.js +2 -1
  22. package/jest.config.isolated.js +2 -1
  23. package/jest.global-live-fabrix-hooks.js +36 -0
  24. package/jest.isolated-projects.js +146 -0
  25. package/jest.projects.js +65 -136
  26. package/lib/api/api-response-errors.js +124 -0
  27. package/lib/api/dataplane-health.api.js +19 -7
  28. package/lib/api/governance-scenario-pack.api.js +159 -0
  29. package/lib/api/index.js +43 -3
  30. package/lib/api/lifecycle.api.js +98 -0
  31. package/lib/api/pipeline.api.js +3 -1
  32. package/lib/api/records-bulk.api.js +14 -1
  33. package/lib/api/resource-types.api.js +107 -0
  34. package/lib/api/runtime-configuration.api.js +72 -0
  35. package/lib/api/types/resource-types.types.js +45 -0
  36. package/lib/api/user-groups.api.js +9 -9
  37. package/lib/api/users.api.js +8 -8
  38. package/lib/api/validation-runner.js +3 -1
  39. package/lib/api/wizard.api.js +10 -2
  40. package/lib/app/deploy-config.js +4 -1
  41. package/lib/app/down.js +78 -27
  42. package/lib/app/index.js +2 -2
  43. package/lib/app/push.js +4 -5
  44. package/lib/app/register.js +7 -4
  45. package/lib/app/run-env-compose.js +84 -30
  46. package/lib/app/run-resolve-image.js +2 -1
  47. package/lib/app/run-stop-containers.js +113 -0
  48. package/lib/app/run.js +1 -31
  49. package/lib/app/show-display.js +35 -0
  50. package/lib/app/show.js +32 -1
  51. package/lib/build/index.js +22 -5
  52. package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
  53. package/lib/certification/sync-system-certification.js +13 -6
  54. package/lib/channels/add-channel.js +348 -0
  55. package/lib/channels/channel-artifact-generator.js +141 -0
  56. package/lib/channels/channel-audit.js +101 -0
  57. package/lib/channels/channel-auth-context.js +37 -0
  58. package/lib/channels/channel-credential.js +200 -0
  59. package/lib/channels/channel-display-name.js +64 -0
  60. package/lib/channels/channel-external-system.js +106 -0
  61. package/lib/channels/channel-integration-generator.js +274 -0
  62. package/lib/channels/channel-probe.js +89 -0
  63. package/lib/channels/channel-runtime-resolver.js +152 -0
  64. package/lib/channels/channel-status-display.js +141 -0
  65. package/lib/channels/channel-targets.js +115 -0
  66. package/lib/cli/index.js +20 -4
  67. package/lib/cli/infra-guided-auth.js +109 -0
  68. package/lib/cli/infra-guided.js +84 -81
  69. package/lib/cli/setup-app.help.js +0 -31
  70. package/lib/cli/setup-app.js +82 -5
  71. package/lib/cli/setup-app.test-commands.js +5 -57
  72. package/lib/cli/setup-auth.js +25 -0
  73. package/lib/cli/setup-certification-commands.js +147 -0
  74. package/lib/cli/setup-certification.help.js +141 -0
  75. package/lib/cli/setup-environment.js +19 -2
  76. package/lib/cli/setup-external-system-help.js +62 -0
  77. package/lib/cli/setup-external-system.js +25 -3
  78. package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
  79. package/lib/cli/setup-infra-up-platform-action.js +2 -1
  80. package/lib/cli/setup-infra.js +9 -1
  81. package/lib/cli/setup-platform.js +6 -1
  82. package/lib/cli/setup-secrets.js +33 -1
  83. package/lib/cli/setup-utility-help-after.js +25 -2
  84. package/lib/cli/setup-utility-repair.js +198 -0
  85. package/lib/cli/setup-utility-resolve.js +44 -5
  86. package/lib/cli/setup-utility-validate.js +197 -0
  87. package/lib/cli/setup-utility.js +4 -239
  88. package/lib/commands/app-logs.js +54 -26
  89. package/lib/commands/app.js +41 -0
  90. package/lib/commands/auth-status-client-tokens.js +214 -0
  91. package/lib/commands/auth-status-dataplane-version.js +142 -68
  92. package/lib/commands/auth-status.js +43 -54
  93. package/lib/commands/channel.js +58 -0
  94. package/lib/commands/datasource-test-trust-cli.js +27 -23
  95. package/lib/commands/datasource-unified-test-cli.js +6 -3
  96. package/lib/commands/datasource-unified-test-cli.options.js +5 -1
  97. package/lib/commands/datasource.js +8 -8
  98. package/lib/commands/dev-show-display.js +8 -1
  99. package/lib/commands/governance-cli-shared.js +94 -0
  100. package/lib/commands/governance-command-action.js +132 -0
  101. package/lib/commands/governance-download-external.js +34 -0
  102. package/lib/commands/governance-generate-external.js +71 -0
  103. package/lib/commands/governance-upload-external.js +36 -0
  104. package/lib/commands/governance-verify-external.js +64 -0
  105. package/lib/commands/governance.js +144 -0
  106. package/lib/commands/identity-apply-sync.js +49 -8
  107. package/lib/commands/identity-group-cli.js +66 -26
  108. package/lib/commands/identity-membership-role-cache.js +58 -37
  109. package/lib/commands/identity-shared.js +22 -1
  110. package/lib/commands/identity-user-cli.js +80 -34
  111. package/lib/commands/lifecycle-command-action.js +123 -0
  112. package/lib/commands/lifecycle-external.js +268 -0
  113. package/lib/commands/login.js +11 -2
  114. package/lib/commands/repair-auth-apply.js +153 -0
  115. package/lib/commands/repair-auth-config.js +8 -13
  116. package/lib/commands/repair-changed-files.js +41 -0
  117. package/lib/commands/repair-datasource-com-v2.js +15 -0
  118. package/lib/commands/repair-datasource-keys.js +4 -1
  119. package/lib/commands/repair-datasource-run.js +59 -0
  120. package/lib/commands/repair-datasource-service-contract.js +206 -0
  121. package/lib/commands/repair-datasource-test-payload.js +259 -0
  122. package/lib/commands/repair-datasource.js +49 -64
  123. package/lib/commands/repair-env-template.js +76 -25
  124. package/lib/commands/repair-external-run.js +216 -0
  125. package/lib/commands/repair-openapi-sync.js +49 -4
  126. package/lib/commands/repair-persist.js +4 -0
  127. package/lib/commands/repair-rbac.js +52 -1
  128. package/lib/commands/repair-steps.js +160 -0
  129. package/lib/commands/repair-system-alignment.js +7 -4
  130. package/lib/commands/repair.js +29 -296
  131. package/lib/commands/resource-type.js +371 -0
  132. package/lib/commands/secrets-get.js +184 -0
  133. package/lib/commands/secrets-remove-all.js +3 -1
  134. package/lib/commands/setup-image-refresh.js +146 -0
  135. package/lib/commands/setup-modes.js +158 -162
  136. package/lib/commands/setup-platform-auth.js +76 -0
  137. package/lib/commands/setup-prompts-admin.js +244 -0
  138. package/lib/commands/setup-prompts-platform-mode.js +70 -0
  139. package/lib/commands/setup-prompts-validators.js +36 -0
  140. package/lib/commands/setup-prompts.js +24 -76
  141. package/lib/commands/setup-run-context.js +45 -0
  142. package/lib/commands/setup-spinners.js +39 -0
  143. package/lib/commands/setup.js +27 -5
  144. package/lib/commands/teardown.js +39 -0
  145. package/lib/commands/test-e2e-external.js +24 -8
  146. package/lib/commands/test-governance-external.js +3 -24
  147. package/lib/commands/test-trust-external.js +48 -100
  148. package/lib/commands/up-common.js +57 -13
  149. package/lib/commands/up-dataplane.js +56 -15
  150. package/lib/commands/upload-publish-context.js +75 -0
  151. package/lib/commands/upload-sidecar-followups.js +89 -0
  152. package/lib/commands/upload.js +113 -110
  153. package/lib/commands/verify-governance-combined-external.js +61 -0
  154. package/lib/commands/verify-governance-command-action.js +64 -0
  155. package/lib/commands/verify-governance-db.js +102 -0
  156. package/lib/commands/verify-governance-external.js +77 -0
  157. package/lib/commands/verify-governance-result-builders.js +71 -0
  158. package/lib/commands/verify-operations-command-action.js +68 -0
  159. package/lib/commands/verify-operations-external.js +268 -0
  160. package/lib/commands/verify-operations-persist.js +75 -0
  161. package/lib/commands/verify-operations-step-plan.js +68 -0
  162. package/lib/commands/verify-operations-steps.js +111 -0
  163. package/lib/commands/verify-trust-command-action.js +73 -0
  164. package/lib/commands/verify-trust-external.js +59 -0
  165. package/lib/commands/wizard-config-normalizer.js +43 -23
  166. package/lib/commands/wizard-core.js +2 -0
  167. package/lib/core/admin-password-generator.js +38 -0
  168. package/lib/core/admin-secrets-bundle.js +111 -0
  169. package/lib/core/admin-secrets-env-overlay.js +62 -0
  170. package/lib/core/admin-secrets.js +32 -4
  171. package/lib/core/config-attach-extensions.js +3 -0
  172. package/lib/core/config-platform-controller.js +44 -0
  173. package/lib/core/config-setup-installation-profile.js +42 -0
  174. package/lib/core/config-setup-platform-mode.js +46 -0
  175. package/lib/core/config.js +46 -36
  176. package/lib/core/secrets-admin-env.js +6 -2
  177. package/lib/core/secrets-ensure.js +4 -0
  178. package/lib/core/secrets-env-content.js +54 -48
  179. package/lib/core/secrets-env-write.js +10 -1
  180. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  181. package/lib/core/setup-installation-profile.js +43 -0
  182. package/lib/core/setup-platform-mode.js +90 -0
  183. package/lib/datasource/agent-trust-debug-log.js +1 -1
  184. package/lib/datasource/agent-trust-run.js +5 -3
  185. package/lib/datasource/audit-evidence-extract-debug.js +30 -18
  186. package/lib/datasource/audit-evidence-extract.js +7 -4
  187. package/lib/datasource/audit-evidence-verify-matrix.js +54 -26
  188. package/lib/datasource/bulk-loader-run-batches.js +15 -3
  189. package/lib/datasource/capability/capability-diff-slice.js +2 -2
  190. package/lib/datasource/capability/capability-manifest.js +254 -0
  191. package/lib/datasource/capability/capability-resolve.js +3 -4
  192. package/lib/datasource/capability/copy-operations.js +5 -3
  193. package/lib/datasource/capability/remove-operations.js +2 -1
  194. package/lib/datasource/capability/run-capability-copy.js +32 -4
  195. package/lib/datasource/capability/run-capability-diff.js +3 -3
  196. package/lib/datasource/capability/run-capability-dimension.js +2 -4
  197. package/lib/datasource/capability/run-capability-edit.js +4 -7
  198. package/lib/datasource/capability/run-capability-relate.js +2 -4
  199. package/lib/datasource/capability/run-capability-remove.js +2 -4
  200. package/lib/datasource/capability/validate-capability-slice.js +2 -1
  201. package/lib/datasource/datasource-identifier.js +2 -1
  202. package/lib/datasource/datasource-load-export-context.js +3 -0
  203. package/lib/datasource/datasource-validate-display.js +1 -1
  204. package/lib/datasource/field-reference-validator.js +24 -3
  205. package/lib/datasource/log-cleaner.js +2 -2
  206. package/lib/datasource/log-viewer.js +8 -8
  207. package/lib/datasource/orphan-profiles-validator.js +98 -0
  208. package/lib/datasource/test-e2e.js +4 -1
  209. package/lib/datasource/unified-validation-run-body.js +3 -1
  210. package/lib/datasource/unified-validation-run.js +5 -2
  211. package/lib/datasource/validate.js +41 -23
  212. package/lib/deployment/environment-poll-status.js +138 -0
  213. package/lib/deployment/environment.js +42 -34
  214. package/lib/external-system/download-helpers.js +87 -0
  215. package/lib/external-system/download.js +78 -138
  216. package/lib/external-system/generator.js +1 -55
  217. package/lib/external-system/integration-auth-context.js +45 -0
  218. package/lib/external-system/test-auth.js +12 -5
  219. package/lib/external-system/test.js +16 -21
  220. package/lib/generator/builders.js +11 -1
  221. package/lib/generator/external-controller-manifest.js +18 -0
  222. package/lib/generator/split.js +3 -1
  223. package/lib/generator/wizard.js +1 -28
  224. package/lib/governance/governance-cli-errors.js +96 -0
  225. package/lib/governance/governance-download-display.js +38 -0
  226. package/lib/governance/governance-run-display.js +215 -25
  227. package/lib/identity/fk-reference.js +65 -0
  228. package/lib/identity/identity-apply-core.js +13 -58
  229. package/lib/identity/identity-apply-phases.js +5 -1
  230. package/lib/identity/identity-display.js +243 -0
  231. package/lib/identity/identity-response-helpers.js +52 -0
  232. package/lib/identity/identity-sync-errors.js +62 -0
  233. package/lib/identity/resolve-user-id.js +129 -0
  234. package/lib/identity/username-default.js +31 -0
  235. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  236. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  237. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  238. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  239. package/lib/infrastructure/helpers.js +23 -129
  240. package/lib/infrastructure/index.js +5 -6
  241. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  242. package/lib/lifecycle/certification-progress.js +231 -0
  243. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  244. package/lib/lifecycle/exit-codes.js +24 -0
  245. package/lib/lifecycle/json-envelope.js +25 -0
  246. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  247. package/lib/lifecycle/logger-quiet.js +29 -0
  248. package/lib/lifecycle/open-api-published.js +59 -0
  249. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  250. package/lib/lifecycle/product-model.js +196 -0
  251. package/lib/lifecycle/report-display-trust.js +70 -0
  252. package/lib/lifecycle/report-display.js +472 -0
  253. package/lib/lifecycle/scenario-upload.js +191 -0
  254. package/lib/lifecycle/verify-step-progress.js +87 -0
  255. package/lib/parameters/infra-kv-discovery.js +27 -2
  256. package/lib/protection/auth-context.js +10 -4
  257. package/lib/resolvers/datasource-resolver.js +2 -3
  258. package/lib/resolvers/resource-type-file.js +159 -0
  259. package/lib/schema/application-schema.json +17 -1
  260. package/lib/schema/datasource-test-run.schema.json +89 -6
  261. package/lib/schema/deployment-rules.yaml +2 -1
  262. package/lib/schema/external-datasource.schema.json +2947 -2619
  263. package/lib/schema/external-system.schema.json +1418 -761
  264. package/lib/schema/infra.parameter.yaml +45 -0
  265. package/lib/schema/type/credential-field-registry.json +103 -97
  266. package/lib/schema/type/document-storage.json +9 -5
  267. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  268. package/lib/utils/api.js +55 -31
  269. package/lib/utils/app-relative-path-env.js +112 -0
  270. package/lib/utils/app-run-containers.js +25 -2
  271. package/lib/utils/authentication-shape.js +224 -0
  272. package/lib/utils/build-copy.js +36 -5
  273. package/lib/utils/build-resolve-image.js +66 -12
  274. package/lib/utils/celery-companion-apps.js +53 -0
  275. package/lib/utils/cli-secrets-error-format.js +83 -21
  276. package/lib/utils/cli-utils.js +20 -4
  277. package/lib/utils/compose-celery-worker.js +23 -0
  278. package/lib/utils/compose-generate-docker-compose.js +13 -1
  279. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  280. package/lib/utils/config-secrets-path.js +46 -0
  281. package/lib/utils/configuration-env-resolver.js +36 -0
  282. package/lib/utils/controller-url.js +88 -1
  283. package/lib/utils/credential-secrets-env-kv.js +216 -0
  284. package/lib/utils/credential-secrets-env.js +175 -257
  285. package/lib/utils/dataplane-resolver.js +18 -2
  286. package/lib/utils/declarative-url-ports.js +27 -4
  287. package/lib/utils/deployment-auth-mode.js +41 -0
  288. package/lib/utils/developer-volume-id.js +33 -0
  289. package/lib/utils/docker-build.js +1 -4
  290. package/lib/utils/env-copy.js +14 -9
  291. package/lib/utils/env-kv-orphan-lines.js +43 -0
  292. package/lib/utils/error-formatters/network-errors.js +13 -4
  293. package/lib/utils/external-env-template.js +11 -3
  294. package/lib/utils/external-system-readiness-core.js +10 -0
  295. package/lib/utils/external-system-readiness-display.js +80 -23
  296. package/lib/utils/external-system-validators.js +8 -2
  297. package/lib/utils/health-check.js +4 -1
  298. package/lib/utils/help-builder.js +13 -8
  299. package/lib/utils/keycloak-realm-ready.js +101 -0
  300. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  301. package/lib/utils/manifest-source-emit.js +2 -0
  302. package/lib/utils/paths.js +25 -0
  303. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  304. package/lib/utils/platform-controller-url.js +157 -0
  305. package/lib/utils/platform-kv-ref.js +57 -0
  306. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  307. package/lib/utils/postgres-wipe.js +9 -2
  308. package/lib/utils/resolve-env-mode.js +47 -0
  309. package/lib/utils/runtime-configuration-download.js +189 -0
  310. package/lib/utils/runtime-configuration-values.js +119 -0
  311. package/lib/utils/schema-loader.js +19 -4
  312. package/lib/utils/secrets-generator.js +31 -25
  313. package/lib/utils/secrets-missing-error.js +164 -0
  314. package/lib/utils/service-url-label.js +81 -0
  315. package/lib/utils/setup-quiet-output.js +25 -0
  316. package/lib/utils/template-helpers.js +15 -0
  317. package/lib/utils/token-manager-client-credentials.js +201 -0
  318. package/lib/utils/token-manager-messages.js +8 -0
  319. package/lib/utils/token-manager-refresh.js +12 -2
  320. package/lib/utils/token-manager.js +86 -105
  321. package/lib/utils/upload-pipeline-ui.js +12 -5
  322. package/lib/utils/upload-sync-options.js +42 -0
  323. package/lib/utils/url-declarative-public-base.js +21 -3
  324. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  325. package/lib/utils/validation-poll-ui.js +8 -0
  326. package/lib/utils/validation-run-stage.js +45 -0
  327. package/lib/utils/worker-sidecars.js +142 -0
  328. package/lib/validation/external-manifest-validator.js +2 -0
  329. package/lib/validation/runtime-configuration-rules.js +98 -0
  330. package/lib/validation/validate-display-dimensions.js +154 -0
  331. package/lib/validation/validate-display-sections.js +276 -0
  332. package/lib/validation/validate-display-steps.js +67 -0
  333. package/lib/validation/validate-display.js +24 -402
  334. package/package.json +1 -1
  335. package/templates/applications/dataplane/application.yaml +7 -1
  336. package/templates/applications/dataplane/env.template +50 -14
  337. package/templates/applications/dataplane/rbac.yaml +108 -1
  338. package/templates/applications/miso-controller/application.yaml +2 -2
  339. package/templates/applications/miso-controller/env.template +53 -122
  340. package/templates/applications/miso-controller/rbac.yaml +28 -13
  341. package/templates/channels/chatgpt/install.template.md.hbs +49 -0
  342. package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
  343. package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
  344. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
  345. package/templates/channels/slack/install.template.md.hbs +49 -0
  346. package/templates/channels/slack/manifest.template.json.hbs +66 -0
  347. package/templates/channels/teams-bot/install.template.md.hbs +49 -0
  348. package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
  349. package/templates/channels/web-embed/install.template.md.hbs +49 -0
  350. package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
  351. package/templates/external-system/README.md.hbs +74 -17
  352. package/templates/python/docker-compose.hbs +75 -18
  353. package/templates/typescript/docker-compose.hbs +70 -17
  354. package/lib/commands/test-governance-command-action.js +0 -55
  355. package/lib/commands/test-trust-command-action.js +0 -118
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Schema-aware testPayload repair (payloadTemplate / expectedResult).
3
+ *
4
+ * @fileoverview Helpers for repair-datasource testPayload generation
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * @param {string|string[]|undefined} type - JSON Schema type
13
+ * @returns {string}
14
+ */
15
+ function primarySchemaType(type) {
16
+ if (Array.isArray(type)) {
17
+ const nonNull = type.filter(t => t !== 'null');
18
+ return nonNull[0] || 'string';
19
+ }
20
+ return type || 'string';
21
+ }
22
+
23
+ /**
24
+ * @param {Object|null|undefined} schema - JSON Schema node
25
+ * @param {string} key - Property name
26
+ * @returns {Object|null}
27
+ */
28
+ function getSchemaProperty(schema, key) {
29
+ if (!schema || typeof schema !== 'object') return null;
30
+ const props = schema.properties;
31
+ if (props && typeof props === 'object' && Object.prototype.hasOwnProperty.call(props, key)) {
32
+ return props[key];
33
+ }
34
+ if (schema.additionalProperties === true) {
35
+ return { type: 'string' };
36
+ }
37
+ if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
38
+ return schema.additionalProperties;
39
+ }
40
+ return null;
41
+ }
42
+
43
+ /**
44
+ * @param {Object|null|undefined} rootSchema - metadataSchema root
45
+ * @param {string[]} pathParts - Dot path under raw payload (no raw. prefix)
46
+ * @returns {Object|null}
47
+ */
48
+ function getSchemaNodeAtPath(rootSchema, pathParts) {
49
+ if (!rootSchema || !pathParts.length) return rootSchema;
50
+ let node = rootSchema;
51
+ for (const part of pathParts) {
52
+ if (!part) continue;
53
+ const next = getSchemaProperty(node, part);
54
+ if (!next) return null;
55
+ node = next;
56
+ }
57
+ return node;
58
+ }
59
+
60
+ /**
61
+ * @param {Object|null|undefined} node - JSON Schema node
62
+ * @returns {*}
63
+ */
64
+ function placeholderFromSchemaNode(node) {
65
+ if (!node || typeof node !== 'object') return '';
66
+ const type = primarySchemaType(node.type);
67
+ if (node.format === 'date-time') return '2025-01-15T10:30:00Z';
68
+ if (node.format === 'date') return '2025-01-15';
69
+ if (node.format === 'uuid') return '00000000-0000-4000-8000-000000000001';
70
+ if (type === 'boolean') return false;
71
+ if (type === 'integer' || type === 'number') return 0;
72
+ if (type === 'array') return [];
73
+ if (type === 'object') {
74
+ if (node.properties && typeof node.properties === 'object') {
75
+ const out = {};
76
+ for (const [k, sub] of Object.entries(node.properties)) {
77
+ out[k] = placeholderFromSchemaNode(sub);
78
+ }
79
+ return out;
80
+ }
81
+ return {};
82
+ }
83
+ return '';
84
+ }
85
+
86
+ /**
87
+ * @param {string} type - Legacy attribute type
88
+ * @returns {*}
89
+ */
90
+ function placeholderForType(type) {
91
+ if (type === 'number' || type === 'integer') return 0;
92
+ if (type === 'boolean') return false;
93
+ if (type === 'array') return [];
94
+ if (type === 'object') return {};
95
+ return '';
96
+ }
97
+
98
+ /**
99
+ * @param {Object} parsed - Datasource config
100
+ * @param {string[]} pathParts - Path under raw payload
101
+ * @param {Object} [attrConfig] - Attribute config (optional type fallback)
102
+ * @returns {*}
103
+ */
104
+ function resolvePlaceholderForPath(parsed, pathParts, attrConfig) {
105
+ const node = getSchemaNodeAtPath(parsed?.metadataSchema, pathParts);
106
+ if (node) return placeholderFromSchemaNode(node);
107
+ return placeholderForType(attrConfig?.type || 'string');
108
+ }
109
+
110
+ /**
111
+ * @param {Object} obj - Target object (mutated)
112
+ * @param {string[]} pathParts - Path segments
113
+ * @param {*} value - Value to set
114
+ */
115
+ function setNestedValue(obj, pathParts, value) {
116
+ const parts = pathParts.filter(Boolean);
117
+ if (!parts.length) return;
118
+ let cur = obj;
119
+ for (let i = 0; i < parts.length - 1; i++) {
120
+ const p = parts[i];
121
+ if (!(p in cur) || typeof cur[p] !== 'object' || cur[p] === null || Array.isArray(cur[p])) {
122
+ cur[p] = {};
123
+ }
124
+ cur = cur[p];
125
+ }
126
+ const last = parts[parts.length - 1];
127
+ const existing = cur[last];
128
+ if (
129
+ existing &&
130
+ typeof existing === 'object' &&
131
+ !Array.isArray(existing) &&
132
+ value &&
133
+ typeof value === 'object' &&
134
+ !Array.isArray(value)
135
+ ) {
136
+ cur[last] = { ...existing, ...value };
137
+ return;
138
+ }
139
+ cur[last] = value;
140
+ }
141
+
142
+ /**
143
+ * @param {Object} parsed - Datasource config
144
+ * @param {Array<{ path: string, config: Object }>} pathEntries - Sorted deepest-first
145
+ * @param {Object} payloadTemplate - payloadTemplate (mutated)
146
+ */
147
+ function enrichObjectLeavesFromNestedPaths(parsed, pathEntries, payloadTemplate) {
148
+ const propertiesNode = getSchemaNodeAtPath(parsed?.metadataSchema, ['properties']);
149
+ for (const { path } of pathEntries) {
150
+ if (!path.startsWith('properties.')) continue;
151
+ const leaf = path.slice('properties.'.length);
152
+ if (!leaf || leaf.includes('.')) continue;
153
+ if (!payloadTemplate.properties || typeof payloadTemplate.properties !== 'object') {
154
+ payloadTemplate.properties = placeholderFromSchemaNode(propertiesNode);
155
+ }
156
+ if (payloadTemplate.properties[leaf] === undefined || payloadTemplate.properties[leaf] === '') {
157
+ const leafNode = getSchemaNodeAtPath(parsed?.metadataSchema, ['properties', leaf]);
158
+ payloadTemplate.properties[leaf] = leafNode
159
+ ? placeholderFromSchemaNode(leafNode)
160
+ : 'sample';
161
+ }
162
+ }
163
+ }
164
+
165
+ /**
166
+ * @param {string} expressionPath - Path from {{ ... }} token
167
+ * @returns {string|null}
168
+ */
169
+ function stripExpressionPathPrefix(expressionPath) {
170
+ const path = expressionPath.trim();
171
+ if (path.startsWith('raw.')) return path.slice(4);
172
+ if (path.startsWith('metadata.')) return path.slice(9);
173
+ return path;
174
+ }
175
+
176
+ /**
177
+ * @param {Object} parsed - Datasource config
178
+ * @param {Object} attrs - fieldMappings.attributes
179
+ * @returns {Array<{ attrKey: string, path: string|null, config: Object, expectedOnly: *|null }>}
180
+ */
181
+ function buildPathEntriesFromAttributes(parsed, attrs) {
182
+ const pathEntries = [];
183
+ for (const [attrKey, config] of Object.entries(attrs)) {
184
+ const match = config?.expression?.match(/\{\{\s*([^}|]+)/);
185
+ if (!match) {
186
+ const metaNode = parsed.metadataSchema?.properties?.[attrKey];
187
+ const expected = metaNode
188
+ ? placeholderFromSchemaNode(metaNode)
189
+ : placeholderForType(config?.type || 'string');
190
+ pathEntries.push({ attrKey, path: null, config, expectedOnly: expected });
191
+ continue;
192
+ }
193
+ pathEntries.push({
194
+ attrKey,
195
+ path: stripExpressionPathPrefix(match[1]),
196
+ config,
197
+ expectedOnly: null
198
+ });
199
+ }
200
+ return pathEntries.sort((a, b) => {
201
+ const depthA = a.path ? a.path.split('.').length : 0;
202
+ const depthB = b.path ? b.path.split('.').length : 0;
203
+ return depthB - depthA;
204
+ });
205
+ }
206
+
207
+ /**
208
+ * @param {Object} parsed - Datasource config
209
+ * @param {Array} pathEntries - From buildPathEntriesFromAttributes
210
+ * @returns {{ payloadTemplate: Object, expectedResult: Object }}
211
+ */
212
+ function buildTemplatesFromPathEntries(parsed, pathEntries) {
213
+ const payloadTemplate = {};
214
+ const expectedResult = {};
215
+ for (const entry of pathEntries) {
216
+ const { attrKey, path, config, expectedOnly } = entry;
217
+ const metaNode = parsed.metadataSchema?.properties?.[attrKey];
218
+ expectedResult[attrKey] = metaNode
219
+ ? placeholderFromSchemaNode(metaNode)
220
+ : (expectedOnly ?? placeholderForType(config?.type || 'string'));
221
+ if (!path) continue;
222
+ const parts = path.split('.');
223
+ setNestedValue(payloadTemplate, parts, resolvePlaceholderForPath(parsed, parts, config));
224
+ }
225
+ enrichObjectLeavesFromNestedPaths(parsed, pathEntries, payloadTemplate);
226
+ return { payloadTemplate, expectedResult };
227
+ }
228
+
229
+ /**
230
+ * Builds minimal payloadTemplate and expectedResult from attributes + metadataSchema.
231
+ *
232
+ * @param {Object} parsed - Parsed datasource (mutated)
233
+ * @param {string[]} changes - Change log
234
+ * @returns {boolean} True if testPayload was added or updated
235
+ */
236
+ function repairTestPayload(parsed, changes) {
237
+ const attrs = parsed?.fieldMappings?.attributes;
238
+ if (!attrs || typeof attrs !== 'object') return false;
239
+
240
+ const pathEntries = buildPathEntriesFromAttributes(parsed, attrs);
241
+ const { payloadTemplate, expectedResult } = buildTemplatesFromPathEntries(parsed, pathEntries);
242
+
243
+ if (!parsed.testPayload) parsed.testPayload = {};
244
+ parsed.testPayload.payloadTemplate = payloadTemplate;
245
+ parsed.testPayload.expectedResult = expectedResult;
246
+ changes.push(
247
+ 'Generated testPayload.payloadTemplate and testPayload.expectedResult from attributes and metadataSchema'
248
+ );
249
+ return true;
250
+ }
251
+
252
+ module.exports = {
253
+ primarySchemaType,
254
+ getSchemaNodeAtPath,
255
+ placeholderFromSchemaNode,
256
+ placeholderForType,
257
+ resolvePlaceholderForPath,
258
+ repairTestPayload
259
+ };
@@ -11,6 +11,13 @@
11
11
 
12
12
  const { repairOpenapiSection } = require('./repair-datasource-openapi');
13
13
  const { normalizeAutoRbacOperationKeys } = require('./repair-datasource-auto-rbac');
14
+ const { repairTestPayload } = require('./repair-datasource-test-payload');
15
+ const { repairComV2Manifest } = require('./repair-datasource-com-v2');
16
+ const {
17
+ isDeprecatedNoneLlmManifest,
18
+ repairNoneLlmToServiceContract,
19
+ stripDeprecatedServiceCategory
20
+ } = require('./repair-datasource-service-contract');
14
21
 
15
22
  const DEFAULT_SYNC = {
16
23
  mode: 'pull',
@@ -358,25 +365,6 @@ function repairSyncSection(parsed, changes) {
358
365
  return true;
359
366
  }
360
367
 
361
- function placeholderForType(type) {
362
- if (type === 'number' || type === 'integer') return 0;
363
- if (type === 'boolean') return false;
364
- if (type === 'array') return [];
365
- if (type === 'object') return {};
366
- return '';
367
- }
368
-
369
- function setNested(obj, pathParts, value) {
370
- let cur = obj;
371
- for (let i = 0; i < pathParts.length - 1; i++) {
372
- const p = pathParts[i];
373
- if (!(p in cur) || typeof cur[p] !== 'object') cur[p] = {};
374
- cur = cur[p];
375
- }
376
- const last = pathParts[pathParts.length - 1];
377
- if (last) cur[last] = value;
378
- }
379
-
380
368
  /**
381
369
  * Removes unknown top-level keys from testPayload (v2.4.1 closed root).
382
370
  * @param {Object} parsed - Parsed datasource (mutated)
@@ -399,35 +387,38 @@ function sanitizeTestPayloadTopLevel(parsed, changes) {
399
387
  }
400
388
 
401
389
  /**
402
- * Builds minimal payloadTemplate and expectedResult from attribute expression paths.
403
- *
404
- * @param {Object} parsed - Parsed datasource (mutated)
405
- * @param {string[]} changes - Change log
406
- * @returns {boolean} True if testPayload was added or updated
390
+ * @param {Object} parsed
391
+ * @param {Object} options
392
+ * @returns {boolean}
407
393
  */
408
- function repairTestPayload(parsed, changes) {
409
- const attrs = parsed?.fieldMappings?.attributes;
410
- if (!attrs || typeof attrs !== 'object') return false;
411
- const payloadTemplate = {};
412
- const expectedResult = {};
413
- for (const [key, config] of Object.entries(attrs)) {
414
- const type = config?.type || 'string';
415
- const placeholder = placeholderForType(type);
416
- expectedResult[key] = placeholder;
417
- const match = config?.expression?.match(/\{\{\s*([^}|]+)/);
418
- if (match) {
419
- let path = match[1].trim();
420
- if (path.startsWith('raw.')) {
421
- path = path.slice(4);
422
- }
423
- setNested(payloadTemplate, path.split('.'), placeholder);
424
- }
394
+ function shouldApplyServiceContractRepair(parsed, options) {
395
+ return (
396
+ isDeprecatedNoneLlmManifest(parsed) ||
397
+ (options.serviceContract && parsed?.resourceType === 'llm')
398
+ );
399
+ }
400
+
401
+ /**
402
+ * @param {Object} parsed
403
+ * @param {Object} options
404
+ * @param {boolean} none
405
+ * @param {string[]} changes
406
+ * @returns {boolean}
407
+ */
408
+ function applyOptionalDatasourceRepairs(parsed, options, none, changes) {
409
+ let updated = false;
410
+ if (options.expose) {
411
+ updated = repairExposeFromAttributes(parsed, changes) || updated;
425
412
  }
426
- if (!parsed.testPayload) parsed.testPayload = {};
427
- parsed.testPayload.payloadTemplate = payloadTemplate;
428
- parsed.testPayload.expectedResult = expectedResult;
429
- changes.push('Generated testPayload.payloadTemplate and testPayload.expectedResult from attributes');
430
- return true;
413
+ if (!none && options.sync) {
414
+ updated = repairSyncSection(parsed, changes) || updated;
415
+ }
416
+ if (options.test) {
417
+ sanitizeTestPayloadTopLevel(parsed, changes);
418
+ updated = repairTestPayload(parsed, changes) || updated;
419
+ updated = sanitizeTestPayloadTopLevel(parsed, changes) || updated;
420
+ }
421
+ return updated;
431
422
  }
432
423
 
433
424
  /**
@@ -440,32 +431,24 @@ function repairTestPayload(parsed, changes) {
440
431
  */
441
432
  function repairDatasourceFile(parsed, options = {}, changes = []) {
442
433
  const out = Array.isArray(changes) ? changes : [];
443
- let updated = false;
444
- const none = isNoneEntityType(parsed?.entityType);
445
-
446
- updated = applyBaseDatasourceRepairs(parsed, none, out) || updated;
447
- updated = applyOpenapiDatasourceRepairs(parsed, out) || updated;
434
+ let updated = stripDeprecatedServiceCategory(parsed, out);
448
435
 
449
- if (options.expose) {
450
- updated = repairExposeFromAttributes(parsed, out) || updated;
436
+ if (shouldApplyServiceContractRepair(parsed, options)) {
437
+ updated = repairNoneLlmToServiceContract(parsed, out) || updated;
451
438
  }
452
439
 
453
- if (!none && options.sync) {
454
- updated = repairSyncSection(parsed, out) || updated;
455
- }
440
+ const none = isNoneEntityType(parsed?.entityType);
456
441
 
457
- if (options.test) {
458
- sanitizeTestPayloadTopLevel(parsed, out);
459
- updated = repairTestPayload(parsed, out) || updated;
460
- updated = sanitizeTestPayloadTopLevel(parsed, out) || updated;
461
- }
442
+ updated = applyBaseDatasourceRepairs(parsed, none, out) || updated;
443
+ updated = applyOpenapiDatasourceRepairs(parsed, out) || updated;
444
+ updated = applyOptionalDatasourceRepairs(parsed, options, none, out) || updated;
462
445
 
463
446
  return { updated, changes: out };
464
447
  }
465
448
 
466
449
  function applyBaseDatasourceRepairs(parsed, none, changes) {
467
- if (none) return false;
468
- let updated = false;
450
+ let updated = repairComV2Manifest(parsed, changes);
451
+ if (none) return updated;
469
452
  updated = repairDimensionBindingShape(parsed, changes) || updated;
470
453
  updated = repairRootDimensionsFromAttributes(parsed, changes) || updated;
471
454
  updated = repairMetadataSchemaFromAttributes(parsed, changes) || updated;
@@ -494,5 +477,7 @@ module.exports = {
494
477
  DEFAULT_SYNC,
495
478
  MINIMAL_METADATA_SCHEMA,
496
479
  TEST_PAYLOAD_TOP_LEVEL_ALLOW,
497
- isNoneEntityType
480
+ isNoneEntityType,
481
+ isDeprecatedNoneLlmManifest,
482
+ repairNoneLlmToServiceContract
498
483
  };
@@ -10,9 +10,11 @@
10
10
  const path = require('path');
11
11
  const fs = require('fs');
12
12
  const { backupIntegrationFile } = require('../utils/integration-file-backup');
13
+ const { trackRepairWrite } = require('./repair-changed-files');
13
14
  const { systemKeyToKvPrefix, kvEnvKeyToPath, securityKeyToVar } = require('../utils/credential-secrets-env');
14
15
  const { extractEnvTemplate } = require('../generator/split');
15
16
  const { generateExternalEnvTemplateContent } = require('../utils/external-env-template');
17
+ const { commentOrphanKvEnvLines } = require('../utils/env-kv-orphan-lines');
16
18
 
17
19
  /**
18
20
  * Normalizes a keyvault config entry to canonical KV_* name and path-style value.
@@ -75,9 +77,16 @@ function addFromConfiguration(effective, config, prefix, seenNames, systemKey) {
75
77
  * @param {string} systemKey - System key for path namespace
76
78
  */
77
79
  function addFromAuthSecurity(effective, systemParsed, prefix, seenNames, systemKey) {
78
- const security = systemParsed.authentication?.security;
80
+ const { getAuthCredentialTemplate } = require('../utils/authentication-shape');
81
+ const { isPlatformCatalogKvRef } = require('../utils/platform-kv-ref');
82
+ const credential = getAuthCredentialTemplate(systemParsed.authentication);
83
+ const security = credential?.security ?? systemParsed.authentication?.security;
79
84
  if (!security || typeof security !== 'object') return;
80
85
  for (const key of Object.keys(security)) {
86
+ const securityVal = security[key];
87
+ if (typeof securityVal === 'string' && isPlatformCatalogKvRef(securityVal)) {
88
+ continue;
89
+ }
81
90
  const envName = `KV_${prefix}_${securityKeyToVar(key)}`;
82
91
  if (seenNames.has(envName)) continue;
83
92
  const pathVal = kvEnvKeyToPath(envName, systemKey);
@@ -136,7 +145,7 @@ function buildExpectedByKey(effective) {
136
145
  * @param {Object} [systemParsed] - Parsed system config for template context (preferred)
137
146
  * @returns {boolean}
138
147
  */
139
- function createEnvTemplateIfMissing(envPath, expectedByKey, dryRun, changes, systemParsed) {
148
+ function createEnvTemplateIfMissing(envPath, expectedByKey, dryRun, changes, systemParsed, backupCtx) {
140
149
  if (fs.existsSync(envPath)) return false;
141
150
  if (expectedByKey.size === 0) return false;
142
151
  const content = systemParsed
@@ -145,6 +154,7 @@ function createEnvTemplateIfMissing(envPath, expectedByKey, dryRun, changes, sys
145
154
  if (!content || !content.trim()) return false;
146
155
  const hasKeyValueLine = /^[A-Z_][A-Z0-9_]*=/m.test(content);
147
156
  if (!hasKeyValueLine) return false;
157
+ trackRepairWrite(envPath, backupCtx);
148
158
  if (!dryRun) {
149
159
  fs.writeFileSync(envPath, content + (content.endsWith('\n') ? '' : '\n'), { mode: 0o644, encoding: 'utf8' });
150
160
  }
@@ -229,6 +239,51 @@ function mergeEnvTemplateContent(content, expectedByKey) {
229
239
  return { output, changed: changedRef.value };
230
240
  }
231
241
 
242
+ /**
243
+ * Comments active KV_* lines whose env key is not in the system-derived expected set.
244
+ * @param {string} content
245
+ * @param {Map<string, string>} expectedByKey
246
+ * @param {{ value: boolean }} changedRef
247
+ * @returns {string}
248
+ */
249
+ function commentOrphanKvLines(content, expectedByKey, changedRef) {
250
+ const { content: next, changed } = commentOrphanKvEnvLines(content, expectedByKey);
251
+ if (changed) {
252
+ changedRef.value = true;
253
+ }
254
+ return next;
255
+ }
256
+
257
+ /**
258
+ * @param {string} envPath
259
+ * @param {string} output
260
+ * @param {boolean} dryRun
261
+ * @param {Object} [backupCtx]
262
+ */
263
+ function persistEnvTemplate(envPath, output, dryRun, backupCtx) {
264
+ trackRepairWrite(envPath, backupCtx);
265
+ if (!dryRun) {
266
+ backupIntegrationFile(envPath, backupCtx);
267
+ fs.writeFileSync(envPath, output, { mode: 0o644, encoding: 'utf8' });
268
+ }
269
+ }
270
+
271
+ /**
272
+ * @param {string} content
273
+ * @param {Map<string, string>} expectedByKey
274
+ * @returns {{ output: string, mergeChanged: boolean, pruneChanged: boolean }}
275
+ */
276
+ function mergeAndPruneEnvTemplate(content, expectedByKey) {
277
+ const mergeResult = mergeEnvTemplateContent(content, expectedByKey);
278
+ const pruneChanged = { value: false };
279
+ const output = commentOrphanKvLines(mergeResult.output, expectedByKey, pruneChanged);
280
+ return {
281
+ output,
282
+ mergeChanged: mergeResult.changed,
283
+ pruneChanged: pruneChanged.value
284
+ };
285
+ }
286
+
232
287
  /**
233
288
  * Repairs env.template so KV_ names and path-style kv:// values match the system file.
234
289
  * @param {string} appPath - Application directory path
@@ -240,41 +295,35 @@ function mergeEnvTemplateContent(content, expectedByKey) {
240
295
  * @returns {boolean} True if env.template was repaired or created
241
296
  */
242
297
  function repairEnvTemplate(appPath, systemParsed, systemKey, dryRun, changes, backupCtx) {
243
- const effective = buildEffectiveConfiguration(systemParsed, systemKey);
244
- const expectedByKey = buildExpectedByKey(effective);
298
+ const expectedByKey = buildExpectedByKey(buildEffectiveConfiguration(systemParsed, systemKey));
245
299
  const envPath = path.join(appPath, 'env.template');
246
300
 
247
- if (createEnvTemplateIfMissing(envPath, expectedByKey, dryRun, changes, systemParsed)) {
301
+ if (createEnvTemplateIfMissing(envPath, expectedByKey, dryRun, changes, systemParsed, backupCtx)) {
248
302
  return true;
249
303
  }
250
304
  if (!fs.existsSync(envPath)) {
251
305
  return false;
252
306
  }
253
307
 
254
- const content = fs.readFileSync(envPath, 'utf8');
255
- const { output, changed } = mergeEnvTemplateContent(content, expectedByKey);
256
-
257
- if (changed && !dryRun) {
258
- backupIntegrationFile(envPath, backupCtx);
259
- fs.writeFileSync(envPath, output, { mode: 0o644, encoding: 'utf8' });
308
+ const { output, mergeChanged, pruneChanged } = mergeAndPruneEnvTemplate(
309
+ fs.readFileSync(envPath, 'utf8'),
310
+ expectedByKey
311
+ );
312
+ if (!mergeChanged && !pruneChanged) {
313
+ return false;
260
314
  }
261
- if (changed) {
315
+
316
+ persistEnvTemplate(envPath, output, dryRun, backupCtx);
317
+ if (mergeChanged) {
262
318
  changes.push('Repaired env.template (KV_* names and path-style kv:// values)');
263
- return true;
264
319
  }
265
- return false;
320
+ if (pruneChanged) {
321
+ changes.push('Commented stale env.template KV_* lines (not in system authentication)');
322
+ }
323
+ return true;
266
324
  }
267
325
 
268
- /**
269
- * Returns true if a kv value looks like legacy format (KeyVault suffix or no path segments).
270
- * @param {string} val - Value from authentication.security or configuration
271
- * @returns {boolean}
272
- */
273
- function isLegacyKvValue(val) {
274
- if (typeof val !== 'string' || !val.trim().toLowerCase().startsWith('kv://')) return false;
275
- const after = val.trim().slice(5); // after 'kv://'
276
- return after.includes('KeyVault') || !after.includes('/');
277
- }
326
+ const { isLegacyKvValue } = require('../utils/platform-kv-ref');
278
327
 
279
328
  /**
280
329
  * Normalizes authentication.security values to path-style kv:// (kv://systemKey/variable).
@@ -341,7 +390,9 @@ function normalizeConfigurationSection(config, prefix, systemKey, changes) {
341
390
  function normalizeSystemFileAuthAndConfig(systemParsed, systemKey, changes) {
342
391
  const prefix = systemKeyToKvPrefix(systemKey);
343
392
  if (!prefix) return false;
344
- const security = systemParsed.authentication?.security;
393
+ const { getAuthCredentialTemplate } = require('../utils/authentication-shape');
394
+ const credential = getAuthCredentialTemplate(systemParsed.authentication);
395
+ const security = credential?.security ?? systemParsed.authentication?.security;
345
396
  let updated = (security && typeof security === 'object' && normalizeSecuritySection(security, prefix, systemKey, changes));
346
397
  const config = systemParsed.configuration;
347
398
  if (Array.isArray(config)) {