@aifabrix/builder 2.45.5 → 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 (231) 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/README.md +43 -17
  5. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  6. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.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 +41 -136
  12. package/lib/api/api-response-errors.js +124 -0
  13. package/lib/api/governance-scenario-pack.api.js +159 -0
  14. package/lib/api/index.js +43 -3
  15. package/lib/api/lifecycle.api.js +77 -0
  16. package/lib/api/pipeline.api.js +3 -1
  17. package/lib/api/records-bulk.api.js +14 -1
  18. package/lib/api/validation-runner.js +3 -1
  19. package/lib/app/deploy-config.js +4 -1
  20. package/lib/app/down.js +20 -1
  21. package/lib/app/push.js +4 -5
  22. package/lib/app/register.js +7 -4
  23. package/lib/app/run-env-compose.js +84 -30
  24. package/lib/build/index.js +5 -0
  25. package/lib/certification/sync-system-certification.js +13 -6
  26. package/lib/cli/index.js +4 -0
  27. package/lib/cli/infra-guided-auth.js +109 -0
  28. package/lib/cli/infra-guided.js +84 -81
  29. package/lib/cli/setup-app.help.js +0 -31
  30. package/lib/cli/setup-app.js +28 -2
  31. package/lib/cli/setup-app.test-commands.js +5 -57
  32. package/lib/cli/setup-auth.js +25 -0
  33. package/lib/cli/setup-certification-commands.js +147 -0
  34. package/lib/cli/setup-certification.help.js +141 -0
  35. package/lib/cli/setup-environment.js +19 -2
  36. package/lib/cli/setup-external-system-help.js +62 -0
  37. package/lib/cli/setup-external-system.js +25 -3
  38. package/lib/cli/setup-infra.js +9 -1
  39. package/lib/cli/setup-platform.js +6 -1
  40. package/lib/cli/setup-secrets.js +33 -1
  41. package/lib/cli/setup-utility-help-after.js +22 -2
  42. package/lib/cli/setup-utility-repair.js +198 -0
  43. package/lib/cli/setup-utility-resolve.js +40 -4
  44. package/lib/cli/setup-utility-validate.js +197 -0
  45. package/lib/cli/setup-utility.js +4 -239
  46. package/lib/commands/auth-status-client-tokens.js +214 -0
  47. package/lib/commands/auth-status-dataplane-version.js +58 -20
  48. package/lib/commands/auth-status.js +41 -47
  49. package/lib/commands/datasource-test-trust-cli.js +27 -23
  50. package/lib/commands/datasource-unified-test-cli.js +6 -3
  51. package/lib/commands/datasource-unified-test-cli.options.js +5 -1
  52. package/lib/commands/datasource.js +7 -7
  53. package/lib/commands/dev-show-display.js +8 -1
  54. package/lib/commands/governance-cli-shared.js +94 -0
  55. package/lib/commands/governance-command-action.js +132 -0
  56. package/lib/commands/governance-download-external.js +34 -0
  57. package/lib/commands/governance-generate-external.js +71 -0
  58. package/lib/commands/governance-upload-external.js +36 -0
  59. package/lib/commands/governance-verify-external.js +64 -0
  60. package/lib/commands/governance.js +144 -0
  61. package/lib/commands/lifecycle-command-action.js +123 -0
  62. package/lib/commands/lifecycle-external.js +268 -0
  63. package/lib/commands/login.js +11 -2
  64. package/lib/commands/repair-auth-apply.js +127 -0
  65. package/lib/commands/repair-changed-files.js +41 -0
  66. package/lib/commands/repair-datasource-keys.js +4 -1
  67. package/lib/commands/repair-datasource-run.js +59 -0
  68. package/lib/commands/repair-datasource-test-payload.js +259 -0
  69. package/lib/commands/repair-datasource.js +1 -51
  70. package/lib/commands/repair-env-template.js +64 -13
  71. package/lib/commands/repair-external-run.js +216 -0
  72. package/lib/commands/repair-openapi-sync.js +23 -0
  73. package/lib/commands/repair-persist.js +4 -0
  74. package/lib/commands/repair-rbac.js +52 -1
  75. package/lib/commands/repair-steps.js +145 -0
  76. package/lib/commands/repair.js +29 -296
  77. package/lib/commands/secrets-get.js +184 -0
  78. package/lib/commands/secrets-remove-all.js +3 -1
  79. package/lib/commands/setup-image-refresh.js +146 -0
  80. package/lib/commands/setup-modes.js +158 -162
  81. package/lib/commands/setup-platform-auth.js +76 -0
  82. package/lib/commands/setup-prompts-admin.js +244 -0
  83. package/lib/commands/setup-prompts-platform-mode.js +67 -0
  84. package/lib/commands/setup-prompts-validators.js +36 -0
  85. package/lib/commands/setup-prompts.js +24 -76
  86. package/lib/commands/setup-run-context.js +45 -0
  87. package/lib/commands/setup-spinners.js +39 -0
  88. package/lib/commands/setup.js +27 -5
  89. package/lib/commands/teardown.js +39 -0
  90. package/lib/commands/test-e2e-external.js +24 -8
  91. package/lib/commands/test-governance-external.js +3 -24
  92. package/lib/commands/test-trust-external.js +20 -21
  93. package/lib/commands/up-common.js +25 -13
  94. package/lib/commands/up-dataplane.js +50 -13
  95. package/lib/commands/upload-publish-context.js +75 -0
  96. package/lib/commands/upload-sidecar-followups.js +89 -0
  97. package/lib/commands/upload.js +90 -108
  98. package/lib/commands/verify-governance-combined-external.js +61 -0
  99. package/lib/commands/verify-governance-command-action.js +64 -0
  100. package/lib/commands/verify-governance-db.js +102 -0
  101. package/lib/commands/verify-governance-external.js +77 -0
  102. package/lib/commands/verify-governance-result-builders.js +71 -0
  103. package/lib/commands/verify-operations-command-action.js +68 -0
  104. package/lib/commands/verify-operations-external.js +268 -0
  105. package/lib/commands/verify-operations-persist.js +75 -0
  106. package/lib/commands/verify-operations-step-plan.js +68 -0
  107. package/lib/commands/verify-operations-steps.js +111 -0
  108. package/lib/commands/verify-trust-command-action.js +70 -0
  109. package/lib/commands/verify-trust-external.js +67 -0
  110. package/lib/commands/wizard-config-normalizer.js +43 -23
  111. package/lib/commands/wizard-core.js +2 -0
  112. package/lib/core/admin-password-generator.js +38 -0
  113. package/lib/core/admin-secrets-bundle.js +111 -0
  114. package/lib/core/admin-secrets-env-overlay.js +62 -0
  115. package/lib/core/admin-secrets.js +32 -4
  116. package/lib/core/config-attach-extensions.js +3 -0
  117. package/lib/core/config-platform-controller.js +44 -0
  118. package/lib/core/config-setup-installation-profile.js +42 -0
  119. package/lib/core/config-setup-platform-mode.js +46 -0
  120. package/lib/core/config.js +46 -36
  121. package/lib/core/secrets-admin-env.js +6 -2
  122. package/lib/core/secrets-ensure.js +4 -0
  123. package/lib/core/secrets-env-content.js +54 -48
  124. package/lib/core/secrets-env-write.js +10 -1
  125. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  126. package/lib/core/setup-installation-profile.js +43 -0
  127. package/lib/core/setup-platform-mode.js +55 -0
  128. package/lib/datasource/agent-trust-debug-log.js +1 -1
  129. package/lib/datasource/agent-trust-run.js +5 -3
  130. package/lib/datasource/audit-evidence-extract-debug.js +30 -18
  131. package/lib/datasource/audit-evidence-extract.js +7 -4
  132. package/lib/datasource/audit-evidence-verify-matrix.js +54 -26
  133. package/lib/datasource/bulk-loader-run-batches.js +15 -3
  134. package/lib/datasource/datasource-load-export-context.js +3 -0
  135. package/lib/datasource/log-cleaner.js +2 -2
  136. package/lib/datasource/log-viewer.js +8 -8
  137. package/lib/datasource/test-e2e.js +4 -1
  138. package/lib/datasource/unified-validation-run-body.js +3 -1
  139. package/lib/datasource/unified-validation-run.js +5 -2
  140. package/lib/deployment/environment-poll-status.js +138 -0
  141. package/lib/deployment/environment.js +42 -34
  142. package/lib/external-system/download.js +21 -20
  143. package/lib/external-system/generator.js +5 -1
  144. package/lib/external-system/integration-auth-context.js +45 -0
  145. package/lib/external-system/test-auth.js +12 -5
  146. package/lib/external-system/test.js +6 -6
  147. package/lib/generator/external-controller-manifest.js +15 -0
  148. package/lib/governance/governance-cli-errors.js +96 -0
  149. package/lib/governance/governance-download-display.js +38 -0
  150. package/lib/governance/governance-run-display.js +215 -25
  151. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  152. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  153. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  154. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  155. package/lib/infrastructure/helpers.js +23 -129
  156. package/lib/infrastructure/index.js +5 -6
  157. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  158. package/lib/lifecycle/certification-progress.js +231 -0
  159. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  160. package/lib/lifecycle/exit-codes.js +24 -0
  161. package/lib/lifecycle/json-envelope.js +25 -0
  162. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  163. package/lib/lifecycle/logger-quiet.js +29 -0
  164. package/lib/lifecycle/open-api-published.js +59 -0
  165. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  166. package/lib/lifecycle/product-model.js +196 -0
  167. package/lib/lifecycle/report-display.js +471 -0
  168. package/lib/lifecycle/scenario-upload.js +191 -0
  169. package/lib/lifecycle/verify-step-progress.js +87 -0
  170. package/lib/parameters/infra-kv-discovery.js +27 -2
  171. package/lib/protection/auth-context.js +10 -4
  172. package/lib/schema/application-schema.json +1 -1
  173. package/lib/schema/datasource-test-run.schema.json +89 -6
  174. package/lib/schema/external-datasource.schema.json +2636 -2622
  175. package/lib/schema/external-system.schema.json +1089 -761
  176. package/lib/schema/infra.parameter.yaml +45 -0
  177. package/lib/schema/type/credential-field-registry.json +103 -97
  178. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  179. package/lib/utils/api.js +55 -31
  180. package/lib/utils/app-relative-path-env.js +112 -0
  181. package/lib/utils/cli-secrets-error-format.js +83 -21
  182. package/lib/utils/cli-utils.js +16 -4
  183. package/lib/utils/compose-generate-docker-compose.js +4 -0
  184. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  185. package/lib/utils/config-secrets-path.js +46 -0
  186. package/lib/utils/controller-url.js +88 -1
  187. package/lib/utils/credential-secrets-env.js +137 -9
  188. package/lib/utils/dataplane-resolver.js +129 -2
  189. package/lib/utils/declarative-url-ports.js +3 -3
  190. package/lib/utils/deployment-auth-mode.js +41 -0
  191. package/lib/utils/developer-volume-id.js +33 -0
  192. package/lib/utils/env-copy.js +14 -9
  193. package/lib/utils/env-kv-orphan-lines.js +43 -0
  194. package/lib/utils/error-formatters/network-errors.js +13 -4
  195. package/lib/utils/external-env-template.js +1 -0
  196. package/lib/utils/external-system-readiness-core.js +10 -0
  197. package/lib/utils/external-system-readiness-display.js +80 -23
  198. package/lib/utils/health-check.js +4 -1
  199. package/lib/utils/help-builder.js +11 -8
  200. package/lib/utils/keycloak-realm-ready.js +101 -0
  201. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  202. package/lib/utils/manifest-source-emit.js +2 -0
  203. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  204. package/lib/utils/platform-controller-url.js +156 -0
  205. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  206. package/lib/utils/postgres-wipe.js +9 -2
  207. package/lib/utils/resolve-env-mode.js +47 -0
  208. package/lib/utils/secrets-generator.js +31 -25
  209. package/lib/utils/secrets-missing-error.js +164 -0
  210. package/lib/utils/service-url-label.js +81 -0
  211. package/lib/utils/setup-quiet-output.js +25 -0
  212. package/lib/utils/template-helpers.js +15 -0
  213. package/lib/utils/token-manager-client-credentials.js +201 -0
  214. package/lib/utils/token-manager-messages.js +8 -0
  215. package/lib/utils/token-manager-refresh.js +12 -2
  216. package/lib/utils/token-manager.js +86 -105
  217. package/lib/utils/upload-pipeline-ui.js +12 -5
  218. package/lib/utils/upload-sync-options.js +42 -0
  219. package/lib/utils/url-declarative-public-base.js +21 -3
  220. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  221. package/lib/utils/validation-poll-ui.js +8 -0
  222. package/lib/utils/validation-run-stage.js +45 -0
  223. package/package.json +1 -1
  224. package/templates/applications/dataplane/application.yaml +2 -1
  225. package/templates/applications/dataplane/env.template +24 -1
  226. package/templates/applications/miso-controller/env.template +16 -0
  227. package/templates/external-system/README.md.hbs +74 -17
  228. package/templates/python/docker-compose.hbs +19 -18
  229. package/templates/typescript/docker-compose.hbs +18 -17
  230. package/lib/commands/test-governance-command-action.js +0 -55
  231. package/lib/commands/test-trust-command-action.js +0 -118
