@aifabrix/builder 2.52.0 → 2.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/lib/api/datasources-extended.api.js +8 -8
  2. package/lib/api/keycloak.api.js +92 -0
  3. package/lib/api/token-providers.api.js +77 -0
  4. package/lib/api/types/keycloak.types.js +23 -0
  5. package/lib/api/types/token-providers.types.js +65 -0
  6. package/lib/app/deploy-config.js +3 -2
  7. package/lib/app/down.js +16 -1
  8. package/lib/channels/add-channel.js +134 -57
  9. package/lib/channels/approval-guides/chatgpt.js +1 -1
  10. package/lib/channels/approval-guides/index.js +3 -0
  11. package/lib/channels/approval-guides/microsoft-copilot.js +27 -14
  12. package/lib/channels/channel-add-ensure.js +128 -0
  13. package/lib/channels/channel-add-existing.js +72 -0
  14. package/lib/channels/channel-artifact-generator.js +11 -1
  15. package/lib/channels/channel-branding.js +94 -0
  16. package/lib/channels/channel-credential.js +8 -0
  17. package/lib/channels/channel-discovery.js +330 -0
  18. package/lib/channels/channel-external-system.js +1 -1
  19. package/lib/channels/channel-runtime-resolver.js +107 -14
  20. package/lib/channels/channel-status-display.js +36 -3
  21. package/lib/channels/microsoft-copilot-channel-add.js +138 -0
  22. package/lib/channels/microsoft-copilot-entra-sso-form.js +251 -0
  23. package/lib/channels/microsoft-copilot-entra-sso.js +285 -0
  24. package/lib/channels/microsoft-copilot-icons.js +74 -0
  25. package/lib/channels/microsoft-copilot-limits.js +103 -0
  26. package/lib/channels/microsoft-copilot-package.js +178 -52
  27. package/lib/channels/microsoft-copilot-validate.js +227 -0
  28. package/lib/cli/attach-default-help-examples.js +102 -0
  29. package/lib/cli/index.js +4 -0
  30. package/lib/cli/infra-guided.js +13 -6
  31. package/lib/cli/setup-credential-deployment.js +7 -5
  32. package/lib/cli/setup-infra.js +31 -4
  33. package/lib/cli/setup-utility-validate.js +1 -0
  34. package/lib/commands/channel-add-setup.js +28 -9
  35. package/lib/commands/channel.js +14 -10
  36. package/lib/commands/credential-push.js +37 -5
  37. package/lib/commands/integration-definition.js +158 -0
  38. package/lib/commands/package-score.js +72 -0
  39. package/lib/commands/repair-auth-apply.js +19 -5
  40. package/lib/commands/repair-datasource-sync-operations.js +4 -1
  41. package/lib/commands/role-assistant-test-output.js +200 -0
  42. package/lib/commands/role-assistant.js +163 -166
  43. package/lib/datasource/capability/capability-resolve.js +15 -5
  44. package/lib/datasource/capability/copy-operations.js +14 -4
  45. package/lib/deployment/deployer-poll-run.js +2 -2
  46. package/lib/deployment/deployer.js +1 -1
  47. package/lib/deployment/environment.js +2 -2
  48. package/lib/external-system/deploy.js +11 -21
  49. package/lib/infrastructure/index.js +19 -4
  50. package/lib/integration-definition/apply.js +476 -0
  51. package/lib/integration-definition/canonical-json.js +51 -0
  52. package/lib/integration-definition/emit-suite-bid.js +94 -0
  53. package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
  54. package/lib/integration-definition/enrich-openapi-operations.js +433 -0
  55. package/lib/integration-definition/from-discovery.js +339 -0
  56. package/lib/integration-definition/hash.js +37 -0
  57. package/lib/integration-definition/index.js +37 -0
  58. package/lib/integration-definition/resource-type-catalog.js +59 -0
  59. package/lib/integration-definition/system-kind-enum.js +40 -0
  60. package/lib/integration-definition/validate.js +148 -0
  61. package/lib/lab/package-parity-score.js +417 -0
  62. package/lib/programmatic/builder-api-request.js +5 -1
  63. package/lib/programmatic/builder-help-governance.js +3 -2
  64. package/lib/programmatic/capability-registry.js +20 -0
  65. package/lib/programmatic/help-content/channel-onboarding.md +35 -0
  66. package/lib/programmatic/help-content/cip-overview.md +27 -0
  67. package/lib/programmatic/help-content/connected-system-ui.md +70 -0
  68. package/lib/programmatic/help-content/dimensions-guide.md +23 -0
  69. package/lib/programmatic/help-content/enterprise-sync-fabrix.md +123 -0
  70. package/lib/programmatic/help-content/overview.md +25 -0
  71. package/lib/programmatic/help-content/subscription-guide.md +27 -0
  72. package/lib/programmatic/help-content/workflow.md +50 -0
  73. package/lib/programmatic/openapi-component-schemas.js +7 -1
  74. package/lib/programmatic/openapi-descriptions.js +46 -0
  75. package/lib/programmatic/openapi-envelope-examples.js +280 -0
  76. package/lib/programmatic/openapi-operation-bindings.js +50 -0
  77. package/lib/programmatic/openapi-request-schemas.js +40 -0
  78. package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
  79. package/lib/programmatic/openapi-result-schemas.js +76 -99
  80. package/lib/programmatic/openapi-schema-descriptions-core.js +25 -4
  81. package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
  82. package/lib/programmatic/openapi-schema-descriptions-operations.js +43 -0
  83. package/lib/programmatic/openapi-schema-helpers.js +9 -9
  84. package/lib/programmatic/openapi-spec.js +11 -1
  85. package/lib/programmatic/orchestration-openapi-meta.js +10 -0
  86. package/lib/programmatic/persist-supplied-openapi.js +180 -0
  87. package/lib/programmatic/route-handler-map.js +35 -1
  88. package/lib/programmatic/run-channel-package.js +236 -0
  89. package/lib/programmatic/run-integration-definition.js +77 -0
  90. package/lib/role-assistant/evidence-sync.js +55 -7
  91. package/lib/role-assistant/knowledge-sync-logging.js +58 -0
  92. package/lib/role-assistant/knowledge-sync.js +88 -93
  93. package/lib/role-assistant/test-cases-bid.js +155 -0
  94. package/lib/role-assistant/test-cases-evaluate.js +9 -1
  95. package/lib/role-assistant/test-cases-honesty.js +24 -10
  96. package/lib/role-assistant/test-cases.js +28 -0
  97. package/lib/role-assistant/test-runner-attachments.js +118 -0
  98. package/lib/role-assistant/test-runner-bid-apply.js +201 -0
  99. package/lib/role-assistant/test-runner-bid-document.js +278 -0
  100. package/lib/role-assistant/test-runner-start-payload.js +96 -0
  101. package/lib/role-assistant/test-runner-workhub-answers.js +249 -20
  102. package/lib/role-assistant/test-runner-workhub-helpers.js +100 -0
  103. package/lib/role-assistant/test-runner-workhub-soft.js +86 -0
  104. package/lib/role-assistant/test-runner-workhub.js +152 -107
  105. package/lib/role-assistant/test-runner.js +5 -63
  106. package/lib/role-assistant/test-suite.js +112 -0
  107. package/lib/schema/application-schema.json +56 -6
  108. package/lib/schema/external-datasource.schema.json +103 -17
  109. package/lib/schema/external-system.schema.json +54 -3
  110. package/lib/schema/integration-definition.schema.json +195 -0
  111. package/lib/utils/authentication-shape.js +6 -0
  112. package/lib/utils/compose-generator.js +10 -1
  113. package/lib/utils/compose-traefik-extra-routes.js +107 -0
  114. package/lib/utils/docker-build.js +2 -2
  115. package/lib/utils/help-builder.js +3 -1
  116. package/lib/utils/promote-dev-docker-image.js +102 -18
  117. package/lib/utils/transient-developer-id.js +28 -0
  118. package/package.json +14 -16
  119. package/templates/README.md +12 -1
  120. package/templates/applications/dataplane/application.yaml +10 -2
  121. package/templates/applications/dataplane/env.template +7 -6
  122. package/templates/applications/miso-controller/application.yaml +23 -8
  123. package/templates/applications/miso-controller/env.template +8 -2
  124. package/templates/applications/miso-controller/rbac.yaml +1 -0
  125. package/templates/channels/README.md +11 -1
  126. package/templates/channels/brand/color-192.png +0 -0
  127. package/templates/channels/brand/dark/128.png +0 -0
  128. package/templates/channels/brand/dark/192.png +0 -0
  129. package/templates/channels/brand/dark/256.png +0 -0
  130. package/templates/channels/brand/dark/32.png +0 -0
  131. package/templates/channels/brand/dark/512.png +0 -0
  132. package/templates/channels/brand/dark/64.png +0 -0
  133. package/templates/channels/brand/light/128.png +0 -0
  134. package/templates/channels/brand/light/192.png +0 -0
  135. package/templates/channels/brand/light/256.png +0 -0
  136. package/templates/channels/brand/light/32.png +0 -0
  137. package/templates/channels/brand/light/512.png +0 -0
  138. package/templates/channels/brand/light/64.png +0 -0
  139. package/templates/channels/brand/mono-white/128.png +0 -0
  140. package/templates/channels/brand/mono-white/192.png +0 -0
  141. package/templates/channels/brand/mono-white/256.png +0 -0
  142. package/templates/channels/brand/mono-white/32.png +0 -0
  143. package/templates/channels/brand/mono-white/512.png +0 -0
  144. package/templates/channels/brand/mono-white/64.png +0 -0
  145. package/templates/channels/brand/outline-32.png +0 -0
  146. package/templates/channels/chatgpt/install.template.md.hbs +6 -4
  147. package/templates/channels/chatgpt/integration/README.md +4 -5
  148. package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
  149. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +14 -16
  150. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
  151. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +30 -0
  152. package/templates/channels/microsoft-copilot/fi-fi.template.json.hbs +7 -0
  153. package/templates/channels/microsoft-copilot/install.template.md.hbs +93 -22
  154. package/templates/channels/microsoft-copilot/integration/README.md +2 -3
  155. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
  156. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -19
  157. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +15 -0
  158. package/templates/channels/slack/integration/README.md +2 -4
  159. package/templates/channels/teams-bot/install.template.md.hbs +4 -0
  160. package/templates/channels/teams-bot/integration/README.md +3 -5
  161. package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
  162. package/templates/python/docker-compose.hbs +36 -0
  163. package/templates/typescript/docker-compose.hbs +36 -0
  164. package/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +0 -374
  165. package/.cursor/process/README.md +0 -9
  166. package/.cursor/rules/anchor-docs.mdc +0 -15
  167. package/.cursor/rules/assistant-driven-integration-build.mdc +0 -40
  168. package/.cursor/rules/cli-layout.mdc +0 -81
  169. package/.cursor/rules/cli-product-neutral.mdc +0 -43
  170. package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +0 -49
  171. package/.cursor/rules/docs-rules.mdc +0 -32
  172. package/.cursor/rules/plan-rules/application-lifecycle.mdc +0 -30
  173. package/.cursor/rules/plan-rules/plan-frontmatter.mdc +0 -35
  174. package/.cursor/rules/plan-rules/plan-lifecycle.mdc +0 -31
  175. package/.cursor/rules/plan-rules/plan-validation-gate.mdc +0 -34
  176. package/.cursor/rules/project-rules.mdc +0 -913
  177. package/.cursor/skills/README.md +0 -15
  178. package/.dockerignore +0 -52
  179. package/.eslintignore +0 -22
  180. package/.markdownlint.json +0 -14
  181. package/anchor-docs/README.md +0 -10
  182. package/anchor-docs/_TEMPLATE +0 -24
  183. package/babel.config.js +0 -6
  184. package/bin/builder-api-cleanup.js +0 -26
  185. package/bin/builder-api-nest.js +0 -10
  186. package/bin/builder-api-spike.js +0 -19
  187. package/enterprise-knowledge/README.md +0 -50
  188. package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +0 -22
  189. package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +0 -15
  190. package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +0 -69
  191. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +0 -52
  192. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +0 -29
  193. package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +0 -2
  194. package/integration/README.md +0 -81
  195. package/integration/github/README.md +0 -53
  196. package/integration/github/application.json +0 -21
  197. package/integration/github/deploy.js +0 -72
  198. package/integration/github/env.template +0 -11
  199. package/integration/github/github-datasource-issue-comments.json +0 -239
  200. package/integration/github/github-datasource-issues.json +0 -399
  201. package/integration/github/github-datasource-repository.json +0 -354
  202. package/integration/github/github-deploy.json +0 -1202
  203. package/integration/github/github-system.json +0 -82
  204. package/integration/hubspot-test/README.md +0 -159
  205. package/integration/hubspot-test/application.json +0 -54
  206. package/integration/hubspot-test/companies.json +0 -2048
  207. package/integration/hubspot-test/create-hubspot.js +0 -523
  208. package/integration/hubspot-test/env.template +0 -4
  209. package/integration/hubspot-test/hubspot-test-datasource-company.json +0 -138
  210. package/integration/hubspot-test/hubspot-test-datasource-contact.json +0 -146
  211. package/integration/hubspot-test/hubspot-test-datasource-deal.json +0 -146
  212. package/integration/hubspot-test/hubspot-test-datasource-users.json +0 -76
  213. package/integration/hubspot-test/hubspot-test-deploy.json +0 -2160
  214. package/integration/hubspot-test/hubspot-test-system.json +0 -74
  215. package/integration/hubspot-test/rbac.json +0 -166
  216. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +0 -20
  217. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +0 -9
  218. package/integration/hubspot-test/test-artifacts/wizard-invalid-add-datasource.yaml +0 -5
  219. package/integration/hubspot-test/test-artifacts/wizard-invalid-app-name.yaml +0 -5
  220. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-create.yaml +0 -7
  221. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-select.yaml +0 -7
  222. package/integration/hubspot-test/test-artifacts/wizard-invalid-known-platform.yaml +0 -4
  223. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-app.yaml +0 -4
  224. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  225. package/integration/hubspot-test/test-artifacts/wizard-invalid-mode.yaml +0 -5
  226. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-file.yaml +0 -5
  227. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-url.yaml +0 -4
  228. package/integration/hubspot-test/test-artifacts/wizard-invalid-source.yaml +0 -4
  229. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-array-test.yaml +0 -5
  230. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  231. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  232. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-test.yaml +0 -5
  233. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-test.yaml +0 -5
  234. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +0 -5
  235. package/integration/hubspot-test/test-dataplane-down-helpers.js +0 -249
  236. package/integration/hubspot-test/test-dataplane-down-tests.js +0 -386
  237. package/integration/hubspot-test/test-dataplane-down.js +0 -209
  238. package/integration/hubspot-test/test.js +0 -1588
  239. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +0 -16
  240. package/integration/hubspot-test/wizard-hubspot-platform.yaml +0 -8
  241. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +0 -23
  242. package/integration/roundtrip-test-local/README.md +0 -143
  243. package/integration/roundtrip-test-local/application.yaml +0 -13
  244. package/integration/roundtrip-test-local/env.template +0 -15
  245. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +0 -14
  246. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +0 -61
  247. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +0 -25
  248. package/integration/roundtrip-test-local2/README.md +0 -143
  249. package/integration/roundtrip-test-local2/application.yaml +0 -13
  250. package/integration/roundtrip-test-local2/env.template +0 -15
  251. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +0 -31
  252. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +0 -86
  253. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +0 -25
  254. package/integration/test/wizard.yaml +0 -8
  255. package/jest.config.coverage.js +0 -37
  256. package/jest.config.default.js +0 -11
  257. package/jest.config.integration.fixtures.js +0 -22
  258. package/jest.config.integration.js +0 -33
  259. package/jest.config.isolated.js +0 -11
  260. package/jest.config.manual.js +0 -30
  261. package/jest.global-live-fabrix-hooks.js +0 -36
  262. package/jest.isolated-projects.js +0 -260
  263. package/jest.projects.js +0 -76
  264. package/packages/builder-api/README.md +0 -120
  265. package/packages/builder-api/package.json +0 -30
  266. package/packages/builder-api/src/app.module.ts +0 -7
  267. package/packages/builder-api/src/auth/auth.module.ts +0 -5
  268. package/packages/builder-api/src/external-system/external-system.module.ts +0 -5
  269. package/packages/builder-api/src/main.ts +0 -16
  270. package/packages/builder-api/tsconfig.json +0 -15
  271. package/scripts/check-builder-api-architecture-grep.js +0 -79
  272. package/scripts/check-datasource-test-run-schema-sync.js +0 -34
  273. package/scripts/ci-fix.sh +0 -19
  274. package/scripts/ci-simulate.sh +0 -19
  275. package/scripts/diagnose-cli.js +0 -150
  276. package/scripts/docker_image_content_gate.js +0 -47
  277. package/scripts/docker_image_content_gate.sh +0 -5
  278. package/scripts/export-builder-api-evidence.js +0 -262
  279. package/scripts/install-local.js +0 -490
  280. package/scripts/lib/builder-api-docker-staging-copy.js +0 -60
  281. package/scripts/lib/builder-api-docker-staging-core.js +0 -183
  282. package/scripts/lib/docker-image-content-gate-core.js +0 -166
  283. package/scripts/lib/plan-actor.js +0 -109
  284. package/scripts/lib/plan-config.js +0 -20
  285. package/scripts/lib/plan-gates.js +0 -154
  286. package/scripts/lib/plan-meta.js +0 -104
  287. package/scripts/lib/plan-parse.js +0 -162
  288. package/scripts/lib/plan-prevalidate-gates.js +0 -337
  289. package/scripts/lib/plan-run-core.js +0 -125
  290. package/scripts/lib/plan-run-report.js +0 -119
  291. package/scripts/plan-approve.js +0 -24
  292. package/scripts/plan-archive.js +0 -49
  293. package/scripts/plan-run.js +0 -13
  294. package/scripts/plan-set-actor.js +0 -21
  295. package/scripts/plan-validate.js +0 -80
  296. package/scripts/pnpm/af.mjs +0 -110
  297. package/scripts/pnpm/check-quiet.mjs +0 -32
  298. package/scripts/pnpm/dev-install.mjs +0 -29
  299. package/scripts/pnpm/dev-reload.mjs +0 -18
  300. package/scripts/pnpm/help.mjs +0 -55
  301. package/scripts/pnpm/lib/dev-reload-core.mjs +0 -189
  302. package/scripts/pnpm/lib/dev-reload-log-capture.mjs +0 -67
  303. package/scripts/pnpm/lib/dev-reload-log-utils.mjs +0 -32
  304. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +0 -124
  305. package/scripts/pnpm/lib/resolve-app-url.mjs +0 -183
  306. package/scripts/pnpm/lib/spinner.mjs +0 -42
  307. package/scripts/pnpm-global-remove.js +0 -48
  308. package/scripts/stage_builder_api_for_docker.js +0 -65
  309. package/scripts/sync-builder-api-template.js +0 -44
  310. package/scripts/test-dataplane-bootstrap.js +0 -331
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Persist validated OpenAPI documents into workspace openapi/ (517.2).
3
+ * @fileoverview Runtime-authorized documents only — Builder never browses task store
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const crypto = require('crypto');
9
+ const path = require('path');
10
+ const fs = require('../internal/fs-real-sync');
11
+ const { assertValidSystemKey } = require('./system-key');
12
+ const { ensureIntegrationDirectory } = require('./workspace-context');
13
+
14
+ function sha256Hex(buffer) {
15
+ return crypto.createHash('sha256').update(buffer).digest('hex');
16
+ }
17
+
18
+ function safeOpenApiBasename(filename) {
19
+ const raw = String(filename || '').trim();
20
+ const base = path.basename(raw);
21
+ if (!base || base === '.' || base === '..') {
22
+ throw new Error('openapi.filename.invalid');
23
+ }
24
+ if (raw.includes('..') || raw.includes('/') || raw.includes('\\') || raw.includes('\0')) {
25
+ throw new Error('openapi.filename.pathTraversal');
26
+ }
27
+ return base.endsWith('.json') || base.endsWith('.yaml') || base.endsWith('.yml')
28
+ ? base
29
+ : `${base}.json`;
30
+ }
31
+
32
+ function parseOpenApiDocument(body) {
33
+ if (body && typeof body === 'object' && !Array.isArray(body)) {
34
+ return body;
35
+ }
36
+ if (typeof body === 'string') {
37
+ const trimmed = body.trim();
38
+ if (!trimmed) {
39
+ throw new Error('openapi.empty');
40
+ }
41
+ if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
42
+ return JSON.parse(trimmed);
43
+ }
44
+ throw new Error('openapi.yamlUnsupportedInSliceB');
45
+ }
46
+ throw new Error('openapi.body.required');
47
+ }
48
+
49
+ function validateOpenApiDocument(doc) {
50
+ if (!doc || typeof doc !== 'object' || Array.isArray(doc)) {
51
+ return { ok: false, reason: 'openapi.invalid', detail: 'Document must be an object' };
52
+ }
53
+ const version = typeof doc.openapi === 'string' ? doc.openapi.trim() : '';
54
+ if (!/^3\./.test(version)) {
55
+ return {
56
+ ok: false,
57
+ reason: 'openapi.invalid',
58
+ detail: 'Top-level openapi must be a 3.x string'
59
+ };
60
+ }
61
+ if (!doc.paths || typeof doc.paths !== 'object' || Array.isArray(doc.paths)) {
62
+ return {
63
+ ok: false,
64
+ reason: 'openapi.invalid',
65
+ detail: 'paths object is required'
66
+ };
67
+ }
68
+ if (Object.keys(doc.paths).length === 0) {
69
+ return {
70
+ ok: false,
71
+ reason: 'openapi.invalid',
72
+ detail: 'paths must not be empty for persist'
73
+ };
74
+ }
75
+ return { ok: true };
76
+ }
77
+
78
+ function documentBytes(doc) {
79
+ return Buffer.from(`${JSON.stringify(doc, null, 2)}\n`, 'utf8');
80
+ }
81
+
82
+ function openapiDirForContext(ctx) {
83
+ ensureIntegrationDirectory(ctx);
84
+ return path.join(ctx.getIntegrationRoot(), ctx.systemKey, 'openapi');
85
+ }
86
+
87
+ function relativeWorkspacePath(ctx, absolutePath) {
88
+ const root = ctx.getIntegrationRoot();
89
+ const rel = path.relative(root, absolutePath);
90
+ // root is .../integration; rel already includes <systemKey>/openapi/<file>
91
+ return path.join('integration', rel).split(path.sep).join('/');
92
+ }
93
+
94
+ function persistOneDocument(ctx, row) {
95
+ let filename = 'unknown.json';
96
+ try {
97
+ filename = safeOpenApiBasename(row && (row.filename || row.name));
98
+ } catch (error) {
99
+ return {
100
+ rejected: {
101
+ filename: String((row && (row.filename || row.name)) || 'unknown'),
102
+ reason: 'openapi.filename.invalid',
103
+ detail: error.message || String(error)
104
+ }
105
+ };
106
+ }
107
+ let doc;
108
+ try {
109
+ doc = parseOpenApiDocument(row && row.body);
110
+ } catch (error) {
111
+ return {
112
+ rejected: {
113
+ filename,
114
+ reason: 'openapi.invalid',
115
+ detail: error.message || String(error)
116
+ }
117
+ };
118
+ }
119
+ const check = validateOpenApiDocument(doc);
120
+ if (!check.ok) {
121
+ return { rejected: { filename, reason: check.reason, detail: check.detail } };
122
+ }
123
+ const bytes = documentBytes(doc);
124
+ const digest = `sha256:${sha256Hex(bytes)}`;
125
+ if (row.digest && String(row.digest).trim() && String(row.digest).trim() !== digest) {
126
+ return {
127
+ rejected: {
128
+ filename,
129
+ reason: 'openapi.digestMismatch',
130
+ detail: 'Provided digest does not match serialized body'
131
+ }
132
+ };
133
+ }
134
+ const dir = openapiDirForContext(ctx);
135
+ fs.mkdirSync(dir, { recursive: true });
136
+ const absolutePath = path.join(dir, filename);
137
+ fs.writeFileSync(absolutePath, bytes);
138
+ return {
139
+ accepted: {
140
+ filename,
141
+ path: relativeWorkspacePath(ctx, absolutePath),
142
+ digest
143
+ }
144
+ };
145
+ }
146
+
147
+ /**
148
+ * @param {import('./workspace-context').WorkspaceContext} ctx
149
+ * @param {Object} body
150
+ * @returns {{ accepted: Object[], rejected: Object[], workspaceRoot: string }}
151
+ */
152
+ function persistSuppliedOpenApi(ctx, body) {
153
+ assertValidSystemKey(ctx.systemKey);
154
+ const documents = body && Array.isArray(body.documents) ? body.documents : null;
155
+ if (!documents || documents.length === 0) {
156
+ throw new Error('documents array is required');
157
+ }
158
+ const accepted = [];
159
+ const rejected = [];
160
+ for (const row of documents) {
161
+ const outcome = persistOneDocument(ctx, row);
162
+ if (outcome.accepted) {
163
+ accepted.push(outcome.accepted);
164
+ } else if (outcome.rejected) {
165
+ rejected.push(outcome.rejected);
166
+ }
167
+ }
168
+ return {
169
+ accepted,
170
+ rejected,
171
+ workspaceRoot: ctx.getIntegrationRoot()
172
+ };
173
+ }
174
+
175
+ module.exports = {
176
+ persistSuppliedOpenApi,
177
+ validateOpenApiDocument,
178
+ safeOpenApiBasename,
179
+ parseOpenApiDocument
180
+ };
@@ -10,7 +10,11 @@
10
10
 
