@aifabrix/builder 2.52.1 → 2.53.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 (225) hide show
  1. package/lib/app/deploy-config.js +3 -2
  2. package/lib/channels/add-channel.js +11 -41
  3. package/lib/channels/approval-guides/index.js +3 -0
  4. package/lib/channels/approval-guides/microsoft-copilot.js +26 -17
  5. package/lib/channels/channel-add-existing.js +72 -0
  6. package/lib/channels/channel-runtime-resolver.js +5 -1
  7. package/lib/channels/channel-status-display.js +30 -3
  8. package/lib/channels/microsoft-copilot-entra-sso-form.js +73 -2
  9. package/lib/channels/microsoft-copilot-entra-sso.js +26 -5
  10. package/lib/channels/microsoft-copilot-limits.js +22 -3
  11. package/lib/channels/microsoft-copilot-package.js +59 -14
  12. package/lib/channels/microsoft-copilot-validate.js +40 -10
  13. package/lib/cli/attach-default-help-examples.js +102 -0
  14. package/lib/cli/index.js +2 -0
  15. package/lib/cli/setup-credential-deployment.js +7 -5
  16. package/lib/cli/setup-utility-validate.js +1 -0
  17. package/lib/commands/credential-push.js +37 -5
  18. package/lib/commands/package-score.js +72 -0
  19. package/lib/commands/role-assistant.js +46 -8
  20. package/lib/deployment/deployer-poll-run.js +2 -2
  21. package/lib/deployment/deployer.js +1 -1
  22. package/lib/deployment/environment.js +2 -2
  23. package/lib/external-system/deploy.js +11 -21
  24. package/lib/integration-definition/apply.js +12 -14
  25. package/lib/integration-definition/enrich-openapi-operations.js +9 -10
  26. package/lib/lab/package-parity-score.js +417 -0
  27. package/lib/programmatic/capability-registry.js +1 -0
  28. package/lib/programmatic/help-content/channel-onboarding.md +35 -0
  29. package/lib/programmatic/help-content/cip-overview.md +27 -0
  30. package/lib/programmatic/help-content/connected-system-ui.md +70 -0
  31. package/lib/programmatic/help-content/dimensions-guide.md +23 -0
  32. package/lib/programmatic/help-content/enterprise-sync-fabrix.md +123 -0
  33. package/lib/programmatic/help-content/overview.md +25 -0
  34. package/lib/programmatic/help-content/subscription-guide.md +27 -0
  35. package/lib/programmatic/help-content/workflow.md +50 -0
  36. package/lib/programmatic/openapi-component-schemas.js +1 -0
  37. package/lib/programmatic/openapi-descriptions.js +6 -0
  38. package/lib/programmatic/openapi-envelope-examples.js +11 -0
  39. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  40. package/lib/programmatic/openapi-request-schemas.js +39 -13
  41. package/lib/programmatic/openapi-result-schemas.js +30 -0
  42. package/lib/programmatic/openapi-schema-descriptions-core.js +18 -0
  43. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  44. package/lib/programmatic/persist-supplied-openapi.js +180 -0
  45. package/lib/programmatic/route-handler-map.js +6 -0
  46. package/lib/programmatic/run-integration-definition.js +13 -5
  47. package/lib/role-assistant/evidence-sync.js +55 -7
  48. package/lib/role-assistant/test-runner-attachments.js +118 -0
  49. package/lib/role-assistant/test-runner-bid-apply.js +100 -21
  50. package/lib/role-assistant/test-runner-bid-document.js +1 -1
  51. package/lib/role-assistant/test-runner-start-payload.js +96 -0
  52. package/lib/role-assistant/test-runner-workhub-answers.js +11 -7
  53. package/lib/role-assistant/test-runner-workhub-helpers.js +100 -0
  54. package/lib/role-assistant/test-runner-workhub-soft.js +86 -0
  55. package/lib/role-assistant/test-runner-workhub.js +68 -105
  56. package/lib/role-assistant/test-runner.js +5 -63
  57. package/lib/role-assistant/test-suite.js +33 -40
  58. package/lib/schema/external-datasource.schema.json +13 -4
  59. package/lib/utils/compose-generator.js +10 -1
  60. package/lib/utils/compose-traefik-extra-routes.js +107 -0
  61. package/lib/utils/docker-build.js +2 -2
  62. package/lib/utils/help-builder.js +2 -1
  63. package/lib/utils/promote-dev-docker-image.js +15 -10
  64. package/package.json +14 -16
  65. package/templates/README.md +12 -1
  66. package/templates/applications/dataplane/application.yaml +8 -1
  67. package/templates/applications/dataplane/env.template +1 -1
  68. package/templates/applications/miso-controller/application.yaml +2 -1
  69. package/templates/applications/miso-controller/rbac.yaml +1 -0
  70. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +6 -2
  71. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +8 -5
  72. package/templates/channels/microsoft-copilot/fi-fi.template.json.hbs +7 -0
  73. package/templates/channels/microsoft-copilot/install.template.md.hbs +74 -22
  74. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +29 -14
  75. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +6 -2
  76. package/templates/python/docker-compose.hbs +36 -0
  77. package/templates/typescript/docker-compose.hbs +36 -0
  78. package/.cursor/plans/Archive/157.1-security-quality-violations-by-type.csv +0 -374
  79. package/.cursor/process/README.md +0 -9
  80. package/.cursor/rules/anchor-docs.mdc +0 -15
  81. package/.cursor/rules/assistant-driven-integration-build.mdc +0 -40
  82. package/.cursor/rules/cli-layout.mdc +0 -81
  83. package/.cursor/rules/cli-product-neutral.mdc +0 -43
  84. package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +0 -49
  85. package/.cursor/rules/docs-rules.mdc +0 -32
  86. package/.cursor/rules/plan-rules/application-lifecycle.mdc +0 -30
  87. package/.cursor/rules/plan-rules/plan-frontmatter.mdc +0 -35
  88. package/.cursor/rules/plan-rules/plan-lifecycle.mdc +0 -31
  89. package/.cursor/rules/plan-rules/plan-validation-gate.mdc +0 -34
  90. package/.cursor/rules/project-rules.mdc +0 -913
  91. package/.cursor/skills/README.md +0 -15
  92. package/.dockerignore +0 -52
  93. package/.eslintignore +0 -22
  94. package/.markdownlint.json +0 -14
  95. package/anchor-docs/README.md +0 -10
  96. package/anchor-docs/_TEMPLATE +0 -24
  97. package/babel.config.js +0 -6
  98. package/bin/builder-api-cleanup.js +0 -26
  99. package/bin/builder-api-nest.js +0 -10
  100. package/bin/builder-api-spike.js +0 -19
  101. package/enterprise-knowledge/README.md +0 -50
  102. package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +0 -22
  103. package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +0 -15
  104. package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +0 -69
  105. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +0 -52
  106. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +0 -29
  107. package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +0 -2
  108. package/integration/README.md +0 -81
  109. package/integration/github/README.md +0 -53
  110. package/integration/github/application.json +0 -21
  111. package/integration/github/deploy.js +0 -72
  112. package/integration/github/env.template +0 -11
  113. package/integration/github/github-datasource-issue-comments.json +0 -239
  114. package/integration/github/github-datasource-issues.json +0 -399
  115. package/integration/github/github-datasource-repository.json +0 -354
  116. package/integration/github/github-deploy.json +0 -1202
  117. package/integration/github/github-system.json +0 -82
  118. package/integration/hubspot-test/README.md +0 -159
  119. package/integration/hubspot-test/application.json +0 -54
  120. package/integration/hubspot-test/companies.json +0 -2048
  121. package/integration/hubspot-test/create-hubspot.js +0 -523
  122. package/integration/hubspot-test/env.template +0 -4
  123. package/integration/hubspot-test/hubspot-test-datasource-company.json +0 -138
  124. package/integration/hubspot-test/hubspot-test-datasource-contact.json +0 -146
  125. package/integration/hubspot-test/hubspot-test-datasource-deal.json +0 -146
  126. package/integration/hubspot-test/hubspot-test-datasource-users.json +0 -76
  127. package/integration/hubspot-test/hubspot-test-deploy.json +0 -2160
  128. package/integration/hubspot-test/hubspot-test-system.json +0 -74
  129. package/integration/hubspot-test/rbac.json +0 -166
  130. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +0 -20
  131. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +0 -9
  132. package/integration/hubspot-test/test-artifacts/wizard-invalid-add-datasource.yaml +0 -5
  133. package/integration/hubspot-test/test-artifacts/wizard-invalid-app-name.yaml +0 -5
  134. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-create.yaml +0 -7
  135. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-select.yaml +0 -7
  136. package/integration/hubspot-test/test-artifacts/wizard-invalid-known-platform.yaml +0 -4
  137. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-app.yaml +0 -4
  138. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  139. package/integration/hubspot-test/test-artifacts/wizard-invalid-mode.yaml +0 -5
  140. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-file.yaml +0 -5
  141. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-url.yaml +0 -4
  142. package/integration/hubspot-test/test-artifacts/wizard-invalid-source.yaml +0 -4
  143. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-array-test.yaml +0 -5
  144. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  145. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  146. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-test.yaml +0 -5
  147. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-test.yaml +0 -5
  148. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +0 -5
  149. package/integration/hubspot-test/test-dataplane-down-helpers.js +0 -249
  150. package/integration/hubspot-test/test-dataplane-down-tests.js +0 -386
  151. package/integration/hubspot-test/test-dataplane-down.js +0 -209
  152. package/integration/hubspot-test/test.js +0 -1588
  153. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +0 -16
  154. package/integration/hubspot-test/wizard-hubspot-platform.yaml +0 -8
  155. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +0 -23
  156. package/integration/roundtrip-test-local/README.md +0 -143
  157. package/integration/roundtrip-test-local/application.yaml +0 -13
  158. package/integration/roundtrip-test-local/env.template +0 -15
  159. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +0 -14
  160. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +0 -61
  161. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +0 -25
  162. package/integration/roundtrip-test-local2/README.md +0 -143
  163. package/integration/roundtrip-test-local2/application.yaml +0 -13
  164. package/integration/roundtrip-test-local2/env.template +0 -15
  165. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +0 -31
  166. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +0 -86
  167. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +0 -25
  168. package/integration/test/wizard.yaml +0 -8
  169. package/jest.config.coverage.js +0 -37
  170. package/jest.config.default.js +0 -11
  171. package/jest.config.integration.fixtures.js +0 -22
  172. package/jest.config.integration.js +0 -33
  173. package/jest.config.isolated.js +0 -11
  174. package/jest.config.manual.js +0 -30
  175. package/jest.global-live-fabrix-hooks.js +0 -36
  176. package/jest.isolated-projects.js +0 -283
  177. package/jest.projects.js +0 -79
  178. package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +0 -205
  179. package/packages/builder-api/README.md +0 -120
  180. package/packages/builder-api/package.json +0 -30
  181. package/packages/builder-api/src/app.module.ts +0 -7
  182. package/packages/builder-api/src/auth/auth.module.ts +0 -5
  183. package/packages/builder-api/src/external-system/external-system.module.ts +0 -5
  184. package/packages/builder-api/src/main.ts +0 -16
  185. package/packages/builder-api/tsconfig.json +0 -15
  186. package/scripts/check-builder-api-architecture-grep.js +0 -79
  187. package/scripts/check-datasource-test-run-schema-sync.js +0 -34
  188. package/scripts/ci-fix.sh +0 -19
  189. package/scripts/ci-simulate.sh +0 -19
  190. package/scripts/diagnose-cli.js +0 -150
  191. package/scripts/docker_image_content_gate.js +0 -47
  192. package/scripts/docker_image_content_gate.sh +0 -5
  193. package/scripts/export-builder-api-evidence.js +0 -262
  194. package/scripts/install-local.js +0 -490
  195. package/scripts/lib/builder-api-docker-staging-copy.js +0 -60
  196. package/scripts/lib/builder-api-docker-staging-core.js +0 -183
  197. package/scripts/lib/docker-image-content-gate-core.js +0 -166
  198. package/scripts/lib/plan-actor.js +0 -109
  199. package/scripts/lib/plan-config.js +0 -20
  200. package/scripts/lib/plan-gates.js +0 -154
  201. package/scripts/lib/plan-meta.js +0 -104
  202. package/scripts/lib/plan-parse.js +0 -162
  203. package/scripts/lib/plan-prevalidate-gates.js +0 -337
  204. package/scripts/lib/plan-run-core.js +0 -125
  205. package/scripts/lib/plan-run-report.js +0 -119
  206. package/scripts/plan-approve.js +0 -24
  207. package/scripts/plan-archive.js +0 -49
  208. package/scripts/plan-run.js +0 -13
  209. package/scripts/plan-set-actor.js +0 -21
  210. package/scripts/plan-validate.js +0 -80
  211. package/scripts/pnpm/af.mjs +0 -125
  212. package/scripts/pnpm/check-quiet.mjs +0 -32
  213. package/scripts/pnpm/dev-install.mjs +0 -29
  214. package/scripts/pnpm/dev-reload.mjs +0 -18
  215. package/scripts/pnpm/help.mjs +0 -55
  216. package/scripts/pnpm/lib/dev-reload-core.mjs +0 -189
  217. package/scripts/pnpm/lib/dev-reload-log-capture.mjs +0 -67
  218. package/scripts/pnpm/lib/dev-reload-log-utils.mjs +0 -32
  219. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +0 -124
  220. package/scripts/pnpm/lib/resolve-app-url.mjs +0 -183
  221. package/scripts/pnpm/lib/spinner.mjs +0 -42
  222. package/scripts/pnpm-global-remove.js +0 -48
  223. package/scripts/stage_builder_api_for_docker.js +0 -65
  224. package/scripts/sync-builder-api-template.js +0 -44
  225. package/scripts/test-dataplane-bootstrap.js +0 -331
