@aifabrix/builder 2.51.0 → 2.52.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 (198) hide show
  1. package/jest.isolated-projects.js +77 -1
  2. package/jest.projects.js +5 -426
  3. package/lib/api/assistants.api.js +67 -0
  4. package/lib/api/datasources-extended.api.js +6 -6
  5. package/lib/api/evidence-authoring-helpers.js +267 -0
  6. package/lib/api/evidence-authoring.api.js +337 -0
  7. package/lib/api/evidence.api.js +57 -1
  8. package/lib/api/index.js +24 -7
  9. package/lib/api/pipeline.api.js +43 -3
  10. package/lib/api/records-bulk.api.js +28 -3
  11. package/lib/api/types/evidence.types.js +156 -8
  12. package/lib/api/work.api.js +283 -0
  13. package/lib/app/run-container-start.js +4 -0
  14. package/lib/app/run-deploy-job.js +216 -0
  15. package/lib/app/run-env-compose.js +32 -0
  16. package/lib/app/run-env-recovery.js +35 -0
  17. package/lib/app/run-helpers.js +15 -9
  18. package/lib/app/run.js +18 -2
  19. package/lib/channels/add-channel.js +54 -23
  20. package/lib/channels/approval-guides/chatgpt.js +13 -9
  21. package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
  22. package/lib/channels/channel-artifact-generator.js +14 -2
  23. package/lib/channels/channel-credential.js +3 -1
  24. package/lib/channels/channel-probe-evidence.js +79 -0
  25. package/lib/channels/channel-probe.js +265 -13
  26. package/lib/channels/channel-runtime-resolver.js +120 -29
  27. package/lib/channels/channel-status-display.js +44 -0
  28. package/lib/channels/channel-targets.js +25 -1
  29. package/lib/channels/chatgpt-runtime-contract.js +29 -0
  30. package/lib/channels/microsoft-copilot-package.js +280 -0
  31. package/lib/channels/runtime-tool-catalog.js +165 -0
  32. package/lib/cli/index.js +2 -0
  33. package/lib/cli/infra-guided-footers.js +1 -1
  34. package/lib/cli/infra-guided-public-url-warn.js +55 -0
  35. package/lib/cli/infra-guided.js +65 -56
  36. package/lib/cli/setup-app.help.js +4 -0
  37. package/lib/cli/setup-app.js +4 -0
  38. package/lib/cli/setup-app.test-commands.js +59 -28
  39. package/lib/cli/setup-external-system-help.js +8 -2
  40. package/lib/cli/setup-external-system.js +62 -12
  41. package/lib/commands/app-test.js +2 -14
  42. package/lib/commands/channel-add-setup.js +8 -2
  43. package/lib/commands/channel.js +16 -5
  44. package/lib/commands/datasource-load-export-cli.js +6 -1
  45. package/lib/commands/deploy-gate-failure-details.js +19 -17
  46. package/lib/commands/deploy-gate-orchestrator.js +3 -2
  47. package/lib/commands/dev-down.js +2 -6
  48. package/lib/commands/evidence-kind.js +65 -15
  49. package/lib/commands/governance-generate-external.js +56 -21
  50. package/lib/commands/governance-verify-external.js +2 -5
  51. package/lib/commands/lifecycle-external.js +21 -8
  52. package/lib/commands/repair-datasource-apply.js +10 -1
  53. package/lib/commands/repair-datasource-run.js +4 -2
  54. package/lib/commands/repair-datasource-test-payload.js +80 -19
  55. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
  56. package/lib/commands/repair-internal.js +9 -1
  57. package/lib/commands/repair-openapi-sync.js +34 -17
  58. package/lib/commands/role-assistant.js +371 -0
  59. package/lib/commands/test-governance-external.js +2 -5
  60. package/lib/commands/up-miso.js +61 -24
  61. package/lib/commands/upload-data-sync-finalize.js +25 -7
  62. package/lib/commands/upload-sidecar-followups.js +144 -47
  63. package/lib/commands/upload.js +43 -6
  64. package/lib/commands/verify-governance-combined-external.js +73 -9
  65. package/lib/commands/verify-governance-db.js +80 -25
  66. package/lib/commands/verify-governance-external.js +31 -10
  67. package/lib/commands/verify-operations-external.js +2 -5
  68. package/lib/commands/verify-operations-step-plan.js +31 -0
  69. package/lib/commands/verify-trust-command-action.js +2 -5
  70. package/lib/commands/wizard-core-helpers.js +7 -3
  71. package/lib/commands/wizard-core.js +78 -9
  72. package/lib/datasource/bulk-loader-run-batches.js +4 -2
  73. package/lib/datasource/bulk-loader-service.js +5 -1
  74. package/lib/datasource/datasource-identifier.js +4 -3
  75. package/lib/datasource/datasource-validate-summary.js +2 -2
  76. package/lib/datasource/integration-context.js +3 -3
  77. package/lib/datasource/resolve-app.js +90 -8
  78. package/lib/datasource/test-e2e.js +2 -2
  79. package/lib/datasource/unified-validation-run-body.js +15 -1
  80. package/lib/datasource/unified-validation-run-resolve.js +2 -2
  81. package/lib/datasource/validate.js +13 -6
  82. package/lib/external-system/deploy-helpers.js +39 -1
  83. package/lib/external-system/deploy.js +25 -11
  84. package/lib/external-system/download-helpers.js +111 -2
  85. package/lib/external-system/download-role-assistant.js +55 -0
  86. package/lib/external-system/download.js +45 -26
  87. package/lib/external-system/test-helpers.js +1 -1
  88. package/lib/external-system/test-system-level.js +21 -3
  89. package/lib/external-system/test.js +43 -7
  90. package/lib/generator/builders.js +12 -15
  91. package/lib/generator/wizard.js +51 -4
  92. package/lib/lifecycle/access-control-abac.js +121 -0
  93. package/lib/lifecycle/lifecycle-governance-options.js +4 -1
  94. package/lib/lifecycle/product-model.js +3 -2
  95. package/lib/lifecycle/report-display.js +14 -2
  96. package/lib/programmatic/capability-registry.js +20 -1
  97. package/lib/programmatic/command-options.js +1 -0
  98. package/lib/programmatic/index.js +4 -0
  99. package/lib/programmatic/manifest-patch-section.js +48 -9
  100. package/lib/programmatic/manifest-section-registry-data.js +3 -2
  101. package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
  102. package/lib/programmatic/openapi-component-schemas.js +16 -1
  103. package/lib/programmatic/openapi-description-enricher.js +3 -1
  104. package/lib/programmatic/openapi-descriptions.js +55 -5
  105. package/lib/programmatic/openapi-evidence-schemas.js +161 -0
  106. package/lib/programmatic/openapi-manifest-schemas.js +6 -2
  107. package/lib/programmatic/openapi-operation-bindings.js +99 -0
  108. package/lib/programmatic/openapi-request-schemas.js +36 -1
  109. package/lib/programmatic/openapi-result-schemas.js +1 -1
  110. package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
  111. package/lib/programmatic/openapi-schema-descriptions-core.js +1 -1
  112. package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
  113. package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
  114. package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
  115. package/lib/programmatic/openapi-spec.js +47 -28
  116. package/lib/programmatic/orchestration-openapi-meta.js +5 -0
  117. package/lib/programmatic/request-timeout.js +3 -0
  118. package/lib/programmatic/route-handler-map.js +89 -2
  119. package/lib/programmatic/run-certify.js +15 -2
  120. package/lib/programmatic/run-deploy.js +74 -0
  121. package/lib/programmatic/run-evidence-authoring.js +80 -0
  122. package/lib/programmatic/run-protection.js +124 -0
  123. package/lib/programmatic/run-template-workspace.js +28 -1
  124. package/lib/programmatic/workspace-context.js +6 -0
  125. package/lib/protection/auth-context.js +48 -6
  126. package/lib/protection/paths.js +22 -0
  127. package/lib/protection/run-protection-create-helpers.js +22 -1
  128. package/lib/protection/run-protection-create.js +1 -1
  129. package/lib/protection/sync-after-upload.js +1 -1
  130. package/lib/role-assistant/evidence-sync.js +450 -0
  131. package/lib/role-assistant/github-workflow.js +96 -0
  132. package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
  133. package/lib/role-assistant/knowledge-sync.js +485 -0
  134. package/lib/role-assistant/settings.js +126 -0
  135. package/lib/role-assistant/test-cases-evaluate.js +170 -0
  136. package/lib/role-assistant/test-cases-honesty.js +184 -0
  137. package/lib/role-assistant/test-cases.js +364 -0
  138. package/lib/role-assistant/test-runner-workhub-answers.js +221 -0
  139. package/lib/role-assistant/test-runner-workhub.js +399 -0
  140. package/lib/role-assistant/test-runner.js +500 -0
  141. package/lib/schema/application-schema.json +24 -28
  142. package/lib/schema/deployment-rules.yaml +1 -0
  143. package/lib/schema/external-datasource.schema.json +38 -4
  144. package/lib/schema/external-system.schema.json +4 -4
  145. package/lib/schema/role-assistant-settings.schema.json +108 -0
  146. package/lib/utils/api.js +52 -27
  147. package/lib/utils/app-path-resolve.js +72 -24
  148. package/lib/utils/authentication-shape.js +3 -1
  149. package/lib/utils/certify-external-workspace.js +40 -0
  150. package/lib/utils/deployment-validation.js +28 -3
  151. package/lib/utils/docker-run-ephemeral.js +115 -0
  152. package/lib/utils/enterprise-knowledge-paths.js +29 -12
  153. package/lib/utils/env-copy-keycloak.js +196 -0
  154. package/lib/utils/env-copy.js +34 -1
  155. package/lib/utils/external-system-readiness-compact-display.js +3 -0
  156. package/lib/utils/health-check-public-warn.js +31 -13
  157. package/lib/utils/health-check.js +11 -1
  158. package/lib/utils/help-builder.js +1 -0
  159. package/lib/utils/paths.js +80 -38
  160. package/lib/utils/pipeline-upload-auth-retry.js +15 -3
  161. package/lib/utils/role-assistant-paths.js +160 -0
  162. package/lib/utils/schema-loader.js +41 -0
  163. package/lib/utils/schema-resolver.js +36 -4
  164. package/lib/utils/token-manager.js +38 -29
  165. package/lib/utils/upload-sync-options.js +7 -4
  166. package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
  167. package/lib/validation/external-manifest-validator.js +31 -8
  168. package/lib/validation/reserved-system-key-prefixes.js +56 -0
  169. package/lib/validation/validate-external-file.js +8 -1
  170. package/lib/validation/validate.js +54 -19
  171. package/package.json +2 -1
  172. package/scripts/export-builder-api-evidence.js +1 -1
  173. package/templates/applications/builder-api/application.yaml +3 -3
  174. package/templates/applications/dataplane/application.yaml +24 -17
  175. package/templates/applications/dataplane/env.template +2 -2
  176. package/templates/applications/dataplane/rbac.yaml +24 -10
  177. package/templates/applications/keycloak/application.yaml +2 -2
  178. package/templates/applications/keycloak/env.template +5 -2
  179. package/templates/applications/miso-controller/application.yaml +3 -3
  180. package/templates/applications/miso-controller/env.template +17 -2
  181. package/templates/channels/chatgpt/install.template.md.hbs +82 -26
  182. package/templates/channels/chatgpt/integration/README.md +11 -7
  183. package/templates/channels/chatgpt/integration/application.json +2 -2
  184. package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
  185. package/templates/channels/chatgpt/integration/env.template +1 -1
  186. package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
  187. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
  188. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
  189. package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
  190. package/templates/channels/microsoft-copilot/integration/README.md +9 -7
  191. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  192. package/templates/channels/microsoft-copilot/integration/env.template +1 -1
  193. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
  194. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
  195. package/templates/channels/slack/install.template.md.hbs +6 -1
  196. package/templates/python/docker-compose.hbs +10 -0
  197. package/templates/typescript/docker-compose.hbs +10 -0
  198. /package/.cursor/plans/{157.1-security-quality-violations-by-type.csv → Done/157.1-security-quality-violations-by-type.csv} +0 -0
