@aifabrix/builder 2.45.0 → 2.46.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 (352) 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/docs-rules.mdc +3 -1
  4. package/.cursor/rules/project-rules.mdc +1 -0
  5. package/README.md +43 -17
  6. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
  7. package/jest.config.default.js +2 -1
  8. package/jest.config.isolated.js +2 -1
  9. package/jest.global-live-fabrix-hooks.js +36 -0
  10. package/jest.isolated-projects.js +130 -0
  11. package/jest.projects.js +69 -121
  12. package/lib/api/agent-metadata-validation.api.js +93 -0
  13. package/lib/api/api-response-errors.js +124 -0
  14. package/lib/api/audit-api-helpers.js +68 -0
  15. package/lib/api/audit.api.js +119 -0
  16. package/lib/api/auth-cache.api.js +44 -0
  17. package/lib/api/certificates.api.js +4 -4
  18. package/lib/api/credential.api.js +2 -2
  19. package/lib/api/dataplane-health.api.js +137 -0
  20. package/lib/api/dataplane-sync.api.js +27 -0
  21. package/lib/api/datasources-core.api.js +21 -18
  22. package/lib/api/datasources-extended.api.js +22 -22
  23. package/lib/api/dimensions.api.js +3 -0
  24. package/lib/api/external-systems.api.js +13 -13
  25. package/lib/api/governance-scenario-pack.api.js +159 -0
  26. package/lib/api/governance-scenarios.api.js +39 -0
  27. package/lib/api/groups.api.js +79 -0
  28. package/lib/api/index.js +88 -5
  29. package/lib/api/lifecycle.api.js +77 -0
  30. package/lib/api/pipeline.api.js +7 -5
  31. package/lib/api/protection.api.js +157 -0
  32. package/lib/api/records-bulk.api.js +43 -0
  33. package/lib/api/records-search-parse.js +49 -0
  34. package/lib/api/records-search.api.js +29 -0
  35. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  36. package/lib/api/types/audit.types.js +72 -0
  37. package/lib/api/types/dataplane-health.types.js +27 -0
  38. package/lib/api/types/dimensions.types.js +2 -0
  39. package/lib/api/types/governance-scenarios.types.js +39 -0
  40. package/lib/api/types/protection.types.js +69 -0
  41. package/lib/api/types/records-bulk.types.js +35 -0
  42. package/lib/api/types/records-search.types.js +28 -0
  43. package/lib/api/user-groups.api.js +50 -0
  44. package/lib/api/users.api.js +95 -0
  45. package/lib/api/validation-run.api.js +3 -3
  46. package/lib/api/validation-runner.js +5 -3
  47. package/lib/api/wizard-platform.api.js +3 -3
  48. package/lib/api/wizard.api.js +21 -21
  49. package/lib/app/deploy-config.js +4 -1
  50. package/lib/app/deploy.js +34 -19
  51. package/lib/app/down.js +20 -1
  52. package/lib/app/push.js +4 -5
  53. package/lib/app/register.js +7 -4
  54. package/lib/app/run-env-compose.js +84 -30
  55. package/lib/build/index.js +5 -0
  56. package/lib/certification/sync-system-certification.js +13 -6
  57. package/lib/cli/index.js +8 -2
  58. package/lib/cli/infra-guided-auth.js +109 -0
  59. package/lib/cli/infra-guided.js +91 -69
  60. package/lib/cli/setup-app.help.js +19 -2
  61. package/lib/cli/setup-app.js +58 -23
  62. package/lib/cli/setup-app.test-commands.js +28 -21
  63. package/lib/cli/setup-auth.js +55 -3
  64. package/lib/cli/setup-certification-commands.js +147 -0
  65. package/lib/cli/setup-certification.help.js +141 -0
  66. package/lib/cli/setup-environment.js +19 -2
  67. package/lib/cli/setup-external-system-help.js +62 -0
  68. package/lib/cli/setup-external-system.js +62 -6
  69. package/lib/cli/setup-infra.js +9 -1
  70. package/lib/cli/setup-platform.js +6 -1
  71. package/lib/cli/setup-secrets.js +33 -1
  72. package/lib/cli/setup-utility-help-after.js +65 -0
  73. package/lib/cli/setup-utility-repair.js +198 -0
  74. package/lib/cli/setup-utility-resolve.js +42 -6
  75. package/lib/cli/setup-utility-validate.js +197 -0
  76. package/lib/cli/setup-utility.js +41 -258
  77. package/lib/commands/auth-status-client-tokens.js +214 -0
  78. package/lib/commands/auth-status-dataplane-version.js +269 -0
  79. package/lib/commands/auth-status-display.js +94 -0
  80. package/lib/commands/auth-status.js +111 -53
  81. package/lib/commands/credential-push.js +10 -5
  82. package/lib/commands/datasource-load-export-cli.js +178 -0
  83. package/lib/commands/datasource-test-trust-cli.js +118 -0
  84. package/lib/commands/datasource-unified-test-cli.js +31 -20
  85. package/lib/commands/datasource-unified-test-cli.options.js +22 -3
  86. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  87. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  88. package/lib/commands/datasource.js +68 -1
  89. package/lib/commands/dev-show-display.js +8 -1
  90. package/lib/commands/dimension-value.js +43 -13
  91. package/lib/commands/dimension.js +38 -7
  92. package/lib/commands/governance-cli-shared.js +94 -0
  93. package/lib/commands/governance-command-action.js +132 -0
  94. package/lib/commands/governance-download-external.js +34 -0
  95. package/lib/commands/governance-generate-external.js +71 -0
  96. package/lib/commands/governance-upload-external.js +36 -0
  97. package/lib/commands/governance-verify-external.js +64 -0
  98. package/lib/commands/governance.js +144 -0
  99. package/lib/commands/identity-apply-sync.js +131 -0
  100. package/lib/commands/identity-group-cli.js +146 -0
  101. package/lib/commands/identity-membership-role-cache.js +175 -0
  102. package/lib/commands/identity-shared.js +94 -0
  103. package/lib/commands/identity-user-cli.js +158 -0
  104. package/lib/commands/identity.js +43 -0
  105. package/lib/commands/lifecycle-command-action.js +123 -0
  106. package/lib/commands/lifecycle-external.js +268 -0
  107. package/lib/commands/login.js +56 -17
  108. package/lib/commands/protection-cli-leaves.js +286 -0
  109. package/lib/commands/protection.js +50 -0
  110. package/lib/commands/repair-auth-apply.js +127 -0
  111. package/lib/commands/repair-changed-files.js +41 -0
  112. package/lib/commands/repair-datasource-keys.js +4 -1
  113. package/lib/commands/repair-datasource-run.js +59 -0
  114. package/lib/commands/repair-datasource-test-payload.js +259 -0
  115. package/lib/commands/repair-datasource.js +1 -51
  116. package/lib/commands/repair-env-template.js +64 -13
  117. package/lib/commands/repair-external-run.js +216 -0
  118. package/lib/commands/repair-openapi-sync.js +23 -0
  119. package/lib/commands/repair-persist.js +4 -0
  120. package/lib/commands/repair-rbac.js +52 -1
  121. package/lib/commands/repair-steps.js +145 -0
  122. package/lib/commands/repair.js +29 -286
  123. package/lib/commands/secrets-get.js +184 -0
  124. package/lib/commands/secrets-remove-all.js +3 -1
  125. package/lib/commands/setup-image-refresh.js +146 -0
  126. package/lib/commands/setup-modes.js +158 -162
  127. package/lib/commands/setup-platform-auth.js +76 -0
  128. package/lib/commands/setup-prompts-admin.js +244 -0
  129. package/lib/commands/setup-prompts-platform-mode.js +67 -0
  130. package/lib/commands/setup-prompts-validators.js +36 -0
  131. package/lib/commands/setup-prompts.js +24 -76
  132. package/lib/commands/setup-run-context.js +45 -0
  133. package/lib/commands/setup-spinners.js +39 -0
  134. package/lib/commands/setup.js +27 -5
  135. package/lib/commands/teardown.js +39 -0
  136. package/lib/commands/test-e2e-external.js +61 -14
  137. package/lib/commands/test-governance-cli-options.js +47 -0
  138. package/lib/commands/test-governance-external.js +67 -0
  139. package/lib/commands/test-trust-cli-options.js +32 -0
  140. package/lib/commands/test-trust-external.js +126 -0
  141. package/lib/commands/up-common.js +25 -13
  142. package/lib/commands/up-dataplane.js +50 -13
  143. package/lib/commands/upload-publish-context.js +75 -0
  144. package/lib/commands/upload-sidecar-followups.js +89 -0
  145. package/lib/commands/upload.js +108 -105
  146. package/lib/commands/verify-governance-combined-external.js +61 -0
  147. package/lib/commands/verify-governance-command-action.js +64 -0
  148. package/lib/commands/verify-governance-db.js +102 -0
  149. package/lib/commands/verify-governance-external.js +77 -0
  150. package/lib/commands/verify-governance-result-builders.js +71 -0
  151. package/lib/commands/verify-operations-command-action.js +68 -0
  152. package/lib/commands/verify-operations-external.js +268 -0
  153. package/lib/commands/verify-operations-persist.js +75 -0
  154. package/lib/commands/verify-operations-step-plan.js +68 -0
  155. package/lib/commands/verify-operations-steps.js +111 -0
  156. package/lib/commands/verify-trust-command-action.js +70 -0
  157. package/lib/commands/verify-trust-external.js +67 -0
  158. package/lib/commands/wizard-config-normalizer.js +43 -23
  159. package/lib/commands/wizard-core.js +5 -0
  160. package/lib/core/admin-password-generator.js +38 -0
  161. package/lib/core/admin-secrets-bundle.js +111 -0
  162. package/lib/core/admin-secrets-env-overlay.js +62 -0
  163. package/lib/core/admin-secrets.js +32 -4
  164. package/lib/core/config-attach-extensions.js +3 -0
  165. package/lib/core/config-device-dataplane.js +185 -0
  166. package/lib/core/config-platform-controller.js +44 -0
  167. package/lib/core/config-setup-installation-profile.js +42 -0
  168. package/lib/core/config-setup-platform-mode.js +46 -0
  169. package/lib/core/config.js +46 -36
  170. package/lib/core/secrets-admin-env.js +6 -2
  171. package/lib/core/secrets-ensure.js +4 -0
  172. package/lib/core/secrets-env-content.js +54 -48
  173. package/lib/core/secrets-env-write.js +10 -1
  174. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  175. package/lib/core/setup-installation-profile.js +43 -0
  176. package/lib/core/setup-platform-mode.js +55 -0
  177. package/lib/datasource/agent-trust-debug-log.js +45 -0
  178. package/lib/datasource/agent-trust-fetch.js +115 -0
  179. package/lib/datasource/agent-trust-map.js +101 -0
  180. package/lib/datasource/agent-trust-mock.js +44 -0
  181. package/lib/datasource/agent-trust-run.js +95 -0
  182. package/lib/datasource/agent-trust-timeout.js +25 -0
  183. package/lib/datasource/audit-evidence-display.js +80 -0
  184. package/lib/datasource/audit-evidence-extract-debug.js +115 -0
  185. package/lib/datasource/audit-evidence-extract.js +180 -0
  186. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  187. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  188. package/lib/datasource/audit-evidence-poll.js +109 -0
  189. package/lib/datasource/audit-evidence-run.js +177 -0
  190. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  191. package/lib/datasource/audit-evidence-verify-matrix.js +205 -0
  192. package/lib/datasource/audit-evidence-verify.js +108 -0
  193. package/lib/datasource/bulk-loader-run-batches.js +100 -0
  194. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  195. package/lib/datasource/bulk-loader-service.js +127 -0
  196. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  197. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  198. package/lib/datasource/datasource-exporter-service.js +144 -0
  199. package/lib/datasource/datasource-identifier.js +148 -0
  200. package/lib/datasource/datasource-load-export-context.js +95 -0
  201. package/lib/datasource/integration-context.js +6 -4
  202. package/lib/datasource/local-data-paths.js +145 -0
  203. package/lib/datasource/log-cleaner.js +241 -0
  204. package/lib/datasource/log-viewer.js +111 -6
  205. package/lib/datasource/record-file-parser.js +85 -0
  206. package/lib/datasource/record-mapper.js +129 -0
  207. package/lib/datasource/resolve-app.js +74 -55
  208. package/lib/datasource/test-e2e.js +29 -2
  209. package/lib/datasource/unified-validation-run-body.js +3 -1
  210. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  211. package/lib/datasource/unified-validation-run.js +47 -18
  212. package/lib/datasource/validate.js +37 -20
  213. package/lib/deployment/environment-poll-status.js +138 -0
  214. package/lib/deployment/environment.js +42 -34
  215. package/lib/external-system/download.js +21 -20
  216. package/lib/external-system/generator.js +5 -1
  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-system-level.js +25 -4
  220. package/lib/external-system/test.js +9 -7
  221. package/lib/generator/external-controller-manifest.js +15 -0
  222. package/lib/governance/governance-cli-errors.js +96 -0
  223. package/lib/governance/governance-download-display.js +38 -0
  224. package/lib/governance/governance-pack-loader.js +70 -0
  225. package/lib/governance/governance-run-display.js +309 -0
  226. package/lib/identity/identity-apply-core.js +108 -0
  227. package/lib/identity/identity-apply-phases.js +244 -0
  228. package/lib/identity/identity-apply-service.js +77 -0
  229. package/lib/identity/identity-csv-parser.js +114 -0
  230. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  231. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  232. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  233. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  234. package/lib/infrastructure/helpers.js +23 -129
  235. package/lib/infrastructure/index.js +5 -6
  236. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  237. package/lib/internal/fs-real-sync.js +29 -1
  238. package/lib/lifecycle/certification-progress.js +231 -0
  239. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  240. package/lib/lifecycle/exit-codes.js +24 -0
  241. package/lib/lifecycle/json-envelope.js +25 -0
  242. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  243. package/lib/lifecycle/logger-quiet.js +29 -0
  244. package/lib/lifecycle/open-api-published.js +59 -0
  245. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  246. package/lib/lifecycle/product-model.js +196 -0
  247. package/lib/lifecycle/report-display.js +471 -0
  248. package/lib/lifecycle/scenario-upload.js +191 -0
  249. package/lib/lifecycle/verify-step-progress.js +87 -0
  250. package/lib/parameters/infra-kv-discovery.js +27 -2
  251. package/lib/protection/auth-context.js +51 -0
  252. package/lib/protection/convert-batch.js +90 -0
  253. package/lib/protection/load.js +41 -0
  254. package/lib/protection/paths.js +158 -0
  255. package/lib/protection/preflight-datasource-ready.js +46 -0
  256. package/lib/protection/protection-create-probe-lines.js +142 -0
  257. package/lib/protection/protection-create-probes.js +95 -0
  258. package/lib/protection/protection-create-scaffold.js +281 -0
  259. package/lib/protection/protection-display-helpers.js +445 -0
  260. package/lib/protection/protection-display.js +215 -0
  261. package/lib/protection/protection-preset-registry.js +97 -0
  262. package/lib/protection/report-exit.js +62 -0
  263. package/lib/protection/resolve.js +202 -0
  264. package/lib/protection/run-commands-validate.js +130 -0
  265. package/lib/protection/run-commands.js +216 -0
  266. package/lib/protection/run-protection-create-helpers.js +134 -0
  267. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  268. package/lib/protection/run-protection-create.js +191 -0
  269. package/lib/protection/scope.js +22 -0
  270. package/lib/protection/sync-after-upload.js +64 -0
  271. package/lib/protection/upload-batch.js +127 -0
  272. package/lib/protection/validate-batch.js +113 -0
  273. package/lib/protection/validate-local.js +65 -0
  274. package/lib/resolvers/dimension-file.js +29 -4
  275. package/lib/schema/application-schema.json +1 -1
  276. package/lib/schema/datasource-test-run.schema.json +89 -6
  277. package/lib/schema/external-datasource.schema.json +2636 -2588
  278. package/lib/schema/external-system.schema.json +1089 -761
  279. package/lib/schema/flag-map-validation-run.json +2 -2
  280. package/lib/schema/infra.parameter.yaml +45 -0
  281. package/lib/schema/protection.schema.json +374 -0
  282. package/lib/schema/type/credential-field-registry.json +642 -0
  283. package/lib/schema/wizard-config.schema.json +1 -1
  284. package/lib/utils/agent-trust-run-display.js +231 -0
  285. package/lib/utils/agent-trust-run-exit.js +59 -0
  286. package/lib/utils/agent-trust-wait-ui.js +94 -0
  287. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  288. package/lib/utils/api.js +55 -31
  289. package/lib/utils/app-relative-path-env.js +112 -0
  290. package/lib/utils/cli-exit-codes.js +30 -0
  291. package/lib/utils/cli-secrets-error-format.js +83 -21
  292. package/lib/utils/cli-sync-options.js +33 -0
  293. package/lib/utils/cli-utils.js +16 -4
  294. package/lib/utils/compose-generate-docker-compose.js +4 -0
  295. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  296. package/lib/utils/config-secrets-path.js +46 -0
  297. package/lib/utils/controller-url.js +88 -1
  298. package/lib/utils/credential-push-ui.js +90 -0
  299. package/lib/utils/credential-secrets-env.js +137 -9
  300. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  301. package/lib/utils/dataplane-cli-version-help.js +74 -0
  302. package/lib/utils/dataplane-resolver.js +129 -2
  303. package/lib/utils/datasource-export-display.js +137 -0
  304. package/lib/utils/datasource-load-display.js +174 -0
  305. package/lib/utils/datasource-load-export-ui.js +120 -0
  306. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  307. package/lib/utils/datasource-test-run-display.js +3 -3
  308. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  309. package/lib/utils/declarative-url-ports.js +3 -3
  310. package/lib/utils/deployment-auth-mode.js +41 -0
  311. package/lib/utils/developer-volume-id.js +33 -0
  312. package/lib/utils/env-copy.js +14 -9
  313. package/lib/utils/env-kv-orphan-lines.js +43 -0
  314. package/lib/utils/error-formatters/network-errors.js +13 -4
  315. package/lib/utils/external-env-template.js +1 -0
  316. package/lib/utils/external-system-readiness-core.js +10 -0
  317. package/lib/utils/external-system-readiness-display.js +80 -23
  318. package/lib/utils/health-check.js +4 -1
  319. package/lib/utils/help-builder.js +35 -26
  320. package/lib/utils/keycloak-realm-ready.js +101 -0
  321. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  322. package/lib/utils/manifest-source-emit.js +24 -0
  323. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  324. package/lib/utils/platform-controller-url.js +156 -0
  325. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  326. package/lib/utils/postgres-wipe.js +9 -2
  327. package/lib/utils/resolve-env-mode.js +47 -0
  328. package/lib/utils/secrets-generator.js +31 -25
  329. package/lib/utils/secrets-missing-error.js +164 -0
  330. package/lib/utils/semver-compare.js +127 -0
  331. package/lib/utils/service-url-label.js +81 -0
  332. package/lib/utils/setup-quiet-output.js +25 -0
  333. package/lib/utils/template-helpers.js +15 -0
  334. package/lib/utils/token-manager-client-credentials.js +201 -0
  335. package/lib/utils/token-manager-messages.js +8 -0
  336. package/lib/utils/token-manager-refresh.js +12 -2
  337. package/lib/utils/token-manager.js +86 -105
  338. package/lib/utils/upload-pipeline-ui.js +98 -0
  339. package/lib/utils/upload-sync-options.js +42 -0
  340. package/lib/utils/url-declarative-public-base.js +21 -3
  341. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  342. package/lib/utils/validation-poll-ui.js +22 -7
  343. package/lib/utils/validation-run-stage.js +45 -0
  344. package/lib/validation/wizard-config-validator.js +58 -19
  345. package/package.json +1 -1
  346. package/templates/applications/dataplane/application.yaml +2 -1
  347. package/templates/applications/dataplane/env.template +36 -1
  348. package/templates/applications/dataplane/rbac.yaml +4 -0
  349. package/templates/applications/miso-controller/env.template +16 -0
  350. package/templates/external-system/README.md.hbs +116 -11
  351. package/templates/python/docker-compose.hbs +19 -18
  352. package/templates/typescript/docker-compose.hbs +18 -17