@@ -131,19 +131,6 @@ async function fetchDataplaneDeployReadiness(controllerUrl, environment, authCon
131
131
  }
132
132
  }
133
133
 
134
- /**
135
- * Deploys via controller and prints readiness summary (config / deployment / runtime layers).
136
- * @async
137
- * @param {Object} manifest - Controller manifest
138
- * @param {string} controllerUrl - Controller base URL
139
- * @param {string} environment - Environment key
140
- * @param {Object} authConfig - Authentication configuration
141
- * @param {Object} options - Deployment options (poll, probe, probeTimeout)
142
- * @returns {Promise<Object>} Deployment result
143
- */
144
- /**
145
- * @param {Object} deploymentOutcome - from parseControllerDeploymentOutcome
146
- */
147
134
  /**
148
135
  * Optional dataplane validation/run after external deploy (--probe).
149
136
  * @async
@@ -202,12 +189,21 @@ function logImmediateControllerDeploymentOutcome(deploymentOutcome) {
202
189
  logger.log(chalk.gray(' Check the controller deployment job or logs for details.'));
203
190
  }
204
191
 
192
+ /** External deploy waits on controller → dataplane publish job (may run prove gates / tests). */
193
+ const EXTERNAL_DEPLOY_POLL_INTERVAL_MS = 2000;
194
+ /** 300 × 2s ≈ 10 minutes — must cover controller publish-job max wait with gates. */
195
+ const EXTERNAL_DEPLOY_POLL_MAX_ATTEMPTS = 300;
196
+
205
197
  function buildExternalDeployPollOpts(options) {
206
198
  return {
207
199
  ...options,
208
200
  poll: true,
209
- pollInterval: options.pollInterval !== undefined ? options.pollInterval : 500,
210
- pollMaxAttempts: options.pollMaxAttempts !== undefined ? options.pollMaxAttempts : 22,
201
+ pollInterval:
202
+ options.pollInterval !== undefined ? options.pollInterval : EXTERNAL_DEPLOY_POLL_INTERVAL_MS,
203
+ pollMaxAttempts:
204
+ options.pollMaxAttempts !== undefined
205
+ ? options.pollMaxAttempts
206
+ : EXTERNAL_DEPLOY_POLL_MAX_ATTEMPTS,
211
207
  silentPoll: options.silentPoll === true
212
208
  };
213
209
  }
@@ -381,12 +377,6 @@ function wrapDeployExternalSystemError(error, appName) {
381
377
  return new Error(message);
382
378
  }
383
379
 
384
- /**
385
- * @async
386
- * @param {string} appName
387
- * @param {Object} options
388
- * @returns {Promise<Object>}
389
- */
390
380
  /**
391
381
  * @param {string} appName
392
382
  * @param {Object} options
@@ -5,9 +5,8 @@
5
5
 
6
6
  'use strict';
7
7
 
8
- const fs = require('../internal/fs-real-sync');
9
- const fsp = require('fs').promises;
10
8
  const path = require('path');
9
+ const fsReal = require('../internal/fs-real-sync');
11
10
  const { getIntegrationPath } = require('../utils/paths');
12
11
  const { validateIntegrationDefinition } = require('./validate');
13
12
 
@@ -20,7 +19,7 @@ const STAMP_FILE = '.aifabrix-integration-definition.json';
20
19
  async function readStamp(dir) {
21
20
  const stampPath = path.join(dir, STAMP_FILE);
22
21
  try {
23
- const raw = await fsp.readFile(stampPath, 'utf8');
22
+ const raw = fsReal.readFileSync(stampPath, 'utf8');
24
23
  return JSON.parse(raw);
25
24
  } catch (err) {
26
25
  if (err && err.code === 'ENOENT') {
@@ -339,22 +338,22 @@ function buildPackageArtifacts(bid, systemKey, enrichOptions = {}) {
339
338
  * @returns {Promise<void>}
340
339
  */
341
340
  async function writePackageArtifacts(outDir, artifacts) {
342
- await fsp.mkdir(outDir, { recursive: true });
343
- await fsp.writeFile(
341
+ fsReal.mkdirSync(outDir, { recursive: true });
342
+ fsReal.writeFileSync(
344
343
  path.join(outDir, 'application.json'),
345
344
  JSON.stringify(artifacts.application, null, 2),
346
345
  'utf8'
347
346
  );
348
- await fsp.writeFile(
347
+ fsReal.writeFileSync(
349
348
  path.join(outDir, artifacts.systemFile),
350
349
  JSON.stringify(artifacts.system, null, 2),
351
350
  'utf8'
352
351
  );
353
352
  for (const file of artifacts.dsFiles) {
354
- await fsp.writeFile(path.join(outDir, file.name), JSON.stringify(file.body, null, 2), 'utf8');
353
+ fsReal.writeFileSync(path.join(outDir, file.name), JSON.stringify(file.body, null, 2), 'utf8');
355
354
  }
356
- await fsp.writeFile(path.join(outDir, 'env.template'), artifacts.envTemplate, 'utf8');
357
- await fsp.writeFile(
355
+ fsReal.writeFileSync(path.join(outDir, 'env.template'), artifacts.envTemplate, 'utf8');
356
+ fsReal.writeFileSync(
358
357
  path.join(outDir, STAMP_FILE),
359
358
  JSON.stringify(artifacts.nextStamp, null, 2),
360
359
  'utf8'
@@ -390,7 +389,7 @@ function idempotentResult(stamp, bid, outDir, systemKey) {
390
389
  stamp &&
391
390
  stamp.definitionId === bid.definitionId &&
392
391
  stamp.definitionHash === bid.definitionHash &&
393
- fs.existsSync(path.join(outDir, 'application.json'))
392
+ fsReal.existsSync(path.join(outDir, 'application.json'))
394
393
  )
395
394
  ) {
396
395
  return null;
@@ -434,11 +433,10 @@ async function applyIntegrationDefinition(bidInput, options = {}) {
434
433
  return early;
435
434
  }
436
435
  assertApplyAllowed(bid, stamp);
437
- // Prefer explicit openapiBaseDir; else ship-local enrichment specs (not caller cwd).
438
- // Suite/builder-api apply runs with workspace cwd and must still resolve openapiSourceRef.
439
- const defaultOpenapiBaseDir = path.join(__dirname, 'openapi-enrichment');
436
+ // Prefer explicit openapiBaseDir (workspace integration/<systemKey>/openapi
437
+ // from 517.2 persist). Do not default product apply to ship-local enrichment.
440
438
  const artifacts = buildPackageArtifacts(bid, systemKey, {
441
- baseDir: options.openapiBaseDir || options.baseDir || defaultOpenapiBaseDir
439
+ baseDir: options.openapiBaseDir || options.baseDir
442
440
  });
443
441
  const fileList = [
444
442
  'application.json',
@@ -5,8 +5,8 @@
5
5
 
6
6
  'use strict';
7
7
 
8
- const fs = require('../internal/fs-real-sync');
9
8
  const path = require('path');
9
+ const fsReal = require('../internal/fs-real-sync');
10
10
  const {
11
11
  maybeEnrichCrmStyleFieldMappings
12
12
  } = require('./enrich-crm-field-mappings');
@@ -26,22 +26,21 @@ function resolveOpenApiRefPath(ref, baseDir) {
26
26
  if (path.isAbsolute(trimmed)) {
27
27
  return trimmed;
28
28
  }
29
- const moduleEnrichmentDir = path.join(__dirname, 'openapi-enrichment');
30
- // Prefer caller baseDir, then bundled openapi-enrichment, then cwd.
31
- // Cwd last avoids false misses when Jest mocks fs.existsSync but readFileSync
32
- // remains real (fallback would otherwise point at <cwd>/<ref>).
29
+ // Product resolve root is workspace openapi/ (517.2) via explicit baseDir.
30
+ // No ship-local vendor enrichment packs under lib/.
33
31
  const candidates = [
34
32
  baseDir ? path.resolve(baseDir, trimmed) : null,
35
- path.join(moduleEnrichmentDir, trimmed),
36
- path.join(moduleEnrichmentDir, path.basename(trimmed)),
33
+ baseDir ? path.resolve(baseDir, path.basename(trimmed)) : null,
37
34
  path.resolve(process.cwd(), trimmed)
38
35
  ].filter(Boolean);
39
36
  for (const candidate of candidates) {
40
- if (fs.existsSync(candidate)) {
37
+ if (fsReal.existsSync(candidate)) {
41
38
  return candidate;
42
39
  }
43
40
  }
44
- return candidates[0];
41
+ throw new Error(
42
+ `OpenAPI enrichment document not found for ref "${trimmed}". Tried: ${candidates.join(', ')}`
43
+ );
45
44
  }
46
45
 
47
46
  /**
@@ -51,7 +50,7 @@ function resolveOpenApiRefPath(ref, baseDir) {
51
50
  */
52
51
  function loadOpenApiDocument(ref, baseDir) {
53
52
  const abs = resolveOpenApiRefPath(ref, baseDir);
54
- const raw = fs.readFileSync(abs, 'utf8');
53
+ const raw = fsReal.readFileSync(abs, 'utf8');
55
54
  const doc = JSON.parse(raw);
56
55
  if (!doc || typeof doc !== 'object' || !doc.paths || typeof doc.paths !== 'object') {
57
56
  throw new Error(`OpenAPI document missing paths: ${abs}`);
@@ -0,0 +1,417 @@
1
+ /**
2
+ * Package-level parity score between a reference integration dir and a candidate.
3
+ * Lab/DoD aid only — reference packages are score baselines, never generation input.
4
+ *
5
+ * @fileoverview Structured parity facets for two integration package directories
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ const fs = require('../internal/fs-real-sync');
11
+ const path = require('path');
12
+ const { loadConfigFile } = require('../utils/config-format');
13
+
14
+ const DATASOURCE_FILE_RE = /datasource/i;
15
+
16
+ /**
17
+ * @param {string} dir
18
+ * @returns {string[]}
19
+ */
20
+ function listJsonFiles(dir) {
21
+ if (!dir || !fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
22
+ return [];
23
+ }
24
+ return fs
25
+ .readdirSync(dir)
26
+ .filter((name) => name.endsWith('.json') || name.endsWith('.yaml') || name.endsWith('.yml'))
27
+ .map((name) => path.join(dir, name))
28
+ .sort();
29
+ }
30
+
31
+ /**
32
+ * @param {string} dir
33
+ * @returns {Object[]}
34
+ */
35
+ function loadDatasourceManifests(dir) {
36
+ const out = [];
37
+ for (const filePath of listJsonFiles(dir)) {
38
+ const base = path.basename(filePath);
39
+ if (!DATASOURCE_FILE_RE.test(base)) {
40
+ continue;
41
+ }
42
+ try {
43
+ const parsed = loadConfigFile(filePath);
44
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
45
+ out.push({ filePath, base, manifest: parsed });
46
+ }
47
+ } catch (_err) {
48
+ // Skip unreadable files; facet will report incomplete package
49
+ }
50
+ }
51
+ return out;
52
+ }
53
+
54
+ /**
55
+ * @param {Object} manifest
56
+ * @returns {string}
57
+ */
58
+ function datasourceKey(manifest) {
59
+ return String(manifest.key || '').trim();
60
+ }
61
+
62
+ /**
63
+ * @param {Object} manifest
64
+ * @returns {string[]}
65
+ */
66
+ function capabilityKeys(manifest) {
67
+ const caps = manifest.capabilities;
68
+ if (!Array.isArray(caps)) {
69
+ return [];
70
+ }
71
+ return caps
72
+ .map((row) => (row && typeof row === 'object' ? String(row.key || '').trim() : ''))
73
+ .filter(Boolean)
74
+ .sort();
75
+ }
76
+
77
+ /**
78
+ * @param {string} dir
79
+ * @returns {string[]}
80
+ */
81
+ function openapiBasenames(dir) {
82
+ const openapiDir = path.join(dir, 'openapi');
83
+ if (!fs.existsSync(openapiDir) || !fs.statSync(openapiDir).isDirectory()) {
84
+ return [];
85
+ }
86
+ return fs
87
+ .readdirSync(openapiDir)
88
+ .filter((name) => /\.(json|yaml|yml)$/i.test(name))
89
+ .map((name) => name.toLowerCase())
90
+ .sort();
91
+ }
92
+
93
+ /**
94
+ * @param {Object} manifest
95
+ * @returns {{ metadataSchema: boolean, fieldMappings: boolean, foreignKeys: boolean, exposed: boolean, cip: boolean }}
96
+ */
97
+ function contractSurfaceFlags(manifest) {
98
+ const execution = manifest.execution && typeof manifest.execution === 'object' ? manifest.execution : {};
99
+ const cip = execution.cip && typeof execution.cip === 'object' ? execution.cip : null;
100
+ return {
101
+ metadataSchema: Boolean(manifest.metadataSchema),
102
+ fieldMappings: Boolean(manifest.fieldMappings),
103
+ foreignKeys: Boolean(manifest.foreignKeys),
104
+ exposed: Boolean(manifest.exposed),
105
+ cip: Boolean(cip && cip.operations)
106
+ };
107
+ }
108
+
109
+ /**
110
+ * @param {boolean} ok
111
+ * @param {string} id
112
+ * @param {string} detail
113
+ * @param {Object} [extra]
114
+ * @returns {Object}
115
+ */
116
+ function facet(ok, id, detail, extra = {}) {
117
+ return { id, ok: Boolean(ok), detail, ...extra };
118
+ }
119
+
120
+ /**
121
+ * @param {string} absDir
122
+ * @returns {boolean}
123
+ */
124
+ function isPackageDir(absDir) {
125
+ return fs.existsSync(absDir) && fs.statSync(absDir).isDirectory();
126
+ }
127
+
128
+ /**
129
+ * @param {Object[]} rows
130
+ * @returns {Map<string, Object>}
131
+ */
132
+ function indexDatasourcesByKey(rows) {
133
+ const byKey = new Map();
134
+ for (const row of rows) {
135
+ const key = datasourceKey(row.manifest);
136
+ if (key) {
137
+ byKey.set(key, row);
138
+ }
139
+ }
140
+ return byKey;
141
+ }
142
+
143
+ /**
144
+ * @param {string} refAbs
145
+ * @param {string} candAbs
146
+ * @param {Object[]} facets
147
+ */
148
+ function pushPresenceFacets(refAbs, candAbs, facets) {
149
+ const refExists = isPackageDir(refAbs);
150
+ const candExists = isPackageDir(candAbs);
151
+ facets.push(
152
+ facet(refExists, 'referencePackagePresent', refExists ? 'Reference package directory found' : 'Reference package directory missing')
153
+ );
154
+ facets.push(
155
+ facet(candExists, 'candidatePackagePresent', candExists ? 'Candidate package directory found' : 'Candidate package directory missing')
156
+ );
157
+ return { refExists, candExists };
158
+ }
159
+
160
+ /**
161
+ * @param {string} refAbs
162
+ * @param {string} candAbs
163
+ * @param {Object[]} facets
164
+ */
165
+ function pushSystemManifestFacet(refAbs, candAbs, facets) {
166
+ const refHasSystem = listJsonFiles(refAbs).some((f) => /system/i.test(path.basename(f)));
167
+ const candHasSystem = listJsonFiles(candAbs).some((f) => /system/i.test(path.basename(f)));
168
+ facets.push(
169
+ facet(
170
+ !refHasSystem || candHasSystem,
171
+ 'systemManifest',
172
+ candHasSystem ? 'Candidate has a system manifest file' : 'Candidate missing system manifest while reference has one'
173
+ )
174
+ );
175
+ }
176
+
177
+ /**
178
+ * @param {Map<string, Object>} refByKey
179
+ * @param {Map<string, Object>} candByKey
180
+ * @param {Object[]} facets
181
+ * @returns {string[]}
182
+ */
183
+ function pushDatasourceCoverageFacet(refByKey, candByKey, facets) {
184
+ const missingDatasourceKeys = [...refByKey.keys()].filter((key) => !candByKey.has(key)).sort();
185
+ facets.push(
186
+ facet(
187
+ missingDatasourceKeys.length === 0 && refByKey.size > 0,
188
+ 'datasourceKeyCoverage',
189
+ missingDatasourceKeys.length === 0
190
+ ? `Candidate covers ${refByKey.size} reference datasource key(s)`
191
+ : `Missing datasource keys: ${missingDatasourceKeys.join(', ')}`,
192
+ { missingDatasourceKeys, referenceCount: refByKey.size, candidateCount: candByKey.size }
193
+ )
194
+ );
195
+ return missingDatasourceKeys;
196
+ }
197
+
198
+ /**
199
+ * @param {string} refAbs
200
+ * @param {string} candAbs
201
+ * @param {Object[]} facets
202
+ */
203
+ function pushOpenapiFacet(refAbs, candAbs, facets) {
204
+ const refOpenapi = openapiBasenames(refAbs);
205
+ const candOpenapi = new Set(openapiBasenames(candAbs));
206
+ const missingOpenapi = refOpenapi.filter((name) => !candOpenapi.has(name));
207
+ facets.push(
208
+ facet(
209
+ refOpenapi.length === 0 || missingOpenapi.length === 0,
210
+ 'openapiDir',
211
+ refOpenapi.length === 0
212
+ ? 'Reference has no openapi/ files — facet skipped'
213
+ : missingOpenapi.length === 0
214
+ ? `Candidate has ${refOpenapi.length} matching openapi basename(s)`
215
+ : `Missing openapi files: ${missingOpenapi.join(', ')}`,
216
+ { missingOpenapi }
217
+ )
218
+ );
219
+ }
220
+
221
+ /**
222
+ * @param {string} key
223
+ * @param {Object} refManifest
224
+ * @param {Object} candManifest
225
+ * @param {Object[]} capabilityGaps
226
+ * @returns {boolean}
227
+ */
228
+ function collectCapabilityGaps(key, refManifest, candManifest, capabilityGaps) {
229
+ const refCaps = new Set(capabilityKeys(refManifest));
230
+ const candCaps = new Set(capabilityKeys(candManifest));
231
+ const missingCaps = [...refCaps].filter((c) => !candCaps.has(c)).sort();
232
+ if (!missingCaps.length) {
233
+ return true;
234
+ }
235
+ capabilityGaps.push({ datasourceKey: key, missingCapabilityKeys: missingCaps });
236
+ return false;
237
+ }
238
+
239
+ /**
240
+ * @param {string} key
241
+ * @param {Object} refManifest
242
+ * @param {Object} candManifest
243
+ * @param {Object[]} resourceTypeGaps
244
+ * @returns {boolean}
245
+ */
246
+ function collectResourceTypeGaps(key, refManifest, candManifest, resourceTypeGaps) {
247
+ const refRt = String(refManifest.resourceType || '').trim();
248
+ const candRt = String(candManifest.resourceType || '').trim();
249
+ if (!(refRt && candRt && refRt !== candRt)) {
250
+ return true;
251
+ }
252
+ resourceTypeGaps.push({ datasourceKey: key, reference: refRt, candidate: candRt });
253
+ return false;
254
+ }
255
+
256
+ /**
257
+ * @param {string} key
258
+ * @param {Object} refManifest
259
+ * @param {Object} candManifest
260
+ * @param {Object[]} surfaceGaps
261
+ * @returns {boolean}
262
+ */
263
+ function collectSurfaceGaps(key, refManifest, candManifest, surfaceGaps) {
264
+ const refFlags = contractSurfaceFlags(refManifest);
265
+ const candFlags = contractSurfaceFlags(candManifest);
266
+ let ok = true;
267
+ for (const [surface, needed] of Object.entries(refFlags)) {
268
+ if (needed && !candFlags[surface]) {
269
+ ok = false;
270
+ surfaceGaps.push({ datasourceKey: key, surface });
271
+ }
272
+ }
273
+ return ok;
274
+ }
275
+
276
+ /**
277
+ * @param {Map<string, Object>} refByKey
278
+ * @param {Map<string, Object>} candByKey
279
+ * @returns {{ capabilityOk: boolean, capabilityGaps: Object[], resourceTypeOk: boolean, resourceTypeGaps: Object[], surfacesOk: boolean, surfaceGaps: Object[] }}
280
+ */
281
+ function compareSharedDatasources(refByKey, candByKey) {
282
+ const capabilityGaps = [];
283
+ const resourceTypeGaps = [];
284
+ const surfaceGaps = [];
285
+ let capabilityOk = true;
286
+ let resourceTypeOk = true;
287
+ let surfacesOk = true;
288
+
289
+ for (const [key, refRow] of refByKey.entries()) {
290
+ const candRow = candByKey.get(key);
291
+ if (!candRow) {
292
+ continue;
293
+ }
294
+ if (!collectCapabilityGaps(key, refRow.manifest, candRow.manifest, capabilityGaps)) {
295
+ capabilityOk = false;
296
+ }
297
+ if (!collectResourceTypeGaps(key, refRow.manifest, candRow.manifest, resourceTypeGaps)) {
298
+ resourceTypeOk = false;
299
+ }
300
+ if (!collectSurfaceGaps(key, refRow.manifest, candRow.manifest, surfaceGaps)) {
301
+ surfacesOk = false;
302
+ }
303
+ }
304
+
305
+ return { capabilityOk, capabilityGaps, resourceTypeOk, resourceTypeGaps, surfacesOk, surfaceGaps };
306
+ }
307
+
308
+ /**
309
+ * @param {ReturnType<typeof compareSharedDatasources>} compared
310
+ * @param {Object[]} facets
311
+ */
312
+ function pushSharedDatasourceFacets(compared, facets) {
313
+ facets.push(
314
+ facet(
315
+ compared.capabilityOk,
316
+ 'capabilityKeys',
317
+ compared.capabilityOk ? 'Capability keys cover reference on shared datasources' : 'Missing capability keys on shared datasources',
318
+ { gaps: compared.capabilityGaps }
319
+ )
320
+ );
321
+ facets.push(
322
+ facet(
323
+ compared.resourceTypeOk,
324
+ 'resourceTypes',
325
+ compared.resourceTypeOk ? 'resourceType matches on shared datasources' : 'resourceType mismatches on shared datasources',
326
+ { gaps: compared.resourceTypeGaps }
327
+ )
328
+ );
329
+ facets.push(
330
+ facet(
331
+ compared.surfacesOk,
332
+ 'contractSurfaces',
333
+ compared.surfacesOk ? 'Contract surfaces present where reference declares them' : 'Missing contract surfaces vs reference',
334
+ { gaps: compared.surfaceGaps }
335
+ )
336
+ );
337
+ }
338
+
339
+ /**
340
+ * @param {Object[]} facets
341
+ * @param {string[]} missingDatasourceKeys
342
+ * @param {string} refAbs
343
+ * @param {string} candAbs
344
+ * @returns {Object}
345
+ */
346
+ function buildParityReport(facets, missingDatasourceKeys, refAbs, candAbs) {
347
+ const scored = facets.filter((f) => f.id !== 'referencePackagePresent');
348
+ const passed = scored.filter((f) => f.ok).length;
349
+ const score = scored.length === 0 ? 0 : Math.round((passed / scored.length) * 100);
350
+ const identical = facets.every((f) => f.ok) && missingDatasourceKeys.length === 0;
351
+ return {
352
+ score,
353
+ maxScore: 100,
354
+ identical,
355
+ referenceDir: refAbs,
356
+ candidateDir: candAbs,
357
+ goldIsScoreOnly: true,
358
+ facets,
359
+ missingDatasourceKeys,
360
+ summary: identical
361
+ ? 'Candidate matches reference facets (score baseline only — not generation input)'
362
+ : `Parity score ${score}/100 — reference is score-only, never generation input`
363
+ };
364
+ }
365
+
366
+ /**
367
+ * Score candidate integration package directory against a reference package directory.
368
+ *
369
+ * @param {string} referenceDir Absolute or relative path to reference package (score baseline only)
370
+ * @param {string} candidateDir Absolute or relative path to candidate package
371
+ * @returns {{
372
+ * score: number,
373
+ * maxScore: number,
374
+ * identical: boolean,
375
+ * referenceDir: string,
376
+ * candidateDir: string,
377
+ * goldIsScoreOnly: true,
378
+ * facets: Object[],
379
+ * missingDatasourceKeys: string[],
380
+ * summary: string
381
+ * }}
382
+ */
383
+ function scorePackageParity(referenceDir, candidateDir) {
384
+ const refAbs = path.resolve(referenceDir);
385
+ const candAbs = path.resolve(candidateDir);
386
+ const facets = [];
387
+ const { refExists, candExists } = pushPresenceFacets(refAbs, candAbs, facets);
388
+
389
+ if (!refExists || !candExists) {
390
+ return {
391
+ score: 0,
392
+ maxScore: 100,
393
+ identical: false,
394
+ referenceDir: refAbs,
395
+ candidateDir: candAbs,
396
+ goldIsScoreOnly: true,
397
+ facets,
398
+ missingDatasourceKeys: [],
399
+ summary: 'Package directories incomplete — score 0 (no silent 100%)'
400
+ };
401
+ }
402
+
403
+ pushSystemManifestFacet(refAbs, candAbs, facets);
404
+ const refByKey = indexDatasourcesByKey(loadDatasourceManifests(refAbs));
405
+ const candByKey = indexDatasourcesByKey(loadDatasourceManifests(candAbs));
406
+ const missingDatasourceKeys = pushDatasourceCoverageFacet(refByKey, candByKey, facets);
407
+ pushOpenapiFacet(refAbs, candAbs, facets);
408
+ pushSharedDatasourceFacets(compareSharedDatasources(refByKey, candByKey), facets);
409
+ return buildParityReport(facets, missingDatasourceKeys, refAbs, candAbs);
410
+ }
411
+
412
+ module.exports = {
413
+ scorePackageParity,
414
+ loadDatasourceManifests,
415
+ openapiBasenames,
416
+ contractSurfaceFlags
417
+ };
@@ -12,6 +12,7 @@
12
12
  const CAPABILITIES = Object.freeze([
13
13
  { method: 'GET', route: '/workspaces/me/systems', handlerId: 'listSystems', operationType: 'workspace.read', mutating: false },
14
14
  { method: 'POST', route: '/workspaces/me/systems', handlerId: 'createSystem', operationType: 'workspace.write', mutating: true },
15
+ { method: 'POST', route: '/systems/:systemKey/openapi/persist-supplied', handlerId: 'persistSuppliedOpenApi', operationType: 'workspace.write', mutating: true },
15
16
  { method: 'GET', route: '/workspaces/me/systems/:systemKey', handlerId: 'getSystemManifest', operationType: 'workspace.read', mutating: false },
16
17
  { method: 'DELETE', route: '/workspaces/me/systems/:systemKey', handlerId: 'deleteSystem', operationType: 'workspace.write', mutating: true },
17
18
  { method: 'POST', route: '/systems/:systemKey/wizard', handlerId: 'runWizard', operationType: 'plan', mutating: true },
@@ -0,0 +1,35 @@
1
+ # Channel onboarding from your work list
2
+
3
+ Onboard an assistant channel after your external system is published from a Builder workspace.
4
+
5
+ ## Work list
6
+
7
+ Call GET workspaces/me/systems to list integration keys in your authenticated workspace. Each entry is a draft or published external system you are building.
8
+
9
+ ## Publish before channel
10
+
11
+ Complete validate and upload for the target systemKey so the dataplane exposes MCP tools and connected-system UI. Use repair with bearer-forward auth for facade APIs. Apply platform RBAC when the integration is a platform orchestration API.
12
+
13
+ ## Choose a channel target
14
+
15
+ | Target | Use when |
16
+ | --- | --- |
17
+ | teams-bot | Microsoft Teams employee assistant |
18
+ | slack | Slack workspace assistant |
19
+ | microsoft-copilot | Microsoft Copilot package (runtime-orchestration MCP) |
20
+ | chatgpt | ChatGPT custom MCP app (runtime-orchestration) |
21
+ | web-embed | Experimental embedded web assistant |
22
+
23
+ Each target requires credentials and deploy approval. Read topic connectedSystemUi after upload for dataplane handoff paths.
24
+
25
+ ## Recommended sequence
26
+
27
+ 1. GET help topic overview and platformRoles when scoping RBAC.
28
+ 2. GET workspaces/me/systems — pick or create systemKey.
29
+ 3. Run wizard, repair, validate, upload via Builder API.
30
+ 4. Scaffold channel integration with the CLI channel scaffold command for the chosen target.
31
+ 5. Register the channel on the deployed application with channel add.
32
+
33
+ ## Governance before go-live
34
+
35
+ Confirm resourceType catalog keys match business entities. Add dimensions on record and document datasources before protection. Scope system.subscriptions to business resource types, not generic placeholders.
@@ -0,0 +1,27 @@
1
+ # CIP execution overview
2
+
3
+ Composable Integration Pipeline is the declarative execution engine for datasource operations. Most external datasources use engine cip under the datasource.execution section.
4
+
5
+ ## Structure
6
+
7
+ Each operation under execution.cip.operations is a named map entry such as list, get, create, update, or delete. Custom operation names are allowed when they match capability keys.
8
+
9
+ Every operation contains an ordered steps array. Each step is a single keyed object: fetch, paginate, map, filter, output, or pythonInline.
10
+
11
+ ## Common step patterns
12
+
13
+ Fetch resolves data from OpenAPI references, raw HTTP, or another datasource. Map applies fieldMappings to normalize vendor payloads. Filter applies optional ABAC or JMESPath expressions. Output emits records for sync and storage.
14
+
15
+ Paginate handles cursor, page, or offset strategies for list operations. Python inline runs sandboxed transforms when declarative steps are insufficient.
16
+
17
+ ## Quality practices
18
+
19
+ Choose entityType and resourceType before authoring execution steps. Align openapi.operations keys with capabilities and fetch openapiRef values.
20
+
21
+ After changing metadata or capabilities, run repair hooks for fieldMappings, expose, and rbac before validate and test.
22
+
23
+ Use validate responses as the authoritative guide when step properties fail schema checks. Request topic schemaCatalog for property indexes, then topic section for governed fragments.
24
+
25
+ ## Related sections
26
+
27
+ datasource.execution, datasource.capabilities, datasource.fieldMappings, datasource.openapiOperations, datasource.metadata