@@ -83,6 +83,7 @@ function requiresBearerAuth(handlerId) {
83
83
  function applyOperationDurationHints(capability, operation) {
84
84
  const durationByHandler = {
85
85
  runUpload: Number(process.env.MAX_UPLOAD_TIMEOUT_MS || 300000),
86
+ runDeploySystem: Number(process.env.MAX_DEPLOY_TIMEOUT_MS || 300000),
86
87
  runTest: Number(process.env.MAX_TEST_E2E_TIMEOUT_MS || 120000),
87
88
  runCertifyPillar: Number(process.env.MAX_CERTIFY_TIMEOUT_MS || 120000)
88
89
  };
@@ -133,6 +134,49 @@ function buildOperationObject(capability) {
133
134
  return operation;
134
135
  }
135
136
 
137
+ /**
138
+ * @returns {Object[]}
139
+ */
140
+ function buildOpenApiTags() {
141
+ return [
142
+ { name: 'workspace', description: 'Per-user integration workspaces (list, create, manifest, status, diff).' },
143
+ { name: 'build', description: 'Wizard generation and manifest repair.' },
144
+ { name: 'manifest', description: 'Governed section get/patch/repair for LLM and MCP workflows.' },
145
+ { name: 'validate', description: 'Local AJV validation and preview-upload (no Dataplane mutation).' },
146
+ { name: 'test', description: 'Unit, integration, and e2e test ladder.' },
147
+ { name: 'certify', description: 'Verify operations/trust/governance and lifecycle certification.' },
148
+ { name: 'apply', description: 'Dev Dataplane upload (requires external-system:publish).' },
149
+ { name: 'template', description: 'Enterprise MCP template.materializeWorkspace and workspace reset.' },
150
+ { name: 'repository', description: 'Enterprise MCP repository.sync, createReview, status, dispatchPipeline.' },
151
+ { name: 'infra', description: 'Liveness and readiness probes (no authentication).' },
152
+ {
153
+ name: 'evidence',
154
+ description:
155
+ 'BTA-facing Evidence authoring: metadata, download, upload (validate+certify), and human-gated activate.'
156
+ }
157
+ ];
158
+ }
159
+
160
+ /**
161
+ * @returns {Object}
162
+ */
163
+ function buildOpenApiInfo() {
164
+ return {
165
+ title: 'AI Fabrix Builder API',
166
+ version: '1.0.0',
167
+ description:
168
+ 'Workspace orchestration for external-system dev publish. Repository and template operations delegate to Enterprise MCP (154.1). Successful JSON responses use `{ success, data, correlationId, requestId? }` with `application/json`. Errors use RFC 7807 Problem Details (`application/problem+json`) aligned with miso-controller and dataplane.',
169
+ 'x-aifabrix': {
170
+ redocUrl: '/api/docs',
171
+ contractTier: 'public',
172
+ mcpGenerationReady: true,
173
+ rbacPreset: 'platform',
174
+ datasourceSplitMode: 'per-tag',
175
+ orchestrationDatasourceTags: listOrchestrationDatasourceTags()
176
+ }
177
+ };
178
+ }
179
+
136
180
  /**
137
181
  * @param {Object} [options]
138
182
  * @param {string} [options.publicBaseUrl]
@@ -148,39 +192,14 @@ function buildOpenApiSpec(options = {}) {
148
192
  if (!paths[pathKey]) {
149
193
  paths[pathKey] = {};
150
194
  }
151
- const method = capability.method.toLowerCase();
152
- paths[pathKey][method] = buildOperationObject(capability);
195
+ paths[pathKey][capability.method.toLowerCase()] = buildOperationObject(capability);
153
196
  }
154
197
 
155
198
  return {
156
199
  openapi: '3.0.3',
157
- info: {
158
- title: 'AI Fabrix Builder API',
159
- version: '1.0.0',
160
- description:
161
- 'Workspace orchestration for external-system dev publish. Repository and template operations delegate to Enterprise MCP (154.1). Successful JSON responses use `{ success, data, correlationId, requestId? }` with `application/json`. Errors use RFC 7807 Problem Details (`application/problem+json`) aligned with miso-controller and dataplane.',
162
- 'x-aifabrix': {
163
- redocUrl: '/api/docs',
164
- contractTier: 'public',
165
- mcpGenerationReady: true,
166
- rbacPreset: 'platform',
167
- datasourceSplitMode: 'per-tag',
168
- orchestrationDatasourceTags: listOrchestrationDatasourceTags()
169
- }
170
- },
200
+ info: buildOpenApiInfo(),
171
201
  servers: [{ url: serverUrl, description: 'Builder API public base URL' }],
172
- tags: [
173
- { name: 'workspace', description: 'Per-user integration workspaces (list, create, manifest, status, diff).' },
174
- { name: 'build', description: 'Wizard generation and manifest repair.' },
175
- { name: 'manifest', description: 'Governed section get/patch/repair for LLM and MCP workflows.' },
176
- { name: 'validate', description: 'Local AJV validation and preview-upload (no Dataplane mutation).' },
177
- { name: 'test', description: 'Unit, integration, and e2e test ladder.' },
178
- { name: 'certify', description: 'Verify operations/trust/governance and lifecycle certification.' },
179
- { name: 'apply', description: 'Dev Dataplane upload (requires external-system:publish).' },
180
- { name: 'template', description: 'Enterprise MCP template.materializeWorkspace and workspace reset.' },
181
- { name: 'repository', description: 'Enterprise MCP repository.sync, createReview, status, dispatchPipeline.' },
182
- { name: 'infra', description: 'Liveness and readiness probes (no authentication).' }
183
- ],
202
+ tags: buildOpenApiTags(),
184
203
  components: {
185
204
  securitySchemes: {
186
205
  bearerAuth: {
@@ -66,6 +66,11 @@ const ORCHESTRATION_TAG_CATALOG = Object.freeze({
66
66
  resourceType: 'channel',
67
67
  permissionClass: 'infra',
68
68
  displayName: 'Infrastructure'
69
+ },
70
+ evidence: {
71
+ resourceType: 'role-evidence',
72
+ permissionClass: 'evidence',
73
+ displayName: 'Evidence'
69
74
  }
70
75
  });
71
76
 
@@ -16,6 +16,9 @@ function getTimeoutMsForHandler(handlerId) {
16
16
  if (handlerId === 'runUpload') {
17
17
  return Number(process.env.MAX_UPLOAD_TIMEOUT_MS || 300000);
18
18
  }
19
+ if (handlerId === 'runDeploySystem') {
20
+ return Number(process.env.MAX_DEPLOY_TIMEOUT_MS || 300000);
21
+ }
19
22
  if (handlerId === 'runTest') {
20
23
  return Number(process.env.MAX_TEST_E2E_TIMEOUT_MS || 120000);
21
24
  }
@@ -14,6 +14,8 @@ const { runWizard } = require('./run-wizard');
14
14
  const { runRepair } = require('./run-repair');
15
15
  const { runValidate } = require('./run-validate');
16
16
  const { runPreviewUpload, runUpload } = require('./run-upload');
17
+ const { runDeploySystem } = require('./run-deploy');
18
+ const { runProtectionUpload, runProtectionCreate } = require('./run-protection');
17
19
  const { runTest } = require('./run-test');
18
20
  const { runCertifyPillar } = require('./run-certify');
19
21
  const { getLifecycleReport } = require('./run-lifecycle');
@@ -32,6 +34,12 @@ const {
32
34
  const { getManifestSection, listManifestSections } = require('./manifest-get-section');
33
35
  const { patchManifestSection } = require('./manifest-patch-section');
34
36
  const { runManifestRepair } = require('./manifest-repair');
37
+ const {
38
+ getEvidenceMetadata,
39
+ downloadEvidence,
40
+ uploadEvidenceCandidates,
41
+ activateEvidenceCandidates
42
+ } = require('./run-evidence-authoring');
35
43
 
36
44
  /** @type {Record<string, Function>} */
37
45
  const HANDLER_MAP = {
@@ -108,6 +116,36 @@ const HANDLER_MAP = {
108
116
  probe: body.probe === true,
109
117
  minimal: true
110
118
  }),
119
+ runDeploySystem: (params, auth, body) =>
120
+ runDeploySystem(buildContext(auth, params.systemKey), {
121
+ gate: body.gate,
122
+ subjectEmail: body.subjectEmail,
123
+ probe: body.probe === true,
124
+ sync: body.sync === true,
125
+ verbose: body.verbose === true
126
+ }),
127
+ runProtectionUpload: (params, auth, body) =>
128
+ runProtectionUpload(buildContext(auth, params.systemKey), params.datasourceKey, {
129
+ dryRun: body.dryRun === true,
130
+ sync: body.sync === true,
131
+ verbose: body.verbose === true
132
+ }),
133
+ runProtectionCreate: (params, auth, body) =>
134
+ runProtectionCreate(buildContext(auth, params.systemKey), params.datasourceKey, {
135
+ type: body.type,
136
+ dimensionKey: body.dimensionKey,
137
+ field: body.field,
138
+ fkName: body.fkName,
139
+ protectionKey: body.protectionKey,
140
+ displayName: body.displayName,
141
+ ruleKey: body.ruleKey,
142
+ principalExpression: body.principalExpression,
143
+ valueExpression: body.valueExpression,
144
+ dryRun: body.dryRun === true,
145
+ force: body.force === true,
146
+ verbose: body.verbose === true,
147
+ disabled: body.disabled === true
148
+ }),
111
149
  runTest: (params, auth, body, query) =>
112
150
  runTest(buildContext(auth, params.systemKey), {
113
151
  runType: body.runType || query.runType || 'integration',
@@ -117,7 +155,10 @@ const HANDLER_MAP = {
117
155
  runCertifyPillar(buildContext(auth, params.systemKey), params.pillar, {
118
156
  noPublish: body.noPublish === true,
119
157
  pack: body.pack,
120
- body: body.lifecycleBody
158
+ body: body.lifecycleBody,
159
+ subjectEmails: _certifySubjectEmails(body),
160
+ subjectUserIds: _stringList(body.subjectUserIds),
161
+ certificationProfile: body.certificationProfile
121
162
  }),
122
163
  getLifecycleReport: (params, auth, _body, query) =>
123
164
  getLifecycleReport(buildContext(auth, params.systemKey), {
@@ -137,7 +178,53 @@ const HANDLER_MAP = {
137
178
  runRepositoryDispatchPipeline: (params, auth, body) =>
138
179
  runRepositoryDispatchPipeline(buildContext(auth, params.systemKey), body),
139
180
  health: () => ({ status: 'ok' }),
140
- ready: () => ({ status: 'ready', dataplaneUrl: process.env.DATAPLANE_URL || null })
181
+ ready: () => ({ status: 'ready', dataplaneUrl: process.env.DATAPLANE_URL || null }),
182
+ getEvidenceMetadata: (params, auth) =>
183
+ getEvidenceMetadata(buildContext(auth, '_evidence'), params.roleKey),
184
+ downloadEvidence: (params, auth, _body, query) =>
185
+ downloadEvidence(buildContext(auth, '_evidence'), {
186
+ roleKey: params.roleKey,
187
+ resourceType: query.resourceType,
188
+ status: query.status,
189
+ kindKey: query.kindKey,
190
+ aspect: query.aspect,
191
+ search: query.search,
192
+ genericOnly: query.genericOnly === 'true',
193
+ limit: query.limit,
194
+ offset: query.offset
195
+ }),
196
+ uploadEvidenceCandidates: (params, auth, body) =>
197
+ uploadEvidenceCandidates(buildContext(auth, '_evidence'), {
198
+ ...(body && typeof body === 'object' ? body : {}),
199
+ roleKey: (body && body.roleKey) || params.roleKey
200
+ }),
201
+ activateEvidenceCandidates: (_params, auth, body) =>
202
+ activateEvidenceCandidates(buildContext(auth, '_evidence'), body)
141
203
  };
142
204
 
205
+ /**
206
+ * @param {unknown} value
207
+ * @returns {string[]}
208
+ */
209
+ function _stringList(value) {
210
+ if (!Array.isArray(value)) {
211
+ return [];
212
+ }
213
+ return value.map(item => String(item || '').trim()).filter(Boolean);
214
+ }
215
+
216
+ /**
217
+ * Normalize subject emails from CertifyRequest (singular or list).
218
+ * @param {Object} body
219
+ * @returns {string[]}
220
+ */
221
+ function _certifySubjectEmails(body) {
222
+ const fromList = _stringList(body && body.subjectEmails);
223
+ const single = body && body.subjectEmail ? String(body.subjectEmail).trim() : '';
224
+ if (single && !fromList.includes(single)) {
225
+ return [single, ...fromList];
226
+ }
227
+ return fromList;
228
+ }
229
+
143
230
  module.exports = { HANDLER_MAP };
@@ -11,6 +11,7 @@
11
11
  const { runVerifyOperationsForExternalSystem } = require('../commands/verify-operations-external');
12
12
  const { runVerifyTrustForExternalSystem } = require('../commands/verify-trust-external');
13
13
  const { runVerifyGovernanceForExternalSystem } = require('../commands/verify-governance-external');
14
+ const { runVerifyGovernanceCombined } = require('../commands/verify-governance-combined-external');
14
15
  const { runSystemLifecycle } = require('../api/lifecycle.api');
15
16
  const { runWithWorkspaceContext } = require('./workspace-context');
16
17
  const { mergeProgrammaticOptions } = require('./command-options');
@@ -45,7 +46,7 @@ async function runCertifyPillar(ctx, pillar, options = {}) {
45
46
  } else if (pillar === 'trust') {
46
47
  result = await runVerifyTrustForExternalSystem(ctx.systemKey, merged);
47
48
  } else if (pillar === 'governance') {
48
- result = await runVerifyGovernanceForExternalSystem(ctx.systemKey, merged);
49
+ result = await runGovernanceCertify(ctx.systemKey, merged);
49
50
  } else {
50
51
  result = await runSystemLifecycle(merged.dataplaneUrl, merged.authConfig, ctx.systemKey, options.body || {});
51
52
  }
@@ -54,4 +55,16 @@ async function runCertifyPillar(ctx, pillar, options = {}) {
54
55
  });
55
56
  }
56
57
 
57
- module.exports = { runCertifyPillar, PILLARS };
58
+ /**
59
+ * Governance certify: generate packs (subjects required) then verify, unless pack override.
60
+ * @param {string} systemKey
61
+ * @param {Object} options
62
+ */
63
+ async function runGovernanceCertify(systemKey, options) {
64
+ if (options.pack) {
65
+ return runVerifyGovernanceForExternalSystem(systemKey, options);
66
+ }
67
+ return runVerifyGovernanceCombined(systemKey, options);
68
+ }
69
+
70
+ module.exports = { runCertifyPillar, runGovernanceCertify, PILLARS };
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Programmatic deploy wrapper — CLI deploy parity for builder-api.
3
+ *
4
+ * @fileoverview runDeploySystem for POST /systems/:systemKey/deploy
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { deployExternalSystem } = require('../external-system/deploy');
12
+ const { runWithWorkspaceContext } = require('./workspace-context');
13
+ const { mergeProgrammaticOptions } = require('./command-options');
14
+ const { touchWorkspaceMeta } = require('./workspace-meta');
15
+
16
+ /**
17
+ * @param {unknown} gate
18
+ * @returns {string|undefined}
19
+ */
20
+ function normalizeGate(gate) {
21
+ if (gate === null || gate === undefined || gate === '') {
22
+ return undefined;
23
+ }
24
+ if (typeof gate === 'string') {
25
+ return gate.trim() || undefined;
26
+ }
27
+ return undefined;
28
+ }
29
+
30
+ /**
31
+ * Map gateFailure / deploy errors to Problem Details status codes.
32
+ * @param {Error & { gateFailure?: boolean, statusCode?: number }} err
33
+ */
34
+ function attachDeployStatus(err) {
35
+ if (err && err.gateFailure === true && !err.statusCode) {
36
+ err.statusCode = 422;
37
+ }
38
+ return err;
39
+ }
40
+
41
+ /**
42
+ * @param {import('./workspace-context').WorkspaceContext} ctx
43
+ * @param {Object} [options]
44
+ * @param {string} [options.gate] - Same as CLI --gate (operations,trust,governance,full)
45
+ * @param {string} [options.subjectEmail]
46
+ * @param {boolean} [options.probe]
47
+ * @param {boolean} [options.sync]
48
+ * @param {boolean} [options.verbose]
49
+ * @returns {Promise<Object>}
50
+ */
51
+ async function runDeploySystem(ctx, options = {}) {
52
+ const merged = mergeProgrammaticOptions(ctx, {
53
+ ...options,
54
+ gate: normalizeGate(options.gate),
55
+ subjectEmail: options.subjectEmail,
56
+ probe: options.probe === true,
57
+ sync: options.sync === true,
58
+ verbose: options.verbose === true
59
+ });
60
+ return runWithWorkspaceContext(ctx, async() => {
61
+ try {
62
+ const result = await deployExternalSystem(ctx.systemKey, merged);
63
+ touchWorkspaceMeta(ctx, { lastOperation: 'deploy' });
64
+ return result && typeof result === 'object' ? result : { deployed: true };
65
+ } catch (err) {
66
+ throw attachDeployStatus(err);
67
+ }
68
+ });
69
+ }
70
+
71
+ module.exports = {
72
+ runDeploySystem,
73
+ normalizeGate
74
+ };
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Programmatic Evidence authoring wrappers (plan 162.0 BTA-facing APIs).
3
+ *
4
+ * @fileoverview HTTP handlers → lib/api/evidence-authoring.api.js
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const {
12
+ getEvidenceMetadata: getEvidenceMetadataApi,
13
+ downloadEvidence: downloadEvidenceApi,
14
+ uploadEvidenceCandidates: uploadEvidenceCandidatesApi,
15
+ activateEvidenceCandidates: activateEvidenceCandidatesApi
16
+ } = require('../api/evidence-authoring.api');
17
+ const { mergeProgrammaticOptions } = require('./command-options');
18
+
19
+ /**
20
+ * @param {import('./workspace-context').WorkspaceContext} ctx
21
+ * @param {Object} [options]
22
+ * @returns {{ dataplaneUrl: string, authConfig: Object, apiOptions: Object }}
23
+ */
24
+ function resolveEvidenceCallContext(ctx, options = {}) {
25
+ const merged = mergeProgrammaticOptions(ctx, options);
26
+ return {
27
+ dataplaneUrl: merged.dataplaneUrl,
28
+ authConfig: merged.authConfig,
29
+ apiOptions: {
30
+ controllerUrl: merged.controllerUrl || options.controllerUrl || null
31
+ }
32
+ };
33
+ }
34
+
35
+ /**
36
+ * @param {import('./workspace-context').WorkspaceContext} ctx
37
+ * @param {string} roleKey
38
+ * @param {Object} [options]
39
+ */
40
+ async function getEvidenceMetadata(ctx, roleKey, options = {}) {
41
+ const call = resolveEvidenceCallContext(ctx, options);
42
+ return getEvidenceMetadataApi(call.dataplaneUrl, call.authConfig, roleKey, call.apiOptions);
43
+ }
44
+
45
+ /**
46
+ * @param {import('./workspace-context').WorkspaceContext} ctx
47
+ * @param {Object} params
48
+ * @param {Object} [options]
49
+ */
50
+ async function downloadEvidence(ctx, params = {}, options = {}) {
51
+ const call = resolveEvidenceCallContext(ctx, options);
52
+ return downloadEvidenceApi(call.dataplaneUrl, call.authConfig, params, call.apiOptions);
53
+ }
54
+
55
+ /**
56
+ * @param {import('./workspace-context').WorkspaceContext} ctx
57
+ * @param {Object|Object[]} payload
58
+ * @param {Object} [options]
59
+ */
60
+ async function uploadEvidenceCandidates(ctx, payload = {}, options = {}) {
61
+ const call = resolveEvidenceCallContext(ctx, options);
62
+ return uploadEvidenceCandidatesApi(call.dataplaneUrl, call.authConfig, payload, call.apiOptions);
63
+ }
64
+
65
+ /**
66
+ * @param {import('./workspace-context').WorkspaceContext} ctx
67
+ * @param {Object} payload
68
+ * @param {Object} [options]
69
+ */
70
+ async function activateEvidenceCandidates(ctx, payload = {}, options = {}) {
71
+ const call = resolveEvidenceCallContext(ctx, options);
72
+ return activateEvidenceCandidatesApi(call.dataplaneUrl, call.authConfig, payload, call.apiOptions);
73
+ }
74
+
75
+ module.exports = {
76
+ getEvidenceMetadata,
77
+ downloadEvidence,
78
+ uploadEvidenceCandidates,
79
+ activateEvidenceCandidates
80
+ };
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Programmatic protection wrappers for builder-api.
3
+ *
4
+ * @fileoverview POST …/protection/upload and …/protection/create
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const {
12
+ runProtectionUpload: uploadProtectionCli
13
+ } = require('../protection/run-commands');
14
+ const { runProtectionCreate: createProtectionCli } = require('../protection/run-protection-create');
15
+ const { resolveProtectionArgument } = require('../protection/resolve');
16
+ const { runWithWorkspaceContext } = require('./workspace-context');
17
+ const { mergeProgrammaticOptions } = require('./command-options');
18
+ const { touchWorkspaceMeta } = require('./workspace-meta');
19
+
20
+ /**
21
+ * Quiet logger for programmatic (non-TTY) protection calls.
22
+ * @returns {{ log: Function, error: Function, warn: Function }}
23
+ */
24
+ function programmaticLogger() {
25
+ return {
26
+ log() {},
27
+ error() {},
28
+ warn() {}
29
+ };
30
+ }
31
+
32
+ /**
33
+ * @param {number} code
34
+ * @param {string} operation
35
+ */
36
+ function assertExitOk(code, operation) {
37
+ if (code === 0 || code === null || code === undefined) {
38
+ return;
39
+ }
40
+ const err = new Error(`${operation} failed (exit ${code})`);
41
+ err.statusCode = 422;
42
+ throw err;
43
+ }
44
+
45
+ /**
46
+ * @param {import('./workspace-context').WorkspaceContext} ctx
47
+ * @param {string} datasourceKey
48
+ * @param {Object} [options]
49
+ * @param {boolean} [options.dryRun]
50
+ * @param {boolean} [options.sync]
51
+ * @param {boolean} [options.verbose]
52
+ * @returns {Promise<{ datasourceKey: string, uploaded: boolean, dryRun: boolean }>}
53
+ */
54
+ async function runProtectionUpload(ctx, datasourceKey, options = {}) {
55
+ const merged = mergeProgrammaticOptions(ctx, options);
56
+ const logger = programmaticLogger();
57
+ return runWithWorkspaceContext(ctx, async() => {
58
+ const resolved = resolveProtectionArgument(datasourceKey);
59
+ const code = await uploadProtectionCli(resolved.datasourceKey, resolved.manifest, {
60
+ ...merged,
61
+ manifestPath: resolved.manifestPath,
62
+ dryRun: options.dryRun === true,
63
+ sync: options.sync === true,
64
+ verbose: options.verbose === true
65
+ }, logger);
66
+ assertExitOk(code, 'Protection upload');
67
+ if (!options.dryRun) {
68
+ touchWorkspaceMeta(ctx, { lastOperation: 'protection:upload' });
69
+ }
70
+ return {
71
+ datasourceKey: resolved.datasourceKey,
72
+ uploaded: options.dryRun !== true,
73
+ dryRun: options.dryRun === true
74
+ };
75
+ });
76
+ }
77
+
78
+ /**
79
+ * @param {import('./workspace-context').WorkspaceContext} ctx
80
+ * @param {string} datasourceKey
81
+ * @param {Object} [options]
82
+ * @returns {Promise<{ datasourceKey: string, created: boolean, dryRun: boolean }>}
83
+ */
84
+ async function runProtectionCreate(ctx, datasourceKey, options = {}) {
85
+ const merged = mergeProgrammaticOptions(ctx, options);
86
+ const logger = programmaticLogger();
87
+ return runWithWorkspaceContext(ctx, async() => {
88
+ const code = await createProtectionCli(
89
+ datasourceKey,
90
+ {
91
+ ...merged,
92
+ type: options.type,
93
+ dimensionKey: options.dimensionKey,
94
+ field: options.field,
95
+ fkName: options.fkName,
96
+ protectionKey: options.protectionKey,
97
+ displayName: options.displayName,
98
+ ruleKey: options.ruleKey,
99
+ principalExpression: options.principalExpression,
100
+ valueExpression: options.valueExpression,
101
+ dryRun: options.dryRun === true,
102
+ force: options.force === true,
103
+ verbose: options.verbose === true,
104
+ disabled: options.disabled === true
105
+ },
106
+ logger
107
+ );
108
+ assertExitOk(code, 'Protection create');
109
+ if (!options.dryRun) {
110
+ touchWorkspaceMeta(ctx, { lastOperation: 'protection:create' });
111
+ }
112
+ return {
113
+ datasourceKey,
114
+ created: options.dryRun !== true,
115
+ dryRun: options.dryRun === true
116
+ };
117
+ });
118
+ }
119
+
120
+ module.exports = {
121
+ runProtectionUpload,
122
+ runProtectionCreate,
123
+ programmaticLogger
124
+ };
@@ -12,6 +12,7 @@ const { runEnterpriseMcpCapability } = require('./run-enterprise-mcp');
12
12
  const { getSystemManifest } = require('./workspace-ops');
13
13
  const { invokeEnterpriseMcpCapability } = require('./enterprise-mcp-client');
14
14
  const { runWithWorkspaceContext } = require('./workspace-context');
15
+ const { touchWorkspaceMeta } = require('./workspace-meta');
15
16
 
16
17
  /**
17
18
  * @param {import('./workspace-context').WorkspaceContext} ctx
@@ -51,11 +52,37 @@ async function getWorkspaceStatus(ctx) {
51
52
  }
52
53
 
53
54
  /**
55
+ * Diff local draft vs remote/template baseline.
56
+ * Local-first when Enterprise MCP workspace DS is not published
57
+ * (default ENTERPRISE_MCP_SYSTEM_KEY=enterprise-integration-lifecycle has no workspace DS).
58
+ *
54
59
  * @param {import('./workspace-context').WorkspaceContext} ctx
55
60
  * @param {Object} body
56
61
  */
57
62
  async function runWorkspaceDiff(ctx, body = {}) {
58
- return runEnterpriseMcpCapability(ctx, 'workspace.diff', body);
63
+ return runWithWorkspaceContext(ctx, async() => {
64
+ const local = getSystemManifest(ctx);
65
+ try {
66
+ const result = await invokeEnterpriseMcpCapability(ctx, 'workspace.diff', body);
67
+ touchWorkspaceMeta(ctx, { lastOperation: 'workspace.diff' });
68
+ return {
69
+ capabilityKey: 'workspace.diff',
70
+ source: 'enterprise-mcp',
71
+ result,
72
+ local
73
+ };
74
+ } catch (err) {
75
+ touchWorkspaceMeta(ctx, { lastOperation: 'workspace.diff' });
76
+ return {
77
+ capabilityKey: 'workspace.diff',
78
+ source: 'local',
79
+ local,
80
+ remote: { unavailable: true, message: err.message },
81
+ note:
82
+ 'Enterprise MCP workspace.diff unavailable; returning local draft snapshot only'
83
+ };
84
+ }
85
+ });
59
86
  }
60
87
 
61
88
  /**
@@ -123,6 +123,12 @@ function runWithWorkspaceContext(ctx, fn) {
123
123
  return alt;
124
124
  }
125
125
  return ctx.getIntegrationPath();
126
+ },
127
+ /** Workspace-scoped protection manifests: integration/.protection next to system folders. */
128
+ getProtectionRoot: () => {
129
+ const root = path.join(ctx.getIntegrationRoot(), '.protection');
130
+ assertPathContained(root, ctx.workspaceRoot);
131
+ return root;
126
132
  }
127
133
  };
128
134
  return runWithIntegrationPathScope(scope, fn);