@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
@@ -0,0 +1,128 @@
1
+ /**
2
+ * @fileoverview Evidence kinds API (Controller /api/v1/evidence-kinds)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { ApiClient } = require('./index');
10
+
11
+ const BASE = '/api/v1/evidence-kinds';
12
+
13
+ /**
14
+ * List evidence kinds (paginated)
15
+ * @requiresPermission {Controller} evidence-kinds:read
16
+ * @async
17
+ * @param {string} controllerUrl
18
+ * @param {Object} authConfig
19
+ * @param {Object} [options]
20
+ * @returns {Promise<Object>}
21
+ */
22
+ async function listEvidenceKinds(controllerUrl, authConfig, options = {}) {
23
+ const client = new ApiClient(controllerUrl, authConfig);
24
+ return await client.get(BASE, { params: options });
25
+ }
26
+
27
+ /**
28
+ * Get an evidence kind by id or key
29
+ * @requiresPermission {Controller} evidence-kinds:read
30
+ * @async
31
+ * @param {string} controllerUrl
32
+ * @param {Object} authConfig
33
+ * @param {string} evidenceKindIdOrKey
34
+ * @returns {Promise<Object>}
35
+ */
36
+ async function getEvidenceKind(controllerUrl, authConfig, evidenceKindIdOrKey) {
37
+ const client = new ApiClient(controllerUrl, authConfig);
38
+ return await client.get(`${BASE}/${encodeURIComponent(evidenceKindIdOrKey)}`);
39
+ }
40
+
41
+ /**
42
+ * @param {Object} payload
43
+ * @param {string} key
44
+ * @param {*} value
45
+ * @returns {void}
46
+ */
47
+ function assignOptionalString(payload, key, value) {
48
+ if (value !== undefined && value !== null && value !== '') {
49
+ payload[key] = value;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * @param {import('./types/evidence-kinds.types').CreateEvidenceKindRequest} body
55
+ * @returns {Object}
56
+ */
57
+ function buildCreateEvidenceKindBody(body) {
58
+ const payload = {
59
+ key: body.key,
60
+ domainKey: body.domainKey,
61
+ displayName: body.displayName,
62
+ purpose: body.purpose
63
+ };
64
+ assignOptionalString(payload, 'description', body.description);
65
+ assignOptionalString(payload, 'expectedUse', body.expectedUse);
66
+ assignOptionalString(payload, 'source', body.source);
67
+ if (body.defaultSeverity) payload.defaultSeverity = body.defaultSeverity;
68
+ if (body.requiresApproval !== undefined && body.requiresApproval !== null) {
69
+ payload.requiresApproval = Boolean(body.requiresApproval);
70
+ }
71
+ if (body.isActive !== undefined && body.isActive !== null) {
72
+ payload.isActive = Boolean(body.isActive);
73
+ }
74
+ if (body.sortOrder !== undefined && body.sortOrder !== null) {
75
+ payload.sortOrder = Number(body.sortOrder);
76
+ }
77
+ if (body.metadata && typeof body.metadata === 'object') {
78
+ payload.metadata = body.metadata;
79
+ }
80
+ return payload;
81
+ }
82
+
83
+ /**
84
+ * Create an evidence kind
85
+ * @requiresPermission {Controller} evidence-kinds:create
86
+ * @async
87
+ * @param {string} controllerUrl
88
+ * @param {Object} authConfig
89
+ * @param {import('./types/evidence-kinds.types').CreateEvidenceKindRequest} body
90
+ * @returns {Promise<Object>}
91
+ */
92
+ async function createEvidenceKind(controllerUrl, authConfig, body) {
93
+ const client = new ApiClient(controllerUrl, authConfig);
94
+ return await client.post(BASE, { body: buildCreateEvidenceKindBody(body) });
95
+ }
96
+
97
+ /**
98
+ * Create-if-missing (idempotent): if an evidence kind already exists for the key, return success.
99
+ * @requiresPermission {Controller} evidence-kinds:read + evidence-kinds:create
100
+ * @async
101
+ * @param {string} controllerUrl
102
+ * @param {Object} authConfig
103
+ * @param {import('./types/evidence-kinds.types').CreateEvidenceKindRequest} body
104
+ * @returns {Promise<{ created: boolean, response: Object }>}
105
+ */
106
+ async function createEvidenceKindIdempotent(controllerUrl, authConfig, body) {
107
+ const key = String(body?.key || '').trim();
108
+ if (!key) {
109
+ throw new Error('Evidence kind key is required');
110
+ }
111
+ const getRes = await getEvidenceKind(controllerUrl, authConfig, key);
112
+ if (getRes && getRes.success === true) {
113
+ return { created: false, response: getRes };
114
+ }
115
+ const createRes = await createEvidenceKind(controllerUrl, authConfig, body);
116
+ if (createRes && createRes.success === true) {
117
+ return { created: true, response: createRes };
118
+ }
119
+ const msg = createRes?.error || createRes?.formattedError || 'Failed to create evidence kind';
120
+ throw new Error(msg);
121
+ }
122
+
123
+ module.exports = {
124
+ listEvidenceKinds,
125
+ getEvidenceKind,
126
+ createEvidenceKind,
127
+ createEvidenceKindIdempotent
128
+ };
@@ -162,11 +162,67 @@ async function loadEvidenceBulk(dataplaneUrl, authConfig, rows, options = {}) {
162
162
  };
163
163
  }
164
164
 
165
+ /**
166
+ * @param {string} dataplaneUrl
167
+ * @param {Object} authConfig
168
+ * @param {string} evidenceIdOrKey
169
+ * @param {Object} [options]
170
+ * @returns {Promise<Object>}
171
+ */
172
+ async function validateEvidenceCandidate(dataplaneUrl, authConfig, evidenceIdOrKey, options = {}) {
173
+ const client = createEvidenceClient(dataplaneUrl, authConfig, options);
174
+ const res = assertApiSuccess(
175
+ await client.post(`${BASE}/${encodeURIComponent(evidenceIdOrKey)}/validate`, { body: {} }),
176
+ 'Validate evidence'
177
+ );
178
+ return unwrapBody(res);
179
+ }
180
+
181
+ /**
182
+ * @param {string} dataplaneUrl
183
+ * @param {Object} authConfig
184
+ * @param {string} evidenceIdOrKey
185
+ * @param {{ comment?: string }} [payload]
186
+ * @param {Object} [options]
187
+ * @returns {Promise<Object>}
188
+ */
189
+ async function certifyEvidenceCandidate(dataplaneUrl, authConfig, evidenceIdOrKey, payload = {}, options = {}) {
190
+ const client = createEvidenceClient(dataplaneUrl, authConfig, options);
191
+ const body = {};
192
+ if (payload && typeof payload.comment === 'string' && payload.comment.trim()) {
193
+ body.comment = payload.comment.trim();
194
+ }
195
+ const res = assertApiSuccess(
196
+ await client.post(`${BASE}/${encodeURIComponent(evidenceIdOrKey)}/certify`, { body }),
197
+ 'Certify evidence'
198
+ );
199
+ return unwrapBody(res);
200
+ }
201
+
202
+ /**
203
+ * @param {string} dataplaneUrl
204
+ * @param {Object} authConfig
205
+ * @param {string} evidenceIdOrKey
206
+ * @param {Object} [options]
207
+ * @returns {Promise<Object>}
208
+ */
209
+ async function activateEvidenceCandidate(dataplaneUrl, authConfig, evidenceIdOrKey, options = {}) {
210
+ const client = createEvidenceClient(dataplaneUrl, authConfig, options);
211
+ const res = assertApiSuccess(
212
+ await client.post(`${BASE}/${encodeURIComponent(evidenceIdOrKey)}/activate`, { body: {} }),
213
+ 'Activate evidence'
214
+ );
215
+ return unwrapBody(res);
216
+ }
217
+
165
218
  module.exports = {
166
219
  listEvidence,
167
220
  getEvidence,
168
221
  insertEvidence,
169
222
  updateEvidence,
170
223
  deleteEvidence,
171
- loadEvidenceBulk
224
+ loadEvidenceBulk,
225
+ validateEvidenceCandidate,
226
+ certifyEvidenceCandidate,
227
+ activateEvidenceCandidate
172
228
  };
package/lib/api/index.js CHANGED
@@ -79,7 +79,7 @@ class ApiClient {
79
79
  headers['Content-Type'] = 'application/json';
80
80
  }
81
81
 
82
- // User token (bearer) → Authorization: Bearer; application token (client-token) → x-client-token
82
+ // User token (bearer) → Authorization: Bearer; app token (client-token) → x-client-token.
83
83
  if (this.authConfig.type === 'bootstrap' && this.authConfig.encryptionKey) {
84
84
  headers['x-encryption-key'] = String(this.authConfig.encryptionKey);
85
85
  } else if (this.authConfig.token) {
@@ -160,7 +160,10 @@ class ApiClient {
160
160
  transport.timeoutMs = options.timeoutMs;
161
161
  }
162
162
 
163
- if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
163
+ if (
164
+ this.authConfig.type === 'bearer' ||
165
+ this.authConfig.type === 'client-token'
166
+ ) {
164
167
  // Pass full authConfig to enable proper token refresh using controller URL
165
168
  return await authenticatedApiCall(url, transport, this.authConfig);
166
169
  }
@@ -213,7 +216,10 @@ class ApiClient {
213
216
  requestOptions.timeoutMs = options.timeoutMs;
214
217
  }
215
218
 
216
- if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
219
+ if (
220
+ this.authConfig.type === 'bearer' ||
221
+ this.authConfig.type === 'client-token'
222
+ ) {
217
223
  // Pass full authConfig to enable proper token refresh using controller URL
218
224
  return await authenticatedApiCall(url, requestOptions, this.authConfig);
219
225
  }
@@ -241,7 +247,9 @@ class ApiClient {
241
247
  body: formData
242
248
  };
243
249
 
244
- const hasToken = this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token';
250
+ const hasToken =
251
+ this.authConfig.type === 'bearer' ||
252
+ this.authConfig.type === 'client-token';
245
253
  if (hasToken && this.authConfig.token) {
246
254
  return await authenticatedApiCall(url, requestOptions, this.authConfig);
247
255
  }
@@ -272,7 +280,10 @@ class ApiClient {
272
280
  requestOptions.body = JSON.stringify(options.body);
273
281
  }
274
282
 
275
- if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
283
+ if (
284
+ this.authConfig.type === 'bearer' ||
285
+ this.authConfig.type === 'client-token'
286
+ ) {
276
287
  // Pass full authConfig to enable proper token refresh using controller URL
277
288
  return await authenticatedApiCall(url, requestOptions, this.authConfig);
278
289
  }
@@ -303,7 +314,10 @@ class ApiClient {
303
314
  requestOptions.body = JSON.stringify(options.body);
304
315
  }
305
316
 
306
- if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
317
+ if (
318
+ this.authConfig.type === 'bearer' ||
319
+ this.authConfig.type === 'client-token'
320
+ ) {
307
321
  // Pass full authConfig to enable proper token refresh using controller URL
308
322
  return await authenticatedApiCall(url, requestOptions, this.authConfig);
309
323
  }
@@ -329,7 +343,10 @@ class ApiClient {
329
343
  headers
330
344
  };
331
345
 
332
- if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
346
+ if (
347
+ this.authConfig.type === 'bearer' ||
348
+ this.authConfig.type === 'client-token'
349
+ ) {
333
350
  // Pass full authConfig to enable proper token refresh using controller URL
334
351
  return await authenticatedApiCall(url, requestOptions, this.authConfig);
335
352
  }
@@ -247,11 +247,53 @@ async function testDatasourceViaPipeline({ dataplaneUrl, systemKey, datasourceKe
247
247
  * @returns {Promise<Object>} API envelope `{ success, data }` where `data` is PublicationResult (uploadId, system, datasources, generateMcpContract, …)
248
248
  * @throws {Error} If upload fails
249
249
  */
250
- async function uploadApplicationViaPipeline(dataplaneUrl, authConfig, payload) {
250
+ /** Multi-datasource publish often exceeds the default 30s HTTP timeout. */
251
+ const PIPELINE_UPLOAD_TIMEOUT_MS = 300000;
252
+ const PIPELINE_UPLOAD_TIMEOUT_MIN_MS = 5000;
253
+ const PIPELINE_UPLOAD_TIMEOUT_MAX_MS = 45 * 60 * 1000;
254
+
255
+ /**
256
+ * Resolve pipeline upload timeout (ms) for HTTP + dataplane budget.
257
+ * @param {number|string|undefined|null} raw
258
+ * @returns {number}
259
+ */
260
+ function resolvePipelineUploadTimeoutMs(raw) {
261
+ if (raw === undefined || raw === null || raw === '') {
262
+ return PIPELINE_UPLOAD_TIMEOUT_MS;
263
+ }
264
+ const n = typeof raw === 'number' ? raw : Number(raw);
265
+ if (!Number.isFinite(n) || n <= 0) {
266
+ throw new Error(
267
+ `--timeout must be a positive number of milliseconds (got ${String(raw)})`
268
+ );
269
+ }
270
+ return Math.min(
271
+ PIPELINE_UPLOAD_TIMEOUT_MAX_MS,
272
+ Math.max(PIPELINE_UPLOAD_TIMEOUT_MIN_MS, Math.floor(n))
273
+ );
274
+ }
275
+
276
+ /**
277
+ * @param {string} dataplaneUrl
278
+ * @param {Object} authConfig
279
+ * @param {Object} payload
280
+ * @param {Object} [options]
281
+ * @param {number|string} [options.timeoutMs] - HTTP + dataplane publish budget (default 300000)
282
+ */
283
+ async function uploadApplicationViaPipeline(
284
+ dataplaneUrl,
285
+ authConfig,
286
+ payload,
287
+ options = {}
288
+ ) {
251
289
  const body = { ...payload, status: payload.status ?? 'draft' };
290
+ const timeoutMs = resolvePipelineUploadTimeoutMs(options.timeoutMs);
252
291
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
253
292
  return await client.post('/api/v1/pipeline/upload', {
254
- body
293
+ body,
294
+ timeoutMs,
295
+ params: { timeoutMs: String(timeoutMs) },
296
+ headers: { 'X-Aifabrix-Timeout-Ms': String(timeoutMs) }
255
297
  });
256
298
  }
257
299
 
@@ -264,6 +306,8 @@ module.exports = {
264
306
  validatePipelineConfig,
265
307
  testSystemViaPipeline,
266
308
  testDatasourceViaPipeline,
267
- uploadApplicationViaPipeline
309
+ uploadApplicationViaPipeline,
310
+ resolvePipelineUploadTimeoutMs,
311
+ PIPELINE_UPLOAD_TIMEOUT_MS
268
312
  };
269
313
 
@@ -7,6 +7,26 @@
7
7
  const { createDataplaneApiClient } = require('./index');
8
8
  const { unwrapApiData } = require('../utils/external-system-readiness-core');
9
9
 
10
+ /** Default HTTP budget for records/bulk (ABAC projection can exceed 30s under load). */
11
+ const DEFAULT_BULK_SYNC_TIMEOUT_MS = 120000;
12
+
13
+ /**
14
+ * Resolve per-request timeout for bulk sync.
15
+ * @param {Object} [transportOpts]
16
+ * @returns {number}
17
+ */
18
+ function resolveBulkSyncTimeoutMs(transportOpts = {}) {
19
+ const raw = transportOpts.timeoutMs ?? transportOpts.timeout;
20
+ if (raw === undefined || raw === null || raw === '') {
21
+ return DEFAULT_BULK_SYNC_TIMEOUT_MS;
22
+ }
23
+ const n = typeof raw === 'number' ? raw : parseInt(String(raw), 10);
24
+ if (!Number.isFinite(n) || n <= 0) {
25
+ return DEFAULT_BULK_SYNC_TIMEOUT_MS;
26
+ }
27
+ return n;
28
+ }
29
+
10
30
  /**
11
31
  * Bulk sync external records for a datasource.
12
32
  * POST /api/v1/data-storage/{sourceIdOrKey}/records/bulk
@@ -16,13 +36,16 @@ const { unwrapApiData } = require('../utils/external-system-readiness-core');
16
36
  * @param {string} sourceIdOrKey
17
37
  * @param {Object} authConfig
18
38
  * @param {import('./types/records-bulk.types').ExternalRecordBulkRequest} body
39
+ * @param {Object} [transportOpts]
40
+ * @param {number} [transportOpts.timeoutMs] - Per-request HTTP timeout (default 120000)
19
41
  * @returns {Promise<import('./types/records-bulk.types').ExternalRecordBulkResponse>}
20
42
  */
21
- async function bulkSyncRecords(dataplaneUrl, sourceIdOrKey, authConfig, body) {
43
+ async function bulkSyncRecords(dataplaneUrl, sourceIdOrKey, authConfig, body, transportOpts = {}) {
22
44
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
45
+ const timeoutMs = resolveBulkSyncTimeoutMs(transportOpts);
23
46
  const res = await client.post(
24
47
  `/api/v1/data-storage/${encodeURIComponent(sourceIdOrKey)}/records/bulk`,
25
- { body }
48
+ { body, timeoutMs }
26
49
  );
27
50
  if (res && res.success === false) {
28
51
  const msg =
@@ -39,5 +62,7 @@ async function bulkSyncRecords(dataplaneUrl, sourceIdOrKey, authConfig, body) {
39
62
  }
40
63
 
41
64
  module.exports = {
42
- bulkSyncRecords
65
+ bulkSyncRecords,
66
+ resolveBulkSyncTimeoutMs,
67
+ DEFAULT_BULK_SYNC_TIMEOUT_MS
43
68
  };
@@ -12,7 +12,7 @@ const BASE = '/api/v1/resource-types';
12
12
 
13
13
  /**
14
14
  * List resource types (paginated)
15
- * @requiresPermission {Controller} resourceTypes:read
15
+ * @requiresPermission {Controller} resource-types:read
16
16
  * @async
17
17
  * @function listResourceTypes
18
18
  * @param {string} controllerUrl
@@ -34,7 +34,7 @@ async function listResourceTypes(controllerUrl, authConfig, options = {}) {
34
34
 
35
35
  /**
36
36
  * Get a resource type by id or key
37
- * @requiresPermission {Controller} resourceTypes:read
37
+ * @requiresPermission {Controller} resource-types:read
38
38
  * @async
39
39
  * @function getResourceType
40
40
  * @param {string} controllerUrl
@@ -49,7 +49,7 @@ async function getResourceType(controllerUrl, authConfig, resourceTypeIdOrKey) {
49
49
 
50
50
  /**
51
51
  * Create a resource type
52
- * @requiresPermission {Controller} resourceTypes:create
52
+ * @requiresPermission {Controller} resource-types:create
53
53
  * @async
54
54
  * @function createResourceType
55
55
  * @param {string} controllerUrl
@@ -75,7 +75,7 @@ async function createResourceType(controllerUrl, authConfig, body) {
75
75
 
76
76
  /**
77
77
  * Create-if-missing (idempotent): if a resource type already exists for the key, return success.
78
- * @requiresPermission {Controller} resourceTypes:read + resourceTypes:create
78
+ * @requiresPermission {Controller} resource-types:read + resource-types:create
79
79
  * @async
80
80
  * @function createResourceTypeIdempotent
81
81
  * @param {string} controllerUrl
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @fileoverview Business roles API (Controller /api/v1/roles)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { ApiClient } = require('./index');
10
+
11
+ const BASE = '/api/v1/roles';
12
+
13
+ /**
14
+ * List business roles (paginated)
15
+ * @requiresPermission {Controller} roles:read
16
+ * @async
17
+ * @param {string} controllerUrl
18
+ * @param {Object} authConfig
19
+ * @param {Object} [options]
20
+ * @returns {Promise<Object>}
21
+ */
22
+ async function listRoles(controllerUrl, authConfig, options = {}) {
23
+ const client = new ApiClient(controllerUrl, authConfig);
24
+ return await client.get(BASE, { params: options });
25
+ }
26
+
27
+ /**
28
+ * Get a business role by id or key
29
+ * @requiresPermission {Controller} roles:read
30
+ * @async
31
+ * @param {string} controllerUrl
32
+ * @param {Object} authConfig
33
+ * @param {string} roleIdOrKey
34
+ * @returns {Promise<Object>}
35
+ */
36
+ async function getRole(controllerUrl, authConfig, roleIdOrKey) {
37
+ const client = new ApiClient(controllerUrl, authConfig);
38
+ return await client.get(`${BASE}/${encodeURIComponent(roleIdOrKey)}`);
39
+ }
40
+
41
+ /**
42
+ * @param {Object} payload
43
+ * @param {string} key
44
+ * @param {*} value
45
+ * @returns {void}
46
+ */
47
+ function assignOptionalString(payload, key, value) {
48
+ if (value !== undefined && value !== null && value !== '') {
49
+ payload[key] = value;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * @param {import('./types/roles.types').CreateBusinessRoleRequest} body
55
+ * @returns {Object}
56
+ */
57
+ function buildCreateRoleBody(body) {
58
+ const payload = {
59
+ key: body.key,
60
+ domainKey: body.domainKey,
61
+ displayName: body.displayName
62
+ };
63
+ assignOptionalString(payload, 'description', body.description);
64
+ assignOptionalString(payload, 'purpose', body.purpose);
65
+ assignOptionalString(payload, 'businessOutcome', body.businessOutcome);
66
+ assignOptionalString(payload, 'source', body.source);
67
+ if (body.isActive !== undefined && body.isActive !== null) {
68
+ payload.isActive = Boolean(body.isActive);
69
+ }
70
+ if (body.metadata && typeof body.metadata === 'object') {
71
+ payload.metadata = body.metadata;
72
+ }
73
+ return payload;
74
+ }
75
+
76
+ /**
77
+ * Create a business role
78
+ * @requiresPermission {Controller} roles:create
79
+ * @async
80
+ * @param {string} controllerUrl
81
+ * @param {Object} authConfig
82
+ * @param {import('./types/roles.types').CreateBusinessRoleRequest} body
83
+ * @returns {Promise<Object>}
84
+ */
85
+ async function createRole(controllerUrl, authConfig, body) {
86
+ const client = new ApiClient(controllerUrl, authConfig);
87
+ return await client.post(BASE, { body: buildCreateRoleBody(body) });
88
+ }
89
+
90
+ /**
91
+ * Create-if-missing (idempotent): if a role already exists for the key, return success.
92
+ * @requiresPermission {Controller} roles:read + roles:create
93
+ * @async
94
+ * @param {string} controllerUrl
95
+ * @param {Object} authConfig
96
+ * @param {import('./types/roles.types').CreateBusinessRoleRequest} body
97
+ * @returns {Promise<{ created: boolean, response: Object }>}
98
+ */
99
+ async function createRoleIdempotent(controllerUrl, authConfig, body) {
100
+ const key = String(body?.key || '').trim();
101
+ if (!key) {
102
+ throw new Error('Role key is required');
103
+ }
104
+ const getRes = await getRole(controllerUrl, authConfig, key);
105
+ if (getRes && getRes.success === true) {
106
+ return { created: false, response: getRes };
107
+ }
108
+ const createRes = await createRole(controllerUrl, authConfig, body);
109
+ if (createRes && createRes.success === true) {
110
+ return { created: true, response: createRes };
111
+ }
112
+ const msg = createRes?.error || createRes?.formattedError || 'Failed to create role';
113
+ throw new Error(msg);
114
+ }
115
+
116
+ module.exports = {
117
+ listRoles,
118
+ getRole,
119
+ createRole,
120
+ createRoleIdempotent
121
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @fileoverview Evidence kind API type definitions (Controller /api/v1/evidence-kinds)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ /**
8
+ * Create evidence kind request body (API shape uses domainKey)
9
+ * @typedef {Object} CreateEvidenceKindRequest
10
+ * @property {string} key - Catalog key (lowercase, digits, underscores)
11
+ * @property {string} domainKey - Enterprise domain key
12
+ * @property {string} displayName - Human-readable name
13
+ * @property {string} purpose - Purpose (required by Controller)
14
+ * @property {string} [description] - Optional description
15
+ * @property {string} [expectedUse] - Expected use guidance
16
+ * @property {string} [defaultSeverity] - low | medium | high | critical
17
+ * @property {boolean} [requiresApproval] - Approval flag
18
+ * @property {boolean} [isActive] - Active flag
19
+ * @property {number} [sortOrder] - Sort order
20
+ * @property {string} [source] - Provenance source
21
+ * @property {Object} [metadata] - Extensions
22
+ */
23
+
24
+ /**
25
+ * Evidence kind record
26
+ * @typedef {Object} EvidenceKindRecord
27
+ * @property {string} id - Controller id
28
+ * @property {string} key - Catalog key
29
+ * @property {string} domainKey - Enterprise domain key
30
+ * @property {string} displayName - Display name
31
+ * @property {string} purpose - Purpose
32
+ * @property {string} [description] - Description
33
+ * @property {string} [expectedUse] - Expected use
34
+ * @property {string} [defaultSeverity] - Default severity
35
+ * @property {boolean} requiresApproval - Approval flag
36
+ * @property {boolean} isActive - Active flag
37
+ * @property {number} sortOrder - Sort order
38
+ * @property {string} [source] - Source
39
+ * @property {Object} [metadata] - Metadata
40
+ */
41
+
42
+ module.exports = {};