@@ -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
 
@@ -17,6 +17,7 @@ const { getDeployJsonPath } = require('../utils/paths');
17
17
  const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
18
18
  const { writeConfigFile, writeYamlPreservingComments, isYamlPath } = require('../utils/config-format');
19
19
  const { backupIntegrationFile } = require('../utils/integration-file-backup');
20
+ const { trackRepairWrite } = require('./repair-changed-files');
20
21
  const logger = require('../utils/logger');
21
22
  const generator = require('../generator');
22
23
  const { generateReadmeFromDeployJson } = require('../generator/split-readme');
@@ -40,6 +41,7 @@ function inferExternalReadmeFileExt(appPath) {
40
41
  async function regenerateManifest(appName, appPath, changes, backupCtx) {
41
42
  try {
42
43
  const deployPath = getDeployJsonPath(appName, 'external', true);
44
+ trackRepairWrite(deployPath, backupCtx);
43
45
  backupIntegrationFile(deployPath, backupCtx);
44
46
  const outPath = await generator.generateDeployJson(appName, { appPath });
45
47
  changes.push(`Regenerated ${path.basename(outPath)}`);
@@ -70,6 +72,7 @@ async function regenerateReadmeIfRequested(appName, appPath, options, changes) {
70
72
  const fileExt = inferExternalReadmeFileExt(appPath);
71
73
  const readmeContent = generateReadmeFromDeployJson(deployment, { fileExt });
72
74
  const readmePath = path.join(appPath, 'README.md');
75
+ trackRepairWrite(readmePath, options.backupCtx);
73
76
  if (!options.dryRun) {
74
77
  backupIntegrationFile(readmePath, options.backupCtx);
75
78
  fs.writeFileSync(readmePath, readmeContent, { mode: 0o644, encoding: 'utf8' });
@@ -84,6 +87,7 @@ async function regenerateReadmeIfRequested(appName, appPath, options, changes) {
84
87
 
85
88
  function persistChangesAndRegenerate(opts) {
86
89
  const { configPath, variables, appName, appPath, changes, originalYamlContent, backupCtx } = opts;
90
+ trackRepairWrite(configPath, backupCtx);
87
91
  backupIntegrationFile(configPath, backupCtx);
88
92
  if (originalYamlContent !== null && originalYamlContent !== undefined && typeof originalYamlContent === 'string' && isYamlPath(configPath)) {
89
93
  writeYamlPreservingComments(configPath, originalYamlContent, variables);
@@ -361,10 +361,61 @@ function mergeRbacFromDatasources(appPath, systemParsed, datasourceFiles, extrac
361
361
  }
362
362
 
363
363
  const { migrateSystemRbacIntoRbacFile } = require('./repair-rbac-migrate');
364
+ const { resolveRbacPath } = require('../utils/app-config-resolver');
365
+
366
+ /**
367
+ * @param {Object} opts
368
+ * @returns {boolean}
369
+ */
370
+ function createRbacFromSystemIfNeeded(opts) {
371
+ const { appPath, systemFilePath, systemParsed, dryRun, changes, format, backupCtx } = opts;
372
+ if (resolveRbacPath(appPath)) return false;
373
+ const rbacFromSystem = extractRbacFromSystem(systemParsed);
374
+ if (!rbacFromSystem) return false;
375
+ if (!dryRun) {
376
+ const rbacFormat = format === 'json' ? 'json' : 'yaml';
377
+ const defaultRbacPath = path.join(appPath, rbacFormat === 'json' ? 'rbac.json' : 'rbac.yaml');
378
+ writeConfigFile(defaultRbacPath, rbacFromSystem, rbacFormat);
379
+ delete systemParsed.roles;
380
+ delete systemParsed.permissions;
381
+ backupIntegrationFile(systemFilePath, backupCtx);
382
+ writeConfigFile(systemFilePath, systemParsed);
383
+ }
384
+ changes.push('Created rbac.yaml from system roles/permissions');
385
+ changes.push('Removed roles/permissions from system file (now in rbac file)');
386
+ return true;
387
+ }
388
+
389
+ /**
390
+ * @param {string} appPath
391
+ * @param {string} systemFilePath
392
+ * @param {Object} systemParsed
393
+ * @param {string[]} datasourceFiles
394
+ * @param {Object} options
395
+ * @returns {{ rbacMigratedFromSystem: boolean, rbacMergeUpdated: boolean }}
396
+ */
397
+ function maybeRepairRbac(appPath, systemFilePath, systemParsed, datasourceFiles, options) {
398
+ if (!options.rbac) return { rbacMigratedFromSystem: false, rbacMergeUpdated: false };
399
+ const { dryRun, changes, backupCtx, rbacFmt } = options;
400
+ const rbacMigratedFromSystem = migrateSystemRbacIntoRbacFile(appPath, systemFilePath, systemParsed, {
401
+ dryRun,
402
+ changes,
403
+ backupCtx
404
+ });
405
+ const rbacMergeUpdated = mergeRbacFromDatasources(appPath, systemParsed, datasourceFiles, extractRbacFromSystem, {
406
+ format: rbacFmt,
407
+ dryRun,
408
+ changes,
409
+ backupCtx
410
+ });
411
+ return { rbacMigratedFromSystem, rbacMergeUpdated };
412
+ }
364
413
 
365
414
  module.exports = {
366
415
  extractRbacFromSystem,
367
416
  getCapabilitiesFromDatasource,
368
417
  mergeRbacFromDatasources,
369
- migrateSystemRbacIntoRbacFile
418
+ migrateSystemRbacIntoRbacFile,
419
+ createRbacFromSystemIfNeeded,
420
+ maybeRepairRbac
370
421
  };
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Core repair step runner (integration block, system alignment, auth, RBAC file, env.template).
3
+ *
4
+ * @fileoverview Repair step orchestration
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { backupIntegrationFile } = require('../utils/integration-file-backup');
12
+ const { writeConfigFile } = require('../utils/config-format');
13
+ const { trackRepairWrite } = require('./repair-changed-files');
14
+ const { repairEnvTemplate, normalizeSystemFileAuthAndConfig } = require('./repair-env-template');
15
+ const { createRbacFromSystemIfNeeded } = require('./repair-rbac');
16
+ const {
17
+ alignAppKeyWithSystem,
18
+ alignDatasourceSystemKeys,
19
+ alignSystemFileDataSources,
20
+ ensureExternalIntegrationBlock,
21
+ removeAuthVarsFromConfiguration
22
+ } = require('./repair-system-alignment');
23
+ const {
24
+ isAllowedRepairAuth,
25
+ normalizeRepairAuthOption,
26
+ buildAuthenticationForRepair,
27
+ mergePreservedAuthVariables,
28
+ repairAuthChangeLabel,
29
+ auditRepairAuthenticationWarnings
30
+ } = require('./repair-auth-apply');
31
+
32
+ /**
33
+ * Apply --auth: replace system file authentication with canonical block for the given method.
34
+ * @param {Object} ctx - Context with systemParsed, systemKey, auth, dryRun, changes
35
+ * @returns {boolean} True if auth was replaced
36
+ */
37
+ function applyAuthMethod(ctx) {
38
+ if (!ctx.auth || typeof ctx.auth !== 'string') return false;
39
+ const authOption = normalizeRepairAuthOption(ctx.auth);
40
+ if (!isAllowedRepairAuth(authOption)) {
41
+ throw new Error(
42
+ 'Invalid --auth "' +
43
+ `${ctx.auth}". Allowed methods: oauth2, aad, apikey, bearerToken, basic, queryParam, oidc, hmac, none`
44
+ );
45
+ }
46
+ const existingAuth = ctx.systemParsed.authentication || ctx.systemParsed.auth || {};
47
+ const newAuth = buildAuthenticationForRepair(ctx.systemKey, authOption);
48
+ const existingVars =
49
+ existingAuth.variables && typeof existingAuth.variables === 'object'
50
+ ? existingAuth.variables
51
+ : {};
52
+ const mergedVariables = { ...newAuth.variables };
53
+ mergePreservedAuthVariables(mergedVariables, existingVars);
54
+ ctx.systemParsed.authentication = {
55
+ ...newAuth,
56
+ variables: mergedVariables
57
+ };
58
+ if (existingAuth.displayName !== undefined) {
59
+ ctx.systemParsed.authentication.displayName = existingAuth.displayName;
60
+ }
61
+ ctx.changes.push(`Set authentication method to ${repairAuthChangeLabel(authOption)}`);
62
+ return true;
63
+ }
64
+
65
+ /**
66
+ * @param {Object} ctx
67
+ * @param {Object} flags
68
+ * @returns {boolean}
69
+ */
70
+ function persistSystemFileRepair(ctx, flags) {
71
+ const systemFileUpdated =
72
+ flags.authReplaced ||
73
+ flags.systemAuthConfigNormalized ||
74
+ flags.systemDataSourcesAligned ||
75
+ flags.authVarsRemoved;
76
+ if (!systemFileUpdated) {
77
+ return false;
78
+ }
79
+ trackRepairWrite(ctx.systemFilePath, ctx.backupCtx);
80
+ if (!ctx.dryRun) {
81
+ backupIntegrationFile(ctx.systemFilePath, ctx.backupCtx);
82
+ writeConfigFile(ctx.systemFilePath, ctx.systemParsed);
83
+ }
84
+ return true;
85
+ }
86
+
87
+ /**
88
+ * @param {Object} ctx
89
+ * @returns {{ updated: boolean, appKeyFixed: boolean, datasourceKeysFixed: boolean, rbacFileCreated: boolean, envTemplateRepaired: boolean }}
90
+ */
91
+ function runRepairSteps(ctx) {
92
+ const bc = ctx.backupCtx;
93
+ let updated = ensureExternalIntegrationBlock(
94
+ ctx.variables, ctx.systemFiles, ctx.datasourceFiles, ctx.changes
95
+ );
96
+ const authReplaced = applyAuthMethod(ctx);
97
+ updated = updated || authReplaced;
98
+ const systemAuthConfigNormalized = normalizeSystemFileAuthAndConfig(
99
+ ctx.systemParsed, ctx.systemKey, ctx.changes
100
+ );
101
+ const systemDataSourcesAligned = alignSystemFileDataSources(
102
+ ctx.appPath, ctx.systemParsed, ctx.datasourceFiles, ctx.systemKey, ctx.dryRun, ctx.changes
103
+ );
104
+ const authVarsRemoved = removeAuthVarsFromConfiguration(
105
+ ctx.systemParsed, ctx.systemKey, ctx.dryRun, ctx.changes
106
+ );
107
+ const systemFileUpdated = persistSystemFileRepair(ctx, {
108
+ authReplaced,
109
+ systemAuthConfigNormalized,
110
+ systemDataSourcesAligned,
111
+ authVarsRemoved
112
+ });
113
+ updated = updated || systemFileUpdated;
114
+ const appKeyFixed = alignAppKeyWithSystem(
115
+ ctx.variables, ctx.systemKey, ctx.systemParsed, ctx.changes
116
+ );
117
+ const datasourceKeysFixed = alignDatasourceSystemKeys(
118
+ ctx.appPath, ctx.datasourceFiles, ctx.systemKey, ctx.dryRun, ctx.changes, bc
119
+ );
120
+ const rbacFileCreated = createRbacFromSystemIfNeeded({
121
+ appPath: ctx.appPath,
122
+ systemFilePath: ctx.systemFilePath,
123
+ systemParsed: ctx.systemParsed,
124
+ dryRun: ctx.dryRun,
125
+ changes: ctx.changes,
126
+ format: ctx.format,
127
+ backupCtx: bc
128
+ });
129
+ const envTemplateRepaired = repairEnvTemplate(
130
+ ctx.appPath, ctx.systemParsed, ctx.systemKey, ctx.dryRun, ctx.changes, bc
131
+ );
132
+ updated = updated || appKeyFixed || datasourceKeysFixed || rbacFileCreated || envTemplateRepaired;
133
+ auditRepairAuthenticationWarnings(ctx.systemParsed, ctx.warnings);
134
+ return {
135
+ updated,
136
+ appKeyFixed,
137
+ datasourceKeysFixed,
138
+ rbacFileCreated,
139
+ envTemplateRepaired
140
+ };
141
+ }
142
+
143
+ module.exports = {
144
+ runRepairSteps
145
+ };