@@ -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,7 @@
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');
14
15
 
15
16
  const DEFAULT_SYNC = {
16
17
  mode: 'pull',
@@ -358,25 +359,6 @@ function repairSyncSection(parsed, changes) {
358
359
  return true;
359
360
  }
360
361
 
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
362
  /**
381
363
  * Removes unknown top-level keys from testPayload (v2.4.1 closed root).
382
364
  * @param {Object} parsed - Parsed datasource (mutated)
@@ -398,38 +380,6 @@ function sanitizeTestPayloadTopLevel(parsed, changes) {
398
380
  return true;
399
381
  }
400
382
 
401
- /**
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
407
- */
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
- }
425
- }
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;
431
- }
432
-
433
383
  /**
434
384
  * Runs all requested datasource repairs.
435
385
  *
@@ -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.
@@ -136,7 +138,7 @@ function buildExpectedByKey(effective) {
136
138
  * @param {Object} [systemParsed] - Parsed system config for template context (preferred)
137
139
  * @returns {boolean}
138
140
  */
139
- function createEnvTemplateIfMissing(envPath, expectedByKey, dryRun, changes, systemParsed) {
141
+ function createEnvTemplateIfMissing(envPath, expectedByKey, dryRun, changes, systemParsed, backupCtx) {
140
142
  if (fs.existsSync(envPath)) return false;
141
143
  if (expectedByKey.size === 0) return false;
142
144
  const content = systemParsed
@@ -145,6 +147,7 @@ function createEnvTemplateIfMissing(envPath, expectedByKey, dryRun, changes, sys
145
147
  if (!content || !content.trim()) return false;
146
148
  const hasKeyValueLine = /^[A-Z_][A-Z0-9_]*=/m.test(content);
147
149
  if (!hasKeyValueLine) return false;
150
+ trackRepairWrite(envPath, backupCtx);
148
151
  if (!dryRun) {
149
152
  fs.writeFileSync(envPath, content + (content.endsWith('\n') ? '' : '\n'), { mode: 0o644, encoding: 'utf8' });
150
153
  }
@@ -229,6 +232,51 @@ function mergeEnvTemplateContent(content, expectedByKey) {
229
232
  return { output, changed: changedRef.value };
230
233
  }
231
234
 
235
+ /**
236
+ * Comments active KV_* lines whose env key is not in the system-derived expected set.
237
+ * @param {string} content
238
+ * @param {Map<string, string>} expectedByKey
239
+ * @param {{ value: boolean }} changedRef
240
+ * @returns {string}
241
+ */
242
+ function commentOrphanKvLines(content, expectedByKey, changedRef) {
243
+ const { content: next, changed } = commentOrphanKvEnvLines(content, expectedByKey);
244
+ if (changed) {
245
+ changedRef.value = true;
246
+ }
247
+ return next;
248
+ }
249
+
250
+ /**
251
+ * @param {string} envPath
252
+ * @param {string} output
253
+ * @param {boolean} dryRun
254
+ * @param {Object} [backupCtx]
255
+ */
256
+ function persistEnvTemplate(envPath, output, dryRun, backupCtx) {
257
+ trackRepairWrite(envPath, backupCtx);
258
+ if (!dryRun) {
259
+ backupIntegrationFile(envPath, backupCtx);
260
+ fs.writeFileSync(envPath, output, { mode: 0o644, encoding: 'utf8' });
261
+ }
262
+ }
263
+
264
+ /**
265
+ * @param {string} content
266
+ * @param {Map<string, string>} expectedByKey
267
+ * @returns {{ output: string, mergeChanged: boolean, pruneChanged: boolean }}
268
+ */
269
+ function mergeAndPruneEnvTemplate(content, expectedByKey) {
270
+ const mergeResult = mergeEnvTemplateContent(content, expectedByKey);
271
+ const pruneChanged = { value: false };
272
+ const output = commentOrphanKvLines(mergeResult.output, expectedByKey, pruneChanged);
273
+ return {
274
+ output,
275
+ mergeChanged: mergeResult.changed,
276
+ pruneChanged: pruneChanged.value
277
+ };
278
+ }
279
+
232
280
  /**
233
281
  * Repairs env.template so KV_ names and path-style kv:// values match the system file.
234
282
  * @param {string} appPath - Application directory path
@@ -240,29 +288,32 @@ function mergeEnvTemplateContent(content, expectedByKey) {
240
288
  * @returns {boolean} True if env.template was repaired or created
241
289
  */
242
290
  function repairEnvTemplate(appPath, systemParsed, systemKey, dryRun, changes, backupCtx) {
243
- const effective = buildEffectiveConfiguration(systemParsed, systemKey);
244
- const expectedByKey = buildExpectedByKey(effective);
291
+ const expectedByKey = buildExpectedByKey(buildEffectiveConfiguration(systemParsed, systemKey));
245
292
  const envPath = path.join(appPath, 'env.template');
246
293
 
247
- if (createEnvTemplateIfMissing(envPath, expectedByKey, dryRun, changes, systemParsed)) {
294
+ if (createEnvTemplateIfMissing(envPath, expectedByKey, dryRun, changes, systemParsed, backupCtx)) {
248
295
  return true;
249
296
  }
250
297
  if (!fs.existsSync(envPath)) {
251
298
  return false;
252
299
  }
253
300
 
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' });
301
+ const { output, mergeChanged, pruneChanged } = mergeAndPruneEnvTemplate(
302
+ fs.readFileSync(envPath, 'utf8'),
303
+ expectedByKey
304
+ );
305
+ if (!mergeChanged && !pruneChanged) {
306
+ return false;
260
307
  }
261
- if (changed) {
308
+
309
+ persistEnvTemplate(envPath, output, dryRun, backupCtx);
310
+ if (mergeChanged) {
262
311
  changes.push('Repaired env.template (KV_* names and path-style kv:// values)');
263
- return true;
264
312
  }
265
- return false;
313
+ if (pruneChanged) {
314
+ changes.push('Commented stale env.template KV_* lines (not in system authentication)');
315
+ }
316
+ return true;
266
317
  }
267
318
 
268
319
  /**
@@ -0,0 +1,216 @@
1
+ /**
2
+ * External integration repair orchestration (local file updates, manifest, README).
3
+ *
4
+ * @fileoverview Repair external integration run pipeline
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { normalizeDatasourceKeysAndFilenames } = require('./repair-datasource-keys');
12
+ const { resolveSystemContext } = require('./repair-system-alignment');
13
+ const { persistChangesAndRegenerate, regenerateReadmeIfRequested } = require('./repair-persist');
14
+ const { runRepairSteps } = require('./repair-steps');
15
+ const { runDatasourceRepairs } = require('./repair-datasource-run');
16
+ const { maybeRepairRbac } = require('./repair-rbac');
17
+ const { maybeRunOpenApiSyncForMcp } = require('./repair-openapi-sync');
18
+
19
+ /**
20
+ * @param {Object} steps
21
+ * @param {boolean} anyUpdated
22
+ * @param {boolean} manifestRegenerated
23
+ * @param {boolean} readmeRegenerated
24
+ * @param {Object} ctx
25
+ * @returns {Object}
26
+ */
27
+ function buildRepairResult(steps, anyUpdated, manifestRegenerated, readmeRegenerated, ctx) {
28
+ return Object.assign(
29
+ {
30
+ updated: anyUpdated,
31
+ actionsPerformed: ctx.actionsPerformed === true,
32
+ changes: ctx.changes,
33
+ warnings: ctx.warnings || [],
34
+ changedFiles: ctx.changedFiles || [],
35
+ systemFiles: ctx.systemFiles,
36
+ datasourceFiles: ctx.datasourceFiles,
37
+ backupPaths: ctx.backupPaths || []
38
+ },
39
+ {
40
+ appKeyFixed: steps.appKeyFixed,
41
+ datasourceKeysFixed: steps.datasourceKeysFixed,
42
+ rbacFileCreated: steps.rbacFileCreated,
43
+ envTemplateRepaired: steps.envTemplateRepaired,
44
+ manifestRegenerated,
45
+ readmeRegenerated
46
+ }
47
+ );
48
+ }
49
+
50
+ /**
51
+ * @param {Object} params
52
+ * @returns {Object}
53
+ */
54
+ function prepareRepairWorkspace(params) {
55
+ const {
56
+ options,
57
+ appPath,
58
+ configPath,
59
+ dryRun,
60
+ authOption,
61
+ backupCtx,
62
+ variables,
63
+ systemFiles,
64
+ datasourceFiles: initialDatasourceFiles
65
+ } = params;
66
+ const changes = [];
67
+ const warnings = [];
68
+ const { systemFilePath, systemParsed, systemKey } = resolveSystemContext(appPath, systemFiles);
69
+ const rbacFmt = options.format === 'json' ? 'json' : 'yaml';
70
+ const { updated: keysNormalized, datasourceFiles } = normalizeDatasourceKeysAndFilenames(
71
+ appPath,
72
+ initialDatasourceFiles,
73
+ systemKey,
74
+ { variables, dryRun, changes, backupCtx }
75
+ );
76
+ return {
77
+ options,
78
+ appPath,
79
+ configPath,
80
+ dryRun,
81
+ authOption,
82
+ backupCtx,
83
+ variables,
84
+ systemFiles,
85
+ changes,
86
+ warnings,
87
+ systemFilePath,
88
+ systemParsed,
89
+ systemKey,
90
+ rbacFmt,
91
+ keysNormalized,
92
+ datasourceFiles
93
+ };
94
+ }
95
+
96
+ /**
97
+ * @param {Object} ws
98
+ * @returns {Promise<Object>}
99
+ */
100
+ async function runRepairIntegrationFileSteps(ws) {
101
+ const steps = runRepairSteps({
102
+ appPath: ws.appPath,
103
+ configPath: ws.configPath,
104
+ variables: ws.variables,
105
+ systemFilePath: ws.systemFilePath,
106
+ systemParsed: ws.systemParsed,
107
+ systemKey: ws.systemKey,
108
+ systemFiles: ws.systemFiles,
109
+ datasourceFiles: ws.datasourceFiles,
110
+ dryRun: ws.dryRun,
111
+ changes: ws.changes,
112
+ warnings: ws.warnings,
113
+ auth: ws.authOption,
114
+ format: ws.rbacFmt,
115
+ backupCtx: ws.backupCtx
116
+ });
117
+ const datasourceRepairUpdated = runDatasourceRepairs(
118
+ ws.appPath,
119
+ ws.datasourceFiles,
120
+ {
121
+ expose: Boolean(ws.options.expose),
122
+ sync: Boolean(ws.options.sync),
123
+ test: Boolean(ws.options.test),
124
+ backupCtx: ws.backupCtx
125
+ },
126
+ ws.dryRun,
127
+ ws.changes
128
+ );
129
+ const openapiActionsPerformed = await maybeRunOpenApiSyncForMcp({
130
+ options: ws.options,
131
+ dryRun: ws.dryRun,
132
+ appPath: ws.appPath,
133
+ systemKey: ws.systemKey,
134
+ datasourceFiles: ws.datasourceFiles,
135
+ changes: ws.changes
136
+ });
137
+ const { rbacMigratedFromSystem, rbacMergeUpdated } = maybeRepairRbac(
138
+ ws.appPath,
139
+ ws.systemFilePath,
140
+ ws.systemParsed,
141
+ ws.datasourceFiles,
142
+ { rbac: Boolean(ws.options.rbac), rbacFmt: ws.rbacFmt, dryRun: ws.dryRun, changes: ws.changes, backupCtx: ws.backupCtx }
143
+ );
144
+ const anyLocalUpdated =
145
+ ws.keysNormalized || steps.updated || datasourceRepairUpdated || rbacMigratedFromSystem || rbacMergeUpdated;
146
+ return {
147
+ steps,
148
+ anyLocalUpdated,
149
+ changes: ws.changes,
150
+ warnings: ws.warnings,
151
+ openapiActionsPerformed,
152
+ systemFiles: ws.systemFiles,
153
+ datasourceFiles: ws.datasourceFiles
154
+ };
155
+ }
156
+
157
+ /**
158
+ * @param {Object} params
159
+ * @returns {Promise<Object>}
160
+ */
161
+ async function runRepairLocalUpdates(params) {
162
+ const ws = prepareRepairWorkspace(params);
163
+ return runRepairIntegrationFileSteps(ws);
164
+ }
165
+
166
+ /**
167
+ * @param {Object} params
168
+ * @param {Object} local
169
+ * @returns {Promise<Object>}
170
+ */
171
+ async function finalizeRepairIntegration(params, local) {
172
+ const { appName, options, appPath, configPath, dryRun, backupPaths, backupCtx, variables, originalYamlContent } =
173
+ params;
174
+ const { steps, anyLocalUpdated, changes, warnings, openapiActionsPerformed, systemFiles, datasourceFiles } = local;
175
+ let manifestRegenerated = false;
176
+ if (anyLocalUpdated && !dryRun) {
177
+ manifestRegenerated = await persistChangesAndRegenerate({
178
+ configPath,
179
+ variables,
180
+ appName,
181
+ appPath,
182
+ changes,
183
+ originalYamlContent,
184
+ backupCtx
185
+ });
186
+ }
187
+ const readmeRegenerated = await regenerateReadmeIfRequested(
188
+ appName,
189
+ appPath,
190
+ { ...options, backupCtx },
191
+ changes
192
+ );
193
+ const changedFiles = dryRun ? backupCtx.wouldChangeFiles : backupCtx.changedFiles;
194
+ return buildRepairResult(steps, anyLocalUpdated, manifestRegenerated, readmeRegenerated, {
195
+ actionsPerformed: openapiActionsPerformed,
196
+ changes,
197
+ warnings,
198
+ changedFiles,
199
+ systemFiles,
200
+ datasourceFiles,
201
+ backupPaths
202
+ });
203
+ }
204
+
205
+ /**
206
+ * @param {Object} params
207
+ * @returns {Promise<Object>}
208
+ */
209
+ async function runRepairExternalIntegration(params) {
210
+ const local = await runRepairLocalUpdates(params);
211
+ return finalizeRepairIntegration(params, local);
212
+ }
213
+
214
+ module.exports = {
215
+ runRepairExternalIntegration
216
+ };
@@ -164,9 +164,32 @@ async function maybeSyncOpenApiFilesForMcp(opts) {
164
164
  return lines;
165
165
  }
166
166
 
167
+ /**
168
+ * @param {Object} args
169
+ * @returns {Promise<boolean>}
170
+ */
171
+ async function maybeRunOpenApiSyncForMcp({ options, dryRun, appPath, systemKey, datasourceFiles, changes }) {
172
+ if (!options.api) return false;
173
+ try {
174
+ const openapiLines = await maybeSyncOpenApiFilesForMcp({
175
+ enabled: true,
176
+ dryRun,
177
+ appPath,
178
+ systemKey,
179
+ datasourceFiles
180
+ });
181
+ openapiLines.forEach((l) => changes.push(l));
182
+ return openapiLines.length > 0;
183
+ } catch (err) {
184
+ changes.push(`OpenAPI upload for MCP failed: ${err.message}`);
185
+ return false;
186
+ }
187
+ }
188
+
167
189
  module.exports = {
168
190
  documentKeyToLocalOpenApiPath,
169
191
  maybeSyncOpenApiFilesForMcp,
192
+ maybeRunOpenApiSyncForMcp,
170
193
  syncOpenApiFilesForMcp
171
194
  };
172
195