11
11
  const { buildContext } = require('./route-handlers-context');
12
12
  const { listSystems, createSystem, getSystemManifest, deleteSystem } = require('./workspace-ops');
13
+ const { persistSuppliedOpenApi } = require('./persist-supplied-openapi');
13
14
  const { runWizard } = require('./run-wizard');
15
+ const {
16
+ applyIntegrationDefinitionInWorkspace
17
+ } = require('./run-integration-definition');
14
18
  const { runRepair } = require('./run-repair');
15
19
  const { runValidate } = require('./run-validate');
16
20
  const { runPreviewUpload, runUpload } = require('./run-upload');
@@ -41,6 +45,15 @@ const {
41
45
  activateEvidenceCandidates
42
46
  } = require('./run-evidence-authoring');
43
47
 
48
+ const {
49
+ buildChannelPackage,
50
+ getChannelPackageSummary,
51
+ getChannelPackageZip,
52
+ patchChannelBranding,
53
+ getChannelBranding
54
+ } = require('./run-channel-package');
55
+ const { handleGetBuilderHelp } = require('./builder-help');
56
+
44
57
  /** @type {Record<string, Function>} */
45
58
  const HANDLER_MAP = {
46
59
  listSystems: (_params, auth) => listSystems(buildContext(auth, '_meta')),
@@ -51,12 +64,22 @@ const HANDLER_MAP = {
51
64
  }
52
65
  return createSystem(buildContext(auth, key), key);
53
66
  },
67
+ persistSuppliedOpenApi: (params, auth, body) =>
68
+ persistSuppliedOpenApi(
69
+ buildContext(auth, params.systemKey),
70
+ body && typeof body === 'object' ? body : {}
71
+ ),
54
72
  getSystemManifest: (params, auth) => getSystemManifest(buildContext(auth, params.systemKey)),
55
73
  deleteSystem: (params, auth) => deleteSystem(buildContext(auth, params.systemKey)),
56
74
  runWizard: (params, auth, body, query) =>
57
75
  runWizard(buildContext(auth, params.systemKey), body, {
58
76
  debug: query.debug === 'true' || body.debug === true
59
77
  }),
78
+ applyIntegrationDefinition: (params, auth, body) =>
79
+ applyIntegrationDefinitionInWorkspace(
80
+ buildContext(auth, params.systemKey),
81
+ body && typeof body === 'object' ? body : {}
82
+ ),
60
83
  runRepair: (params, auth, body, query) =>
61
84
  runRepair(buildContext(auth, params.systemKey), {
62
85
  dryRun: query.dryRun === 'true' || body.dryRun === true,
@@ -179,6 +202,7 @@ const HANDLER_MAP = {
179
202
  runRepositoryDispatchPipeline(buildContext(auth, params.systemKey), body),
180
203
  health: () => ({ status: 'ok' }),
181
204
  ready: () => ({ status: 'ready', dataplaneUrl: process.env.DATAPLANE_URL || null }),
205
+ getBuilderHelp: (_params, _auth, _body, query) => handleGetBuilderHelp(query || {}),
182
206
  getEvidenceMetadata: (params, auth) =>
183
207
  getEvidenceMetadata(buildContext(auth, '_evidence'), params.roleKey),
184
208
  downloadEvidence: (params, auth, _body, query) =>
@@ -199,7 +223,17 @@ const HANDLER_MAP = {
199
223
  roleKey: (body && body.roleKey) || params.roleKey
200
224
  }),
201
225
  activateEvidenceCandidates: (_params, auth, body) =>
202
- activateEvidenceCandidates(buildContext(auth, '_evidence'), body)
226
+ activateEvidenceCandidates(buildContext(auth, '_evidence'), body),
227
+ buildChannelPackage: (params, auth, body) =>
228
+ buildChannelPackage(buildContext(auth, params.systemKey), body || {}),
229
+ getChannelPackageSummary: (params, auth) =>
230
+ getChannelPackageSummary(buildContext(auth, params.systemKey)),
231
+ getChannelPackageZip: (params, auth) =>
232
+ getChannelPackageZip(buildContext(auth, params.systemKey)),
233
+ getChannelBranding: (params, auth) =>
234
+ getChannelBranding(buildContext(auth, params.systemKey)),
235
+ patchChannelBranding: (params, auth, body) =>
236
+ patchChannelBranding(buildContext(auth, params.systemKey), body || {})
203
237
  };
204
238
 
205
239
  /**
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Builder API channel packaging handlers (plan 167.0).
3
+ *
4
+ * @fileoverview Package build / branding / zip for workspace channel systemKeys
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const fs = require('../internal/fs-real-sync');
12
+ const path = require('path');
13
+ const { ensureIntegrationDirectory } = require('./workspace-context');
14
+ const { resolveChannelRuntimeValues } = require('../channels/channel-runtime-resolver');
15
+ const { generateChannelArtifact } = require('../channels/channel-artifact-generator');
16
+ const { loadChannelPackageContext } = require('../channels/channel-discovery');
17
+ const {
18
+ readChannelBranding,
19
+ writeChannelBranding,
20
+ mergeChannelBranding
21
+ } = require('../channels/channel-branding');
22
+ const { isChannelTargetKey } = require('../channels/channel-targets');
23
+
24
+ /**
25
+ * @param {import('./workspace-context').WorkspaceContext} ctx
26
+ * @returns {string}
27
+ */
28
+ function workspaceSystemDir(ctx) {
29
+ return ctx.getIntegrationPath();
30
+ }
31
+
32
+ /**
33
+ * Channel target equals workspace systemKey for packaging apps.
34
+ * @param {string} systemKey
35
+ * @returns {string}
36
+ */
37
+ function requireChannelTarget(systemKey) {
38
+ if (!isChannelTargetKey(systemKey)) {
39
+ throw new Error(
40
+ `systemKey "${systemKey}" is not a supported channel packaging target`
41
+ );
42
+ }
43
+ return systemKey;
44
+ }
45
+
46
+ /**
47
+ * @param {import('./workspace-context').WorkspaceContext} ctx
48
+ * @returns {{ type: string, token: string }}
49
+ */
50
+ function authConfigFromContext(ctx) {
51
+ return { type: 'bearer', token: ctx.bearerToken };
52
+ }
53
+
54
+ /**
55
+ * @param {Object} branding
56
+ * @param {string} channelTarget
57
+ * @returns {Object}
58
+ */
59
+ function fallbackPackageContext(branding, channelTarget) {
60
+ return {
61
+ displayName: branding.displayName || channelTarget,
62
+ description: branding.description || '',
63
+ governedToolNames: [],
64
+ mcpServerUrl: null,
65
+ installReady: false,
66
+ blocker: 'discoveryUnavailable',
67
+ authentication: null
68
+ };
69
+ }
70
+
71
+ /**
72
+ * @param {import('./workspace-context').WorkspaceContext} ctx
73
+ * @param {Object} body
74
+ * @param {string} channelTarget
75
+ * @param {Object} branding
76
+ * @param {string} dataplaneUrl
77
+ * @param {Object} packageContext
78
+ * @returns {Object}
79
+ */
80
+ function generatePackageArtifact(ctx, body, channelTarget, branding, dataplaneUrl, packageContext) {
81
+ const appName =
82
+ branding.displayName || body.name || packageContext.displayName || channelTarget;
83
+ const description =
84
+ branding.description || body.description || packageContext.description || '';
85
+ const runtimeValues = resolveChannelRuntimeValues({
86
+ dataplaneBaseUrl: dataplaneUrl,
87
+ channelTarget,
88
+ appName,
89
+ baseName: body.baseName || appName,
90
+ environment: body.environment || ctx.environment || 'dev',
91
+ appDescription: description,
92
+ layerBToolNames: packageContext.governedToolNames || [],
93
+ packageContext,
94
+ iconColorPath: branding.iconColorPath,
95
+ iconOutlinePath: branding.iconOutlinePath
96
+ });
97
+ return generateChannelArtifact({
98
+ projectRoot: ctx.getUserRoot(),
99
+ channelTarget,
100
+ runtimeValues,
101
+ dryRun: body.dryRun === true
102
+ });
103
+ }
104
+
105
+ /**
106
+ * @param {import('./workspace-context').WorkspaceContext} ctx
107
+ * @param {Object} body
108
+ * @returns {Promise<Object>}
109
+ */
110
+ async function buildChannelPackage(ctx, body = {}) {
111
+ const channelTarget = requireChannelTarget(ctx.systemKey);
112
+ ensureIntegrationDirectory(ctx);
113
+ const dir = workspaceSystemDir(ctx);
114
+ const branding = mergeChannelBranding(readChannelBranding(dir), body.branding || {});
115
+ if (body.branding && typeof body.branding === 'object') {
116
+ writeChannelBranding(dir, branding);
117
+ }
118
+ const dataplaneUrl = ctx.dataplaneUrl || body.dataplaneUrl;
119
+ if (!dataplaneUrl) {
120
+ throw new Error('dataplaneUrl is required to build a channel package');
121
+ }
122
+ const packageContext = await loadChannelPackageContext({
123
+ dataplaneBaseUrl: dataplaneUrl,
124
+ authConfig: authConfigFromContext(ctx),
125
+ channelTarget,
126
+ branding
127
+ }).catch(() => fallbackPackageContext(branding, channelTarget));
128
+ const artifact = generatePackageArtifact(
129
+ ctx,
130
+ body,
131
+ channelTarget,
132
+ branding,
133
+ dataplaneUrl,
134
+ packageContext
135
+ );
136
+ return {
137
+ channelTarget,
138
+ installReady: artifact.installReady === true,
139
+ blocker: artifact.blocker || packageContext.blocker || null,
140
+ artifactHash: artifact.artifactHash,
141
+ links: artifact.links || {
142
+ readme: artifact.installPath,
143
+ zip: artifact.zipPath || null
144
+ },
145
+ summary: artifact.content ? JSON.parse(artifact.content) : null
146
+ };
147
+ }
148
+
149
+ /**
150
+ * @param {import('./workspace-context').WorkspaceContext} ctx
151
+ * @returns {Object}
152
+ */
153
+ function getChannelPackageSummary(ctx) {
154
+ const channelTarget = requireChannelTarget(ctx.systemKey);
155
+ const projectRoot = ctx.getUserRoot();
156
+ const summaryPath = path.join(
157
+ projectRoot,
158
+ 'dist',
159
+ 'channels',
160
+ channelTarget,
161
+ `channel-${channelTarget}.json`
162
+ );
163
+ if (!fs.existsSync(summaryPath)) {
164
+ throw new Error(`Channel package summary not found: ${summaryPath}`);
165
+ }
166
+ const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
167
+ return {
168
+ channelTarget,
169
+ summary,
170
+ links: summary.links || null,
171
+ installReady: summary.installReady === true,
172
+ blocker: summary.blocker || null
173
+ };
174
+ }
175
+
176
+ /**
177
+ * @param {import('./workspace-context').WorkspaceContext} ctx
178
+ * @returns {{ fileName: string, contentType: string, byteLength: number, zipBase64: string }}
179
+ */
180
+ function getChannelPackageZip(ctx) {
181
+ const channelTarget = requireChannelTarget(ctx.systemKey);
182
+ const summary = getChannelPackageSummary(ctx).summary;
183
+ const zipPath =
184
+ typeof summary?.links?.zip === 'string' && summary.links.zip
185
+ ? summary.links.zip
186
+ : path.join(
187
+ ctx.getUserRoot(),
188
+ 'dist',
189
+ 'channels',
190
+ channelTarget,
191
+ 'build',
192
+ `appPackage.${summary.environment || 'dev'}.zip`
193
+ );
194
+ if (!fs.existsSync(zipPath)) {
195
+ throw new Error(`Channel package ZIP not found: ${zipPath}`);
196
+ }
197
+ const buffer = fs.readFileSync(zipPath);
198
+ return {
199
+ fileName: path.basename(zipPath),
200
+ contentType: 'application/zip',
201
+ byteLength: buffer.length,
202
+ zipBase64: buffer.toString('base64')
203
+ };
204
+ }
205
+
206
+ /**
207
+ * @param {import('./workspace-context').WorkspaceContext} ctx
208
+ * @param {Object} body
209
+ * @returns {Object}
210
+ */
211
+ function patchChannelBranding(ctx, body = {}) {
212
+ requireChannelTarget(ctx.systemKey);
213
+ ensureIntegrationDirectory(ctx);
214
+ const branding = writeChannelBranding(workspaceSystemDir(ctx), body || {});
215
+ return { systemKey: ctx.systemKey, branding };
216
+ }
217
+
218
+ /**
219
+ * @param {import('./workspace-context').WorkspaceContext} ctx
220
+ * @returns {Object}
221
+ */
222
+ function getChannelBranding(ctx) {
223
+ requireChannelTarget(ctx.systemKey);
224
+ return {
225
+ systemKey: ctx.systemKey,
226
+ branding: readChannelBranding(workspaceSystemDir(ctx))
227
+ };
228
+ }
229
+
230
+ module.exports = {
231
+ buildChannelPackage,
232
+ getChannelPackageSummary,
233
+ getChannelPackageZip,
234
+ patchChannelBranding,
235
+ getChannelBranding
236
+ };
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Apply Builder Integration Definition (BID) into a user workspace.
3
+ *
4
+ * @fileoverview POST …/integration-definition/apply
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const fs = require('../internal/fs-real-sync');
13
+ const {
14
+ applyIntegrationDefinition,
15
+ withDefinitionHash
16
+ } = require('../integration-definition');
17
+ const {
18
+ runWithWorkspaceContext,
19
+ ensureIntegrationDirectory,
20
+ WORKSPACE_INTEGRATION_FORMAT
21
+ } = require('./workspace-context');
22
+ const { touchWorkspaceMeta } = require('./workspace-meta');
23
+
24
+ /**
25
+ * Workspace openapi/ is the only product resolve root for BID openapiSourceRef
26
+ * (517.2 persist layout). Do not bind apply to ship-local enrichment fixtures.
27
+ * @param {import('./workspace-context').WorkspaceContext} ctx
28
+ * @returns {string}
29
+ */
30
+ function workspaceOpenApiBaseDir(ctx) {
31
+ const dir = path.join(ctx.getIntegrationPath(), 'openapi');
32
+ fs.mkdirSync(dir, { recursive: true });
33
+ return dir;
34
+ }
35
+
36
+ /**
37
+ * @param {import('./workspace-context').WorkspaceContext} ctx
38
+ * @param {Object} body
39
+ * @param {Object} [body.integrationDefinition]
40
+ * @param {boolean} [body.force]
41
+ * @param {boolean} [body.dryRun]
42
+ * @returns {Promise<Object>}
43
+ */
44
+ async function applyIntegrationDefinitionInWorkspace(ctx, body = {}) {
45
+ const bidInput = body.integrationDefinition || body.bid || body;
46
+ if (!bidInput || typeof bidInput !== 'object') {
47
+ throw new Error('integrationDefinition object is required in request body');
48
+ }
49
+ const force = body.force === true;
50
+ const dryRun = body.dryRun === true;
51
+ return runWithWorkspaceContext(ctx, async() => {
52
+ ensureIntegrationDirectory(ctx);
53
+ const bid = withDefinitionHash(bidInput);
54
+ const identity = bid.packageIdentity || {};
55
+ const bidKey = String(identity.systemKey || '').trim();
56
+ if (bidKey && bidKey !== ctx.systemKey) {
57
+ throw new Error(
58
+ `BID packageIdentity.systemKey (${bidKey}) must match path systemKey (${ctx.systemKey})`
59
+ );
60
+ }
61
+ const out = await applyIntegrationDefinition(bid, {
62
+ outputDir: ctx.getIntegrationPath(),
63
+ force,
64
+ dryRun,
65
+ openapiBaseDir: workspaceOpenApiBaseDir(ctx)
66
+ });
67
+ touchWorkspaceMeta(ctx, {
68
+ lastOperation: 'integration-definition-apply',
69
+ format: WORKSPACE_INTEGRATION_FORMAT,
70
+ definitionId: out.definitionId,
71
+ definitionHash: out.definitionHash
72
+ });
73
+ return out;
74
+ });
75
+ }
76
+
77
+ module.exports = { applyIntegrationDefinitionInWorkspace };