@aifabrix/builder 2.50.0 → 2.51.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +374 -0
  2. package/.cursor/rules/assistant-driven-integration-build.mdc +40 -0
  3. package/integration/hubspot-test/create-hubspot.js +1 -16
  4. package/jest.isolated-projects.js +77 -1
  5. package/jest.projects.js +5 -426
  6. package/lib/api/assistants.api.js +67 -0
  7. package/lib/api/datasources-extended.api.js +6 -6
  8. package/lib/api/evidence-authoring-helpers.js +267 -0
  9. package/lib/api/evidence-authoring.api.js +337 -0
  10. package/lib/api/evidence-kinds.api.js +128 -0
  11. package/lib/api/evidence.api.js +57 -1
  12. package/lib/api/index.js +24 -7
  13. package/lib/api/pipeline.api.js +47 -3
  14. package/lib/api/records-bulk.api.js +28 -3
  15. package/lib/api/resource-types.api.js +4 -4
  16. package/lib/api/roles.api.js +121 -0
  17. package/lib/api/types/evidence-kinds.types.js +42 -0
  18. package/lib/api/types/evidence.types.js +156 -8
  19. package/lib/api/types/roles.types.js +36 -0
  20. package/lib/api/work.api.js +283 -0
  21. package/lib/app/certification-verify-rows.js +1 -1
  22. package/lib/app/run-deploy-job.js +108 -0
  23. package/lib/app/run-helpers.js +10 -3
  24. package/lib/app/run.js +8 -0
  25. package/lib/app/show.js +2 -2
  26. package/lib/certification/merge-certification-from-artifact.js +1 -1
  27. package/lib/channels/add-channel.js +54 -23
  28. package/lib/channels/approval-guides/chatgpt.js +8 -7
  29. package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
  30. package/lib/channels/channel-artifact-generator.js +16 -3
  31. package/lib/channels/channel-credential.js +3 -1
  32. package/lib/channels/channel-probe-evidence.js +79 -0
  33. package/lib/channels/channel-probe.js +265 -13
  34. package/lib/channels/channel-runtime-resolver.js +120 -29
  35. package/lib/channels/channel-status-display.js +44 -0
  36. package/lib/channels/channel-targets.js +7 -0
  37. package/lib/channels/chatgpt-runtime-contract.js +29 -0
  38. package/lib/channels/microsoft-copilot-package.js +280 -0
  39. package/lib/channels/runtime-tool-catalog.js +165 -0
  40. package/lib/cli/index.js +6 -0
  41. package/lib/cli/setup-app.js +26 -5
  42. package/lib/cli/setup-dev.js +23 -7
  43. package/lib/cli/setup-external-system-help.js +8 -2
  44. package/lib/cli/setup-external-system.js +12 -0
  45. package/lib/cli/setup-utility-help-after.js +12 -0
  46. package/lib/cli/setup-utility-validate.js +41 -2
  47. package/lib/commands/app-test.js +2 -14
  48. package/lib/commands/datasource-load-export-cli.js +6 -1
  49. package/lib/commands/deploy-gate-failure-details.js +23 -0
  50. package/lib/commands/deploy-gate-orchestrator.js +64 -18
  51. package/lib/commands/dev-cli-handlers.js +6 -3
  52. package/lib/commands/dev-down.js +2 -6
  53. package/lib/commands/dev-init.js +7 -109
  54. package/lib/commands/dev-refresh.js +197 -0
  55. package/lib/commands/evidence-kind.js +478 -0
  56. package/lib/commands/governance-command-action.js +4 -2
  57. package/lib/commands/governance-generate-external.js +56 -21
  58. package/lib/commands/lifecycle-external.js +10 -4
  59. package/lib/commands/repair-datasource-apply.js +10 -1
  60. package/lib/commands/repair-datasource-run.js +4 -2
  61. package/lib/commands/repair-datasource-test-payload.js +80 -19
  62. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
  63. package/lib/commands/repair-internal.js +9 -1
  64. package/lib/commands/repair-openapi-export.js +1 -1
  65. package/lib/commands/repair-openapi-sync.js +43 -18
  66. package/lib/commands/resource-type.js +101 -18
  67. package/lib/commands/role-assistant.js +313 -0
  68. package/lib/commands/role.js +452 -0
  69. package/lib/commands/setup.js +1 -1
  70. package/lib/commands/upload-data-sync-finalize.js +5 -4
  71. package/lib/commands/upload-sidecar-followups.js +144 -47
  72. package/lib/commands/upload.js +43 -6
  73. package/lib/commands/validate-json.js +230 -0
  74. package/lib/commands/verify-governance-combined-external.js +73 -9
  75. package/lib/commands/verify-governance-db.js +80 -25
  76. package/lib/commands/verify-governance-external.js +29 -5
  77. package/lib/commands/verify-operations-step-plan.js +78 -40
  78. package/lib/commands/wizard-core-helpers.js +7 -3
  79. package/lib/commands/wizard-core.js +78 -9
  80. package/lib/core/audit-logger.js +1 -1
  81. package/lib/core/secrets-load.js +1 -3
  82. package/lib/datasource/bulk-loader-run-batches.js +4 -2
  83. package/lib/datasource/bulk-loader-service.js +5 -1
  84. package/lib/datasource/capability/relate-operations.js +1 -1
  85. package/lib/datasource/datasource-validate-summary.js +2 -2
  86. package/lib/datasource/validate.js +13 -6
  87. package/lib/external-system/deploy-helpers.js +35 -1
  88. package/lib/external-system/deploy.js +23 -10
  89. package/lib/external-system/download-helpers.js +111 -2
  90. package/lib/external-system/download-role-assistant.js +55 -0
  91. package/lib/external-system/download.js +45 -26
  92. package/lib/external-system/test-helpers.js +1 -1
  93. package/lib/external-system/test.js +39 -3
  94. package/lib/generator/builders.js +3 -0
  95. package/lib/generator/helpers.js +13 -5
  96. package/lib/generator/split-variables.js +9 -3
  97. package/lib/generator/wizard.js +51 -4
  98. package/lib/infrastructure/helpers-docker-check.js +6 -0
  99. package/lib/infrastructure/helpers.js +1 -1
  100. package/lib/lifecycle/access-control-abac.js +121 -0
  101. package/lib/lifecycle/lifecycle-governance-options.js +4 -1
  102. package/lib/lifecycle/product-model.js +3 -2
  103. package/lib/lifecycle/report-display.js +14 -2
  104. package/lib/programmatic/auth-config.js +3 -2
  105. package/lib/programmatic/bearer-auth.js +1 -1
  106. package/lib/programmatic/capability-registry.js +20 -1
  107. package/lib/programmatic/command-options.js +1 -0
  108. package/lib/programmatic/enterprise-mcp-client.js +18 -2
  109. package/lib/programmatic/index.js +4 -0
  110. package/lib/programmatic/manifest-patch-section.js +48 -9
  111. package/lib/programmatic/manifest-section-registry-data.js +3 -2
  112. package/lib/programmatic/manifest-section-registry.js +18 -1
  113. package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
  114. package/lib/programmatic/openapi-component-schemas.js +16 -1
  115. package/lib/programmatic/openapi-description-enricher.js +3 -1
  116. package/lib/programmatic/openapi-descriptions.js +55 -5
  117. package/lib/programmatic/openapi-evidence-schemas.js +161 -0
  118. package/lib/programmatic/openapi-manifest-schemas.js +6 -2
  119. package/lib/programmatic/openapi-operation-bindings.js +99 -0
  120. package/lib/programmatic/openapi-request-schemas.js +36 -1
  121. package/lib/programmatic/openapi-result-schemas.js +29 -12
  122. package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
  123. package/lib/programmatic/openapi-schema-descriptions-core.js +6 -3
  124. package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
  125. package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
  126. package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
  127. package/lib/programmatic/openapi-schema-helpers.js +16 -1
  128. package/lib/programmatic/openapi-spec.js +48 -29
  129. package/lib/programmatic/orchestration-openapi-meta.js +5 -0
  130. package/lib/programmatic/request-timeout.js +3 -0
  131. package/lib/programmatic/route-handler-map.js +89 -2
  132. package/lib/programmatic/run-certify.js +15 -2
  133. package/lib/programmatic/run-deploy.js +74 -0
  134. package/lib/programmatic/run-evidence-authoring.js +80 -0
  135. package/lib/programmatic/run-protection.js +124 -0
  136. package/lib/programmatic/run-repository-emcp.js +11 -3
  137. package/lib/programmatic/run-template-workspace.js +42 -2
  138. package/lib/programmatic/workspace-context.js +6 -0
  139. package/lib/protection/auth-context.js +48 -6
  140. package/lib/protection/paths.js +22 -0
  141. package/lib/protection/run-protection-create-helpers.js +22 -1
  142. package/lib/protection/run-protection-create.js +1 -1
  143. package/lib/protection/sync-after-upload.js +1 -1
  144. package/lib/resolvers/evidence-kind-file.js +230 -0
  145. package/lib/resolvers/resource-type-file.js +36 -1
  146. package/lib/resolvers/role-file.js +227 -0
  147. package/lib/role-assistant/evidence-sync.js +450 -0
  148. package/lib/role-assistant/github-workflow.js +96 -0
  149. package/lib/role-assistant/knowledge-sync.js +465 -0
  150. package/lib/role-assistant/settings.js +126 -0
  151. package/lib/role-assistant/test-cases-evaluate.js +170 -0
  152. package/lib/role-assistant/test-cases-honesty.js +184 -0
  153. package/lib/role-assistant/test-cases.js +364 -0
  154. package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
  155. package/lib/role-assistant/test-runner-workhub.js +385 -0
  156. package/lib/role-assistant/test-runner.js +500 -0
  157. package/lib/schema/application-schema.json +36 -2
  158. package/lib/schema/deployment-rules.yaml +1 -0
  159. package/lib/schema/external-datasource.schema.json +121 -4
  160. package/lib/schema/external-system.schema.json +20 -6
  161. package/lib/schema/infra.parameter.yaml +45 -1
  162. package/lib/schema/role-assistant-settings.schema.json +108 -0
  163. package/lib/schema/type/credential-field-registry.json +19 -3
  164. package/lib/utils/api.js +52 -21
  165. package/lib/utils/app-path-resolve.js +72 -24
  166. package/lib/utils/app-register-config.js +4 -3
  167. package/lib/utils/authentication-shape.js +3 -1
  168. package/lib/utils/cli-sync-options.js +59 -3
  169. package/lib/utils/cli-utils.js +11 -3
  170. package/lib/utils/deployment-validation.js +28 -3
  171. package/lib/utils/dev-ca-install.js +5 -6
  172. package/lib/utils/dev-config.js +2 -2
  173. package/lib/utils/dev-list-sort.js +145 -0
  174. package/lib/utils/dev-platform-tls.js +0 -63
  175. package/lib/utils/docker-build.js +46 -27
  176. package/lib/utils/docker-daemon-tls-ca.js +1 -1
  177. package/lib/utils/docker-exec.js +91 -6
  178. package/lib/utils/docker-run-ephemeral.js +55 -0
  179. package/lib/utils/enterprise-knowledge-paths.js +29 -12
  180. package/lib/utils/env-copy-keycloak.js +196 -0
  181. package/lib/utils/env-copy.js +35 -4
  182. package/lib/utils/error-formatters/error-parser.js +0 -3
  183. package/lib/utils/external-env-template.js +1 -1
  184. package/lib/utils/external-system-readiness-compact-blocks.js +28 -1
  185. package/lib/utils/external-system-readiness-compact-display.js +3 -0
  186. package/lib/utils/health-check-request.js +2 -3
  187. package/lib/utils/help-builder.js +4 -0
  188. package/lib/utils/image-name.js +22 -4
  189. package/lib/utils/logger.js +37 -4
  190. package/lib/utils/manifest-source-emit.js +1 -1
  191. package/lib/utils/paths.js +98 -26
  192. package/lib/utils/pipeline-upload-auth-retry.js +19 -3
  193. package/lib/utils/postgres-platform-bootstrap.js +24 -17
  194. package/lib/utils/postgres-wipe.js +5 -2
  195. package/lib/utils/remote-dev-auth.js +39 -4
  196. package/lib/utils/resolve-docker-image-ref.js +36 -6
  197. package/lib/utils/role-assistant-paths.js +98 -0
  198. package/lib/utils/schema-loader.js +41 -0
  199. package/lib/utils/schema-resolver.js +36 -4
  200. package/lib/utils/token-manager.js +47 -22
  201. package/lib/utils/upload-sync-options.js +17 -3
  202. package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
  203. package/lib/validation/external-manifest-validator.js +31 -8
  204. package/lib/validation/openapi-contract-surface-validator.js +4 -0
  205. package/lib/validation/validate-external-file.js +8 -1
  206. package/lib/validation/validator.js +6 -5
  207. package/package.json +35 -12
  208. package/scripts/export-builder-api-evidence.js +3 -3
  209. package/scripts/lib/plan-actor.js +2 -2
  210. package/scripts/lib/plan-meta.js +1 -1
  211. package/scripts/lib/plan-parse.js +1 -1
  212. package/scripts/lib/plan-prevalidate-gates.js +12 -1
  213. package/scripts/lib/plan-run-report.js +13 -2
  214. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +10 -10
  215. package/templates/applications/dataplane/application.yaml +47 -17
  216. package/templates/applications/dataplane/env.template +28 -9
  217. package/templates/applications/dataplane/rbac.yaml +78 -2
  218. package/templates/applications/miso-controller/application.yaml +10 -1
  219. package/templates/applications/miso-controller/env.template +13 -8
  220. package/templates/applications/miso-controller/rbac.yaml +72 -0
  221. package/templates/channels/chatgpt/install.template.md.hbs +42 -24
  222. package/templates/channels/chatgpt/integration/README.md +10 -7
  223. package/templates/channels/chatgpt/integration/application.json +2 -2
  224. package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
  225. package/templates/channels/chatgpt/integration/env.template +1 -1
  226. package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
  227. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
  228. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
  229. package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
  230. package/templates/channels/microsoft-copilot/integration/README.md +9 -7
  231. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  232. package/templates/channels/microsoft-copilot/integration/env.template +1 -1
  233. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
  234. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
  235. package/templates/channels/slack/integration/slack-system.json +60 -2
  236. package/templates/python/requirements.txt +1 -1
@@ -213,12 +213,21 @@ async function generateWizardFiles(appName, systemConfig, datasourceConfigs, sys
213
213
  const fmt = format === 'json' ? 'json' : 'yaml';
214
214
  const ext = FORMAT_EXT[fmt] || '.yaml';
215
215
  const { appPath, finalSystemKey, updatedSystemConfig, updatedDatasourceConfigs } = await prepareWizardContext(appName, systemConfig, datasourceConfigs);
216
+ const newDatasourceFileNames = await writeDatasourceYamlFiles(appPath, finalSystemKey, updatedDatasourceConfigs, fmt);
217
+ const existingDatasourceFileNames = listExistingDatasourceFileNames(appPath, finalSystemKey, ext);
218
+ const datasourceFileNames = [...new Set([...existingDatasourceFileNames, ...newDatasourceFileNames])];
219
+ const existingDatasourceKeys = datasourceFileNames
220
+ .map((name) => datasourceFileNameToKey(name, finalSystemKey, ext))
221
+ .filter(Boolean);
216
222
  const systemConfigWithDataSourcesKeys = {
217
223
  ...updatedSystemConfig,
218
- dataSources: updatedDatasourceConfigs.map(ds => ds.key)
224
+ dataSources: [...new Set([
225
+ ...(Array.isArray(updatedSystemConfig.dataSources) ? updatedSystemConfig.dataSources : []),
226
+ ...existingDatasourceKeys,
227
+ ...updatedDatasourceConfigs.map((ds) => ds.key).filter(Boolean)
228
+ ])]
219
229
  };
220
230
  const systemFilePath = await writeSystemYamlFile(appPath, finalSystemKey, systemConfigWithDataSourcesKeys, fmt);
221
- const datasourceFileNames = await writeDatasourceYamlFiles(appPath, finalSystemKey, updatedDatasourceConfigs, fmt);
222
231
  const systemFileName = `${finalSystemKey}-system${ext}`;
223
232
  const configFiles = await generateConfigFilesForWizard({
224
233
  appPath,
@@ -234,7 +243,7 @@ async function generateWizardFiles(appName, systemConfig, datasourceConfigs, sys
234
243
  return {
235
244
  appPath,
236
245
  systemFilePath,
237
- datasourceFilePaths: datasourceFileNames.map(name => path.join(appPath, name)),
246
+ datasourceFilePaths: newDatasourceFileNames.map(name => path.join(appPath, name)),
238
247
  ...configFiles
239
248
  };
240
249
  } catch (error) {
@@ -242,6 +251,40 @@ async function generateWizardFiles(appName, systemConfig, datasourceConfigs, sys
242
251
  }
243
252
  }
244
253
 
254
+ /**
255
+ * List existing datasource files so add-datasource merges instead of replacing.
256
+ * @param {string} appPath
257
+ * @param {string} finalSystemKey
258
+ * @param {string} ext
259
+ * @returns {string[]}
260
+ */
261
+ function listExistingDatasourceFileNames(appPath, finalSystemKey, ext) {
262
+ const fsSync = require('fs');
263
+ if (!fsSync.existsSync(appPath)) {
264
+ return [];
265
+ }
266
+ const prefix = `${finalSystemKey}-datasource-`;
267
+ return fsSync
268
+ .readdirSync(appPath)
269
+ .filter((name) => name.startsWith(prefix) && name.endsWith(ext));
270
+ }
271
+
272
+ /**
273
+ * Map `systemKey-datasource-companies.json` → `systemKey-companies`.
274
+ * @param {string} fileName
275
+ * @param {string} finalSystemKey
276
+ * @param {string} ext
277
+ * @returns {string|null}
278
+ */
279
+ function datasourceFileNameToKey(fileName, finalSystemKey, ext) {
280
+ const prefix = `${finalSystemKey}-datasource-`;
281
+ if (!fileName.startsWith(prefix) || !fileName.endsWith(ext)) {
282
+ return null;
283
+ }
284
+ const segment = fileName.slice(prefix.length, fileName.length - ext.length);
285
+ return segment ? `${finalSystemKey}-${segment}` : null;
286
+ }
287
+
245
288
  function mergeAppAndExternalIntegration(variables, appName, systemFileName, datasourceFileNames, systemConfig) {
246
289
  if (!variables.app) {
247
290
  variables.app = {
@@ -257,10 +300,14 @@ function mergeAppAndExternalIntegration(variables, appName, systemFileName, data
257
300
  if (!variables.deployment) {
258
301
  variables.deployment = { controllerUrl: '', environment: 'dev' };
259
302
  }
303
+ const existingDs = Array.isArray(variables.externalIntegration?.dataSources)
304
+ ? variables.externalIntegration.dataSources
305
+ : [];
306
+ const mergedDataSources = [...new Set([...existingDs, ...datasourceFileNames])];
260
307
  variables.externalIntegration = {
261
308
  schemaBasePath: './',
262
309
  systems: [systemFileName],
263
- dataSources: datasourceFileNames,
310
+ dataSources: mergedDataSources,
264
311
  autopublish: true,
265
312
  version: systemConfig.version || '1.0.0'
266
313
  };
@@ -53,6 +53,12 @@ function formatDockerInfrastructureFailure(detail) {
53
53
  */
54
54
  async function checkDockerAvailability() {
55
55
  await ensureDevCertsIfNeededForRemoteDocker();
56
+ try {
57
+ const { ensureDevCertFreshIfDue } = require('../commands/dev-refresh');
58
+ await ensureDevCertFreshIfDue({ quiet: true });
59
+ } catch {
60
+ // Best-effort renew for remote Docker TLS; docker check reports real failures
61
+ }
56
62
  try {
57
63
  await dockerUtils.ensureDockerAndCompose();
58
64
  } catch (error) {
@@ -181,7 +181,7 @@ function generatePgAdminConfig(infraDir, postgresPassword) {
181
181
  * @returns {string} Escaped string
182
182
  */
183
183
  function escapePgString(s) {
184
- if (s === null || s === undefined || typeof s !== 'string') return '';
184
+ if (typeof s !== 'string') return '';
185
185
  return s.replace(/'/g, '\'\'');
186
186
  }
187
187
 
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @fileoverview Parse accessControl.abac from local integration datasource files (505.0).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
10
+ const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
11
+ const { loadConfigFile } = require('../utils/config-format');
12
+ const { discoverIntegrationFiles, buildEffectiveDatasourceFiles } = require('../commands/repair-internal');
13
+
14
+ /**
15
+ * @param {Object|null|undefined} config
16
+ * @returns {{ applicability: 'required'|'notApplicable', rationale: string|null }}
17
+ */
18
+ function parseAccessControlAbac(config) {
19
+ const access = config && typeof config === 'object' ? config.accessControl : null;
20
+ const abac = access && typeof access === 'object' ? access.abac : null;
21
+ if (!abac || typeof abac !== 'object') {
22
+ return { applicability: 'required', rationale: null };
23
+ }
24
+ const raw = String(abac.applicability || '').trim();
25
+ const applicability = raw === 'notApplicable' ? 'notApplicable' : 'required';
26
+ const rationale =
27
+ typeof abac.rationale === 'string' && abac.rationale.trim()
28
+ ? abac.rationale.trim()
29
+ : null;
30
+ return { applicability, rationale };
31
+ }
32
+
33
+ /**
34
+ * @param {string} appPath
35
+ * @returns {Object}
36
+ */
37
+ function loadApplicationVariables(appPath) {
38
+ try {
39
+ const configPath = resolveApplicationConfigPath(appPath);
40
+ if (!fs.existsSync(configPath)) {
41
+ return {};
42
+ }
43
+ return loadConfigFile(configPath) || {};
44
+ } catch {
45
+ return {};
46
+ }
47
+ }
48
+
49
+ /**
50
+ * @param {string} filePath
51
+ * @param {Map<string, Object>} map
52
+ */
53
+ function putDatasourceConfig(filePath, map) {
54
+ if (!fs.existsSync(filePath)) {
55
+ return;
56
+ }
57
+ try {
58
+ const parsed = loadConfigFile(filePath);
59
+ const key = parsed && typeof parsed.key === 'string' ? parsed.key.trim() : '';
60
+ if (key) {
61
+ map.set(key, parsed);
62
+ }
63
+ } catch {
64
+ // skip unreadable datasource files
65
+ }
66
+ }
67
+
68
+ /**
69
+ * @param {string} systemKey
70
+ * @returns {Map<string, Object>}
71
+ */
72
+ function loadDatasourceConfigsByKey(systemKey) {
73
+ const appPath = resolveExternalSystemPath(systemKey);
74
+ const map = new Map();
75
+ if (!fs.existsSync(appPath)) {
76
+ return map;
77
+ }
78
+ const variables = loadApplicationVariables(appPath);
79
+ const { datasourceFiles: discovered } = discoverIntegrationFiles(appPath);
80
+ const datasourceFiles = buildEffectiveDatasourceFiles(
81
+ appPath,
82
+ discovered,
83
+ variables.externalIntegration?.dataSources
84
+ );
85
+ for (const fileName of datasourceFiles) {
86
+ putDatasourceConfig(path.join(appPath, fileName), map);
87
+ }
88
+ return map;
89
+ }
90
+
91
+ /**
92
+ * @param {string} systemKey
93
+ * @param {string[]} keys
94
+ * @returns {string[]}
95
+ */
96
+ function filterAbacRequiredDatasourceKeys(systemKey, keys) {
97
+ const configs = loadDatasourceConfigsByKey(systemKey);
98
+ return (keys || []).filter(key => {
99
+ const abac = parseAccessControlAbac(configs.get(key) || null);
100
+ return abac.applicability !== 'notApplicable';
101
+ });
102
+ }
103
+
104
+ /**
105
+ * @param {string} systemKey
106
+ * @param {string[]} keys
107
+ * @returns {boolean}
108
+ */
109
+ function allDatasourcesAbacNotApplicable(systemKey, keys) {
110
+ if (!keys || keys.length === 0) {
111
+ return false;
112
+ }
113
+ return filterAbacRequiredDatasourceKeys(systemKey, keys).length === 0;
114
+ }
115
+
116
+ module.exports = {
117
+ parseAccessControlAbac,
118
+ loadDatasourceConfigsByKey,
119
+ filterAbacRequiredDatasourceKeys,
120
+ allDatasourcesAbacNotApplicable
121
+ };
@@ -21,7 +21,10 @@ function lifecycleHasGovernanceSubjects(options) {
21
21
  * @returns {boolean}
22
22
  */
23
23
  function lifecycleGovernanceNeedsWork(report) {
24
- return report?.governance?.verdict !== VERDICT.VERIFIED;
24
+ const verdict = report?.governance?.verdict;
25
+ return (
26
+ verdict !== VERDICT.VERIFIED && verdict !== VERDICT.NOT_APPLICABLE
27
+ );
25
28
  }
26
29
 
27
30
  /**
@@ -7,7 +7,7 @@
7
7
 
8
8
  'use strict';
9
9
 
10
- /** @typedef {'VERIFIED'|'FAILED'|'NOT_VERIFIED'} LifecycleVerdict */
10
+ /** @typedef {'VERIFIED'|'FAILED'|'NOT_VERIFIED'|'NOT_APPLICABLE'} LifecycleVerdict */
11
11
 
12
12
  /** @typedef {'BRONZE'|'SILVER'|'GOLD'|'PLATINUM'|'NONE'} CertificationLevel */
13
13
 
@@ -16,7 +16,8 @@
16
16
  const VERDICT = Object.freeze({
17
17
  VERIFIED: 'VERIFIED',
18
18
  FAILED: 'FAILED',
19
- NOT_VERIFIED: 'NOT_VERIFIED'
19
+ NOT_VERIFIED: 'NOT_VERIFIED',
20
+ NOT_APPLICABLE: 'NOT_APPLICABLE'
20
21
  });
21
22
 
22
23
  const CERTIFICATION_LEVEL = Object.freeze({
@@ -58,9 +58,13 @@ function displayHeroMetric(label, percent) {
58
58
  logger.log('');
59
59
  }
60
60
 
61
- function displayVerdictLine(verdict, verifiedLabel, failedLabel) {
61
+ function displayVerdictLine(verdict, verifiedLabel, failedLabel, notApplicableLabel) {
62
62
  if (verdict === VERDICT.VERIFIED) {
63
63
  logger.log(formatSuccessParagraph(verifiedLabel));
64
+ } else if (verdict === VERDICT.NOT_APPLICABLE) {
65
+ logger.log(
66
+ formatSuccessParagraph(notApplicableLabel || 'NOT APPLICABLE')
67
+ );
64
68
  } else {
65
69
  logger.log(formatBlockingError(failedLabel));
66
70
  }
@@ -196,7 +200,12 @@ function displayVerifyGovernanceTTY(systemKey, result, opts = {}) {
196
200
  logger.log(chalk.white('Enforcement Scenarios'));
197
201
  logger.log(chalk.cyan.bold(`${passed}/${total} Passed`));
198
202
  logger.log('');
199
- displayVerdictLine(result.verdict, 'GOVERNANCE VERIFIED', 'GOVERNANCE FAILED');
203
+ displayVerdictLine(
204
+ result.verdict,
205
+ 'GOVERNANCE VERIFIED',
206
+ 'GOVERNANCE FAILED',
207
+ 'GOVERNANCE NOT APPLICABLE'
208
+ );
200
209
  if (opts.details === true) {
201
210
  displayGovernanceVerboseDetails(result);
202
211
  }
@@ -209,6 +218,9 @@ function formatPillarLine(label, verdict) {
209
218
  if (verdict === VERDICT.FAILED) {
210
219
  return `${label.padEnd(24)}${failureGlyph()} FAILED`;
211
220
  }
221
+ if (verdict === VERDICT.NOT_APPLICABLE) {
222
+ return `${label.padEnd(24)}— NOT APPLICABLE`;
223
+ }
212
224
  return `${label.padEnd(24)}— NOT VERIFIED`;
213
225
  }
214
226
 
@@ -10,7 +10,7 @@
10
10
 
11
11
  /**
12
12
  * @param {string} bearerToken
13
- * @returns {{ type: 'bearer', token: string }}
13
+ * @returns {{ type: 'bearer', token: string, preserveBearer: true }}
14
14
  */
15
15
  function buildBearerAuthConfig(bearerToken) {
16
16
  if (!bearerToken || typeof bearerToken !== 'string') {
@@ -20,7 +20,8 @@ function buildBearerAuthConfig(bearerToken) {
20
20
  if (!token) {
21
21
  throw new Error('Bearer token is required');
22
22
  }
23
- return { type: 'bearer', token };
23
+ // Token-forward: caller JWT must reach dataplane CIP/pipeline unchanged (no API_KEY swap).
24
+ return { type: 'bearer', token, preserveBearer: true };
24
25
  }
25
26
 
26
27
  /**
@@ -20,7 +20,7 @@ function extractBearerToken(req) {
20
20
  if (!auth || typeof auth !== 'string') {
21
21
  return null;
22
22
  }
23
- const match = auth.match(/^Bearer\s+(.+)$/i);
23
+ const match = auth.match(/^Bearer\s+([^\s]+)\s*$/i);
24
24
  return match ? match[1].trim() : null;
25
25
  }
26
26
 
@@ -40,6 +40,21 @@ const CAPABILITIES = Object.freeze([
40
40
  { method: 'POST', route: '/systems/:systemKey/certify/:pillar', handlerId: 'runCertifyPillar', operationType: 'certify', mutating: true },
41
41
  { method: 'GET', route: '/systems/:systemKey/lifecycle', handlerId: 'getLifecycleReport', operationType: 'certify.read', mutating: false },
42
42
  { method: 'POST', route: '/systems/:systemKey/upload', handlerId: 'runUpload', operationType: 'apply.dev', mutating: true },
43
+ { method: 'POST', route: '/systems/:systemKey/deploy', handlerId: 'runDeploySystem', operationType: 'apply.prod', mutating: true },
44
+ {
45
+ method: 'POST',
46
+ route: '/systems/:systemKey/datasources/:datasourceKey/protection/upload',
47
+ handlerId: 'runProtectionUpload',
48
+ operationType: 'apply.protection',
49
+ mutating: true
50
+ },
51
+ {
52
+ method: 'POST',
53
+ route: '/systems/:systemKey/datasources/:datasourceKey/protection/create',
54
+ handlerId: 'runProtectionCreate',
55
+ operationType: 'apply.protection',
56
+ mutating: true
57
+ },
43
58
  { method: 'POST', route: '/systems/:systemKey/template/materialize', handlerId: 'materializeTemplate', operationType: 'template', mutating: true },
44
59
  { method: 'GET', route: '/systems/:systemKey/workspace/status', handlerId: 'getWorkspaceStatus', operationType: 'workspace.read', mutating: false },
45
60
  { method: 'POST', route: '/systems/:systemKey/workspace/diff', handlerId: 'runWorkspaceDiff', operationType: 'workspace.read', mutating: false },
@@ -49,7 +64,11 @@ const CAPABILITIES = Object.freeze([
49
64
  { method: 'GET', route: '/systems/:systemKey/repository/status', handlerId: 'getRepositoryStatus', operationType: 'repository.read', mutating: false },
50
65
  { method: 'POST', route: '/systems/:systemKey/repository/dispatch-pipeline', handlerId: 'runRepositoryDispatchPipeline', operationType: 'repository', mutating: true },
51
66
  { method: 'GET', route: '/health', handlerId: 'health', operationType: 'infra', mutating: false },
52
- { method: 'GET', route: '/health/ready', handlerId: 'ready', operationType: 'infra', mutating: false }
67
+ { method: 'GET', route: '/health/ready', handlerId: 'ready', operationType: 'infra', mutating: false },
68
+ { method: 'GET', route: '/evidence/:roleKey/metadata', handlerId: 'getEvidenceMetadata', operationType: 'evidence.read', mutating: false },
69
+ { method: 'GET', route: '/evidence/:roleKey/download', handlerId: 'downloadEvidence', operationType: 'evidence.read', mutating: false },
70
+ { method: 'POST', route: '/evidence/:roleKey/upload', handlerId: 'uploadEvidenceCandidates', operationType: 'evidence.write', mutating: true },
71
+ { method: 'POST', route: '/evidence/:roleKey/activate', handlerId: 'activateEvidenceCandidates', operationType: 'evidence.activate', mutating: true }
53
72
  ]);
54
73
 
55
74
  /**
@@ -22,6 +22,7 @@ function mergeProgrammaticOptions(ctx, options = {}) {
22
22
  authConfig: auth.authConfig,
23
23
  dataplaneUrl: auth.dataplaneUrl,
24
24
  environment: auth.environment,
25
+ controllerUrl: ctx.controllerUrl || options.controllerUrl,
25
26
  silentResolve: true,
26
27
  minimal: options.minimal !== false
27
28
  };
@@ -19,6 +19,21 @@ function resolveEnterpriseMcpSystemKey() {
19
19
  return (process.env.ENTERPRISE_MCP_SYSTEM_KEY || 'enterprise-integration-lifecycle').trim();
20
20
  }
21
21
 
22
+ /**
23
+ * COM-generated Enterprise MCP tools sometimes use OpenAPI operationId forms
24
+ * (e.g. repository.getrepositorystatus) instead of canonical capability keys
25
+ * (repository.status). Map canonical → published name when present.
26
+ *
27
+ * @type {Record<string, string>}
28
+ */
29
+ const ENTERPRISE_MCP_TOOL_ALIASES = {
30
+ 'repository.status': 'repository.getrepositorystatus',
31
+ 'repository.sync': 'repository.runrepositorysync',
32
+ 'repository.createReview': 'repository.runrepositorycreatereview',
33
+ 'repository.dispatchPipeline': 'repository.runrepositorydispatchpipeline',
34
+ 'template.materializeWorkspace': 'template.runwizard'
35
+ };
36
+
22
37
  /**
23
38
  * @param {string} capabilityKey
24
39
  * @returns {{ resourceType: string, toolName: string }}
@@ -29,9 +44,10 @@ function parseCapabilityKey(capabilityKey) {
29
44
  if (dotIndex <= 0) {
30
45
  throw new Error('capabilityKey must be resourceType.operation form');
31
46
  }
47
+ const toolName = ENTERPRISE_MCP_TOOL_ALIASES[key] || key;
32
48
  return {
33
49
  resourceType: key.slice(0, dotIndex),
34
- toolName: key
50
+ toolName
35
51
  };
36
52
  }
37
53
 
@@ -42,7 +58,7 @@ function parseCapabilityKey(capabilityKey) {
42
58
  * @returns {string}
43
59
  */
44
60
  function buildMcpExecutePath(systemKey, resourceType, toolName) {
45
- return `/api/v1/mcp/${encodeURIComponent(systemKey)}/${encodeURIComponent(resourceType)}/tools/${encodeURIComponent(toolName)}/execute`;
61
+ return `/api/v1/mcp/servers/${encodeURIComponent(systemKey)}/${encodeURIComponent(resourceType)}/tools/${encodeURIComponent(toolName)}/execute`;
46
62
  }
47
63
 
48
64
  /**
@@ -6,6 +6,8 @@
6
6
 
7
7
  const workspaceContext = require('./workspace-context');
8
8
  const runUpload = require('./run-upload');
9
+ const runDeploy = require('./run-deploy');
10
+ const runProtection = require('./run-protection');
9
11
  const runWizard = require('./run-wizard');
10
12
  const runRepair = require('./run-repair');
11
13
  const runValidate = require('./run-validate');
@@ -29,6 +31,8 @@ const enterpriseMcpClient = require('./enterprise-mcp-client');
29
31
  module.exports = {
30
32
  ...workspaceContext,
31
33
  ...runUpload,
34
+ ...runDeploy,
35
+ ...runProtection,
32
36
  ...runWizard,
33
37
  ...runRepair,
34
38
  ...runValidate,
@@ -33,7 +33,7 @@ const { runWithWorkspaceContext } = require('./workspace-context');
33
33
 
34
34
  /**
35
35
  * @param {import('./workspace-context').WorkspaceContext} ctx
36
- * @param {{ sectionKey: string, datasourceKey?: string, intent: string, patch: Object, mode: string, sectionReadToken: string, dryRun?: boolean }} body
36
+ * @param {{ sectionKey: string, datasourceKey?: string, intent: string, patch: Object, mode: string, sectionReadToken?: string, dryRun?: boolean }} body
37
37
  */
38
38
  async function patchManifestSection(ctx, body) {
39
39
  return runWithWorkspaceContext(ctx, async() => {
@@ -43,10 +43,11 @@ async function patchManifestSection(ctx, body) {
43
43
 
44
44
  const loaded = loadTargetDocument(ctx, def.target, body.datasourceKey);
45
45
  const beforeValue = extractSectionValue(loaded.doc, def);
46
- verifyReadToken(body, beforeValue);
46
+ assertSectionReadToken(body, beforeValue);
47
47
 
48
48
  const draft = JSON.parse(JSON.stringify(loaded.doc));
49
- injectSectionValue(draft, def, body.patch, body.mode === 'replace' ? 'replace' : 'merge');
49
+ const patchValue = normalizeSectionPatch(def, body.patch);
50
+ injectSectionValue(draft, def, patchValue, body.mode === 'replace' ? 'replace' : 'merge');
50
51
  validateDraftDocument(def.target, draft);
51
52
 
52
53
  const afterValue = extractSectionValue(draft, def);
@@ -58,6 +59,47 @@ async function patchManifestSection(ctx, body) {
58
59
  });
59
60
  }
60
61
 
62
+ /**
63
+ * Scalar single-path sections (resourceType, entityType, syncBridge, …) accept
64
+ * `{ value: <scalar> }` or `{ <leaf>: <scalar> }` because OpenAPI types `patch` as object.
65
+ *
66
+ * @param {import('./manifest-section-registry-data').SectionDefinition} def
67
+ * @param {Object} patch
68
+ * @returns {*}
69
+ */
70
+ function normalizeSectionPatch(def, patch) {
71
+ if (!def || !Array.isArray(def.paths) || def.paths.length !== 1) {
72
+ return patch;
73
+ }
74
+ if (!patch || typeof patch !== 'object' || Array.isArray(patch)) {
75
+ return patch;
76
+ }
77
+ const keys = Object.keys(patch);
78
+ if (keys.length !== 1) {
79
+ return patch;
80
+ }
81
+ const leaf = def.paths[0].includes('.') ? def.paths[0].split('.').pop() : def.paths[0];
82
+ if (keys[0] === 'value' || keys[0] === leaf) {
83
+ return patch[keys[0]];
84
+ }
85
+ return patch;
86
+ }
87
+
88
+ /**
89
+ * When sectionReadToken is omitted, trust the freshly loaded section value
90
+ * (CLI patch-section parity — plan 496.2). Explicit tokens still verify.
91
+ *
92
+ * @param {Object} body
93
+ * @param {*} beforeValue
94
+ */
95
+ function assertSectionReadToken(body, beforeValue) {
96
+ const token = typeof body.sectionReadToken === 'string' ? body.sectionReadToken.trim() : '';
97
+ if (!token) {
98
+ return;
99
+ }
100
+ verifyReadToken({ ...body, sectionReadToken: token }, beforeValue);
101
+ }
102
+
61
103
  /**
62
104
  * @param {Object} body
63
105
  * @param {*} beforeValue
@@ -162,11 +204,6 @@ function assertPatchRequest(body) {
162
204
  err.statusCode = 400;
163
205
  throw err;
164
206
  }
165
- if (!body.sectionReadToken) {
166
- const err = new Error('sectionReadToken is required — GET the section first');
167
- err.statusCode = 400;
168
- throw err;
169
- }
170
207
  if (body.mode !== 'merge' && body.mode !== 'replace') {
171
208
  const err = new Error('mode must be merge or replace');
172
209
  err.statusCode = 400;
@@ -248,5 +285,7 @@ function buildRiskHints(sectionKey, patch, downstream) {
248
285
  }
249
286
 
250
287
  module.exports = {
251
- patchManifestSection
288
+ patchManifestSection,
289
+ normalizeSectionPatch,
290
+ assertSectionReadToken
252
291
  };
@@ -226,11 +226,12 @@ const SECTION_DEFINITIONS = Object.freeze({
226
226
  },
227
227
  'datasource.dimensions': {
228
228
  target: 'datasource',
229
- paths: ['fieldMappings.dimensions'],
229
+ // Canonical root is top-level `dimensions` (fieldMappings.dimensions removed).
230
+ paths: ['dimensions'],
230
231
  mutable: true,
231
232
  required: false,
232
233
  sourceOfTruth: true,
233
- repairHooks: ['fieldMappings']
234
+ repairHooks: []
234
235
  },
235
236
  'datasource.abac': {
236
237
  target: 'datasource',
@@ -22,6 +22,15 @@ const SCHEMA_DIR = path.join(__dirname, '..', 'schema');
22
22
  let systemSchemaDoc = null;
23
23
  /** @type {Object|null} */
24
24
  let datasourceSchemaDoc = null;
25
+ const PROTOTYPE_POLLUTION_KEYS = new Set(['__proto__', 'prototype', 'constructor']);
26
+
27
+ /**
28
+ * @param {string} part
29
+ * @returns {boolean}
30
+ */
31
+ function isSafePathPart(part) {
32
+ return typeof part === 'string' && part.length > 0 && !PROTOTYPE_POLLUTION_KEYS.has(part);
33
+ }
25
34
 
26
35
  function getSystemSchemaDoc() {
27
36
  if (!systemSchemaDoc) {
@@ -90,10 +99,18 @@ function getPathValue(doc, dottedPath) {
90
99
  */
91
100
  function setPathValue(doc, dottedPath, value) {
92
101
  const parts = dottedPath.split('.');
102
+ for (const part of parts) {
103
+ if (!isSafePathPart(part)) {
104
+ const err = new Error(`Unsafe path segment: ${part}`);
105
+ err.statusCode = 400;
106
+ throw err;
107
+ }
108
+ }
93
109
  let current = doc;
94
110
  for (let i = 0; i < parts.length - 1; i += 1) {
95
111
  const part = parts[i];
96
- if (current[part] === null || current[part] === undefined || typeof current[part] !== 'object') {
112
+ const next = Object.prototype.hasOwnProperty.call(current, part) ? current[part] : undefined;
113
+ if (next === null || next === undefined || typeof next !== 'object' || Array.isArray(next)) {
97
114
  current[part] = {};
98
115
  }
99
116
  current = current[part];
@@ -0,0 +1,44 @@
1
+ /**
2
+ * OpenAPI result schemas for deploy and protection apply routes.
3
+ *
4
+ * @fileoverview Split from openapi-result-schemas for file-size limits
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { frozenObject, schemaRef } = require('./openapi-schema-helpers');
12
+
13
+ /** @type {Record<string, Object>} */
14
+ const OPENAPI_APPLY_RESULT_SCHEMAS = {
15
+ DeployResultData: frozenObject(
16
+ {
17
+ deployed: { type: 'boolean' },
18
+ deploymentId: { type: 'string' },
19
+ environment: { type: 'string' },
20
+ systemKey: schemaRef('SystemKey')
21
+ },
22
+ ['deployed']
23
+ ),
24
+ ProtectionUploadData: frozenObject(
25
+ {
26
+ datasourceKey: schemaRef('DatasourceKey'),
27
+ uploaded: { type: 'boolean' },
28
+ dryRun: { type: 'boolean' }
29
+ },
30
+ ['datasourceKey', 'uploaded', 'dryRun']
31
+ ),
32
+ ProtectionCreateData: frozenObject(
33
+ {
34
+ datasourceKey: schemaRef('DatasourceKey'),
35
+ created: { type: 'boolean' },
36
+ dryRun: { type: 'boolean' }
37
+ },
38
+ ['datasourceKey', 'created', 'dryRun']
39
+ )
40
+ };
41
+
42
+ module.exports = {
43
+ OPENAPI_APPLY_RESULT_SCHEMAS
44
+ };