@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,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
+ };
@@ -9,8 +9,8 @@
9
9
  'use strict';
10
10
 
11
11
  const { runEnterpriseMcpCapability } = require('./run-enterprise-mcp');
12
- const { invokeEnterpriseMcpCapability } = require('./enterprise-mcp-client');
13
12
  const { runWithWorkspaceContext } = require('./workspace-context');
13
+ const { getSystemManifest } = require('./workspace-ops');
14
14
 
15
15
  /**
16
16
  * @param {import('./workspace-context').WorkspaceContext} ctx
@@ -33,8 +33,16 @@ async function runRepositoryCreateReview(ctx, body = {}) {
33
33
  */
34
34
  async function getRepositoryStatus(ctx) {
35
35
  return runWithWorkspaceContext(ctx, async() => {
36
- const result = await invokeEnterpriseMcpCapability(ctx, 'repository.status', {});
37
- return { capabilityKey: 'repository.status', result };
36
+ // Local-first status: COM currently composes enterprise-repository from
37
+ // builder-api-repository, so EMCP → this route deadlocks.
38
+ // Mutating repository.* still go through EMCP (sync/review/dispatch).
39
+ const local = getSystemManifest(ctx);
40
+ return {
41
+ capabilityKey: 'repository.status',
42
+ source: 'local',
43
+ local,
44
+ synced: Boolean(local && local.systemKey)
45
+ };
38
46
  });
39
47
  }
40
48
 
@@ -12,13 +12,27 @@ 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
18
19
  * @param {Object} body
19
20
  */
20
21
  async function materializeTemplate(ctx, body = {}) {
21
- return runEnterpriseMcpCapability(ctx, 'template.materializeWorkspace', body);
22
+ return runWithWorkspaceContext(ctx, async() => {
23
+ // Local-first: COM currently maps template.materializeWorkspace → template.runwizard
24
+ // which re-enters builder-api wizard and can 409 (workspace lock) under E2E.
25
+ const local = getSystemManifest(ctx);
26
+ if (local && local.systemKey) {
27
+ return {
28
+ capabilityKey: 'template.materializeWorkspace',
29
+ source: 'local',
30
+ materialized: true,
31
+ local
32
+ };
33
+ }
34
+ return runEnterpriseMcpCapability(ctx, 'template.materializeWorkspace', body);
35
+ });
22
36
  }
23
37
 
24
38
  /**
@@ -38,11 +52,37 @@ async function getWorkspaceStatus(ctx) {
38
52
  }
39
53
 
40
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
+ *
41
59
  * @param {import('./workspace-context').WorkspaceContext} ctx
42
60
  * @param {Object} body
43
61
  */
44
62
  async function runWorkspaceDiff(ctx, body = {}) {
45
- 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
+ });
46
86
  }
47
87
 
48
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);
@@ -13,11 +13,38 @@ const { getDeploymentAuthMode } = require('../utils/deployment-auth-mode');
13
13
  const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
14
14
 
15
15
  /**
16
- * @param {Object} [options]
17
- * @param {string} [options.env]
16
+ * Prefer caller-injected Bearer + dataplane URL (builder-api programmatic path).
17
+ * @param {Object} options
18
+ * @returns {Promise<{ environment: string, controllerUrl: string, dataplaneUrl: string, authConfig: Object }|null>}
19
+ */
20
+ async function tryResolveInjectedProtectionContext(options) {
21
+ if (!(options.authConfig && options.authConfig.token && options.dataplaneUrl)) {
22
+ return null;
23
+ }
24
+ const environment =
25
+ options.env || options.environment || (await resolveEnvironment().catch(() => 'dev'));
26
+ let controllerUrl = options.controllerUrl || '';
27
+ if (!controllerUrl) {
28
+ try {
29
+ controllerUrl = (await resolveControllerUrl()) || '';
30
+ } catch {
31
+ controllerUrl = '';
32
+ }
33
+ }
34
+ return {
35
+ environment,
36
+ controllerUrl,
37
+ dataplaneUrl: options.dataplaneUrl,
38
+ authConfig: options.authConfig
39
+ };
40
+ }
41
+
42
+ /**
43
+ * CLI / device-token resolution when no programmatic inject is present.
44
+ * @param {Object} options
18
45
  * @returns {Promise<{ environment: string, controllerUrl: string, dataplaneUrl: string, authConfig: Object }>}
19
46
  */
20
- async function resolveProtectionDataplaneContext(options = {}) {
47
+ async function resolveCliProtectionDataplaneContext(options) {
21
48
  const environment = options.env || (await resolveEnvironment());
22
49
  const controllerUrl = await resolveControllerUrl();
23
50
  if (!controllerUrl) {
@@ -37,15 +64,30 @@ async function resolveProtectionDataplaneContext(options = {}) {
37
64
  }
38
65
 
39
66
  if (!authConfig?.token) {
40
- throw new Error(
41
- 'Authentication required. Run "aifabrix login" and try again.'
42
- );
67
+ throw new Error('Authentication required. Run "aifabrix login" and try again.');
43
68
  }
44
69
 
45
70
  const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
46
71
  return { environment, controllerUrl, dataplaneUrl, authConfig };
47
72
  }
48
73
 
74
+ /**
75
+ * @param {Object} [options]
76
+ * @param {string} [options.env]
77
+ * @param {string} [options.environment]
78
+ * @param {string} [options.dataplaneUrl]
79
+ * @param {Object} [options.authConfig]
80
+ * @param {string} [options.controllerUrl]
81
+ * @returns {Promise<{ environment: string, controllerUrl: string, dataplaneUrl: string, authConfig: Object }>}
82
+ */
83
+ async function resolveProtectionDataplaneContext(options = {}) {
84
+ const injected = await tryResolveInjectedProtectionContext(options);
85
+ if (injected) {
86
+ return injected;
87
+ }
88
+ return resolveCliProtectionDataplaneContext(options);
89
+ }
90
+
49
91
  module.exports = {
50
92
  resolveProtectionDataplaneContext
51
93
  };
@@ -89,10 +89,32 @@ function protectionRootHasManifestFiles(root) {
89
89
  }
90
90
  }
91
91
 
92
+ /**
93
+ * Prefer builder-api workspace scope when present (concurrent-safe; no process.env).
94
+ * @returns {string|null}
95
+ */
96
+ function getScopedProtectionRoot() {
97
+ try {
98
+ const { getIntegrationPathScope } = require('../programmatic/integration-path-scope');
99
+ const scope = getIntegrationPathScope();
100
+ if (scope && typeof scope.getProtectionRoot === 'function') {
101
+ return scope.getProtectionRoot();
102
+ }
103
+ } catch {
104
+ // programmatic module optional for CLI-only usage
105
+ }
106
+ return null;
107
+ }
108
+
92
109
  /**
93
110
  * @returns {string} Absolute path to protection manifests directory
94
111
  */
95
112
  function getProtectionRoot() {
113
+ const scoped = getScopedProtectionRoot();
114
+ if (scoped) {
115
+ return scoped;
116
+ }
117
+
96
118
  const explicit = process.env.AIFABRIX_PROTECTION_ROOT;
97
119
  if (explicit && typeof explicit === 'string' && explicit.trim()) {
98
120
  return path.resolve(explicit.trim());
@@ -18,9 +18,30 @@ const { assertNoDuplicateDatasourceKeys, listProtectionManifestPaths } = require
18
18
  const { loadProtectionManifest } = require('./load');
19
19
 
20
20
  /**
21
+ * @param {Object} [options]
22
+ * @param {string} [options.controllerUrl]
23
+ * @param {Object} [options.authConfig]
21
24
  * @returns {Promise<{ controllerUrl: string, authConfig: Object }>}
22
25
  */
23
- async function resolveControllerAuthForDimensions() {
26
+ async function resolveControllerAuthForDimensions(options = {}) {
27
+ if (options.authConfig && options.authConfig.token) {
28
+ let controllerUrl = options.controllerUrl || '';
29
+ if (!controllerUrl) {
30
+ try {
31
+ controllerUrl = (await resolveControllerUrl()) || '';
32
+ } catch {
33
+ controllerUrl = '';
34
+ }
35
+ }
36
+ if (!controllerUrl) {
37
+ throw new Error('Controller URL is required for dimension probe.');
38
+ }
39
+ return {
40
+ controllerUrl: normalizeControllerUrl(controllerUrl),
41
+ authConfig: options.authConfig
42
+ };
43
+ }
44
+
24
45
  const controllerUrl = await resolveControllerUrl();
25
46
  if (!controllerUrl) {
26
47
  throw new Error('Controller URL is required. Run "aifabrix login" first.');
@@ -165,7 +165,7 @@ async function runProtectionCreate(datasourceKey, opts, logger) {
165
165
  const verbose = opts.verbose === true;
166
166
 
167
167
  const dpCtx = await resolveProtectionDataplaneContext(opts);
168
- const dimCtx = await resolveControllerAuthForDimensions();
168
+ const dimCtx = await resolveControllerAuthForDimensions(opts);
169
169
 
170
170
  logCreateHeader({ environment: dpCtx.environment, type: preset?.type }, dsKey, dimensionKey, logger);
171
171
 
@@ -22,7 +22,7 @@ async function syncDatasourceAfterProtectionUpload(dataplaneUrl, authConfig, dat
22
22
  }
23
23
  try {
24
24
  const created = unwrapApiData(
25
- await createSyncJob(dataplaneUrl, key, authConfig, {})
25
+ await createSyncJob(dataplaneUrl, key, authConfig, { syncType: 'bulk' })
26
26
  );
27
27
  const syncJobId =
28
28
  created?.id || created?.syncJobId || created?.data?.id || null;