@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,330 @@
1
+ /**
2
+ * External AI Discovery mapping for channel packaging (plan 167.0).
3
+ *
4
+ * @fileoverview Pure discovery → package context; optional Dataplane fetch
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { ApiClient } = require('../api');
12
+ const { RUNTIME_ORCHESTRATION_SYSTEM_KEY } = require('./chatgpt-runtime-contract');
13
+ const { normalizeBaseUrl, joinUrl } = require('./channel-runtime-resolver');
14
+
15
+ const GATEWAY_TOOLS_CATALOG_MARKER = '/api/v1/mcp/servers/tools';
16
+
17
+ /**
18
+ * @param {string} pathOrUrl
19
+ * @returns {boolean}
20
+ */
21
+ function isGatewayToolsCatalogUrl(pathOrUrl) {
22
+ const value = String(pathOrUrl || '');
23
+ return value.includes(GATEWAY_TOOLS_CATALOG_MARKER);
24
+ }
25
+
26
+ /**
27
+ * Normalize discovery paths that include a public `/data` prefix.
28
+ * @param {string} dataplaneBaseUrl
29
+ * @param {string|null|undefined} pathOrUrl
30
+ * @returns {string|null}
31
+ */
32
+ function resolveDiscoveryUrl(dataplaneBaseUrl, pathOrUrl) {
33
+ if (!pathOrUrl || typeof pathOrUrl !== 'string') {
34
+ return null;
35
+ }
36
+ const trimmed = pathOrUrl.trim();
37
+ if (!trimmed) {
38
+ return null;
39
+ }
40
+ if (isGatewayToolsCatalogUrl(trimmed)) {
41
+ throw new Error(
42
+ 'Gateway tools catalog must not be used as MCP server URL for channel packages'
43
+ );
44
+ }
45
+ if (/^https?:\/\//i.test(trimmed)) {
46
+ return trimmed;
47
+ }
48
+ let relative = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
49
+ if (relative.startsWith('/data/api/')) {
50
+ relative = relative.slice('/data'.length);
51
+ }
52
+ return joinUrl(normalizeBaseUrl(dataplaneBaseUrl), relative);
53
+ }
54
+
55
+ /**
56
+ * @param {Object} discovery
57
+ * @param {string} key
58
+ * @returns {Object|null}
59
+ */
60
+ function findEntryPoint(discovery, key) {
61
+ const entries = Array.isArray(discovery?.entryPoints) ? discovery.entryPoints : [];
62
+ return entries.find((ep) => ep && ep.key === key) || null;
63
+ }
64
+
65
+ /**
66
+ * @param {Object} discovery
67
+ * @returns {string[]}
68
+ */
69
+ function extractGovernedToolNames(discovery) {
70
+ const fromRuntime = discovery?.runtimeDiscovery?.governedToolNames;
71
+ if (Array.isArray(fromRuntime) && fromRuntime.length > 0) {
72
+ return fromRuntime.map((name) => String(name)).filter(Boolean);
73
+ }
74
+ const tools = Array.isArray(discovery?.tools) ? discovery.tools : [];
75
+ return tools.map((tool) => (tool && tool.name ? String(tool.name) : '')).filter(Boolean);
76
+ }
77
+
78
+ /**
79
+ * @param {Object} discovery
80
+ * @param {Object|null} adapter
81
+ * @param {string} dataplaneBaseUrl
82
+ * @returns {{ mcpServerUrl: string|null, installReady: boolean, blocker: string|null }}
83
+ */
84
+ function resolveMcpConnection(discovery, adapter, dataplaneBaseUrl) {
85
+ const adapterUrl = adapter?.connection?.mcpServerUrl || null;
86
+ const remote = findEntryPoint(discovery, 'remoteMcp');
87
+ const fromEntry =
88
+ resolveDiscoveryUrl(dataplaneBaseUrl, remote?.absoluteUrl) ||
89
+ resolveDiscoveryUrl(dataplaneBaseUrl, remote?.path);
90
+ const mcpServerUrl = resolveDiscoveryUrl(dataplaneBaseUrl, adapterUrl) || fromEntry;
91
+ if (mcpServerUrl && isGatewayToolsCatalogUrl(mcpServerUrl)) {
92
+ throw new Error(
93
+ 'Gateway tools catalog must not be used as MCP server URL for channel packages'
94
+ );
95
+ }
96
+ const mcpReady = discovery?.publication?.mcpReady === true;
97
+ const connectable = adapter ? adapter.connectable === true : mcpReady;
98
+ const remoteAvailable = discovery?.transport?.remoteMcp?.status === 'available';
99
+ const installReady = Boolean(mcpReady && connectable && remoteAvailable && mcpServerUrl);
100
+ const blocker =
101
+ adapter?.blocker ||
102
+ (!installReady ? 'remoteMcpUnavailable' : null);
103
+ return { mcpServerUrl, installReady, blocker };
104
+ }
105
+
106
+ /**
107
+ * @param {Object} branding
108
+ * @param {Object} identity
109
+ * @param {Object|null} adapter
110
+ * @returns {string}
111
+ */
112
+ function resolvePackageDisplayName(branding, identity, adapter) {
113
+ if (typeof branding.displayName === 'string' && branding.displayName.trim()) {
114
+ return branding.displayName.trim();
115
+ }
116
+ return (
117
+ identity.displayName ||
118
+ adapter?.nameForHuman ||
119
+ adapter?.displayName ||
120
+ identity.systemKey ||
121
+ RUNTIME_ORCHESTRATION_SYSTEM_KEY
122
+ );
123
+ }
124
+
125
+ /**
126
+ * @param {Object} branding
127
+ * @param {Object} identity
128
+ * @param {Object|null} adapter
129
+ * @param {Object} discovery
130
+ * @returns {string}
131
+ */
132
+ function resolvePackageDescription(branding, identity, adapter, discovery) {
133
+ if (typeof branding.description === 'string' && branding.description.trim()) {
134
+ return branding.description.trim();
135
+ }
136
+ return (
137
+ identity.description ||
138
+ adapter?.descriptionForHuman ||
139
+ adapter?.description ||
140
+ discovery.operatingModel?.summary ||
141
+ ''
142
+ );
143
+ }
144
+
145
+ /**
146
+ * @param {Object|null} auth
147
+ * @returns {Object|null}
148
+ */
149
+ function mapAdapterAuthentication(auth) {
150
+ if (!auth) return null;
151
+ return {
152
+ recommendedCapability: auth.recommendedCapability || null,
153
+ issuer: auth.issuer || null,
154
+ openidConfigurationUrl: auth.openidConfigurationUrl || null,
155
+ authorizationServerMetadataUrl: auth.authorizationServerMetadataUrl || null,
156
+ protectedResourceMetadataUrl: auth.protectedResourceMetadataUrl || null,
157
+ resource: auth.resource || auth.apiResource || null,
158
+ audience: auth.audience !== undefined && auth.audience !== null ? auth.audience : null,
159
+ scopes: Array.isArray(auth.scopes)
160
+ ? [...auth.scopes]
161
+ : Array.isArray(auth.allowedScopes)
162
+ ? [...auth.allowedScopes]
163
+ : [],
164
+ operatorNote: auth.operatorNote || null,
165
+ pkceRequired: auth.pkceRequired === true,
166
+ clientRegistrationModes: Array.isArray(auth.clientRegistrationModes)
167
+ ? [...auth.clientRegistrationModes]
168
+ : []
169
+ };
170
+ }
171
+
172
+ /**
173
+ * @param {Object} branding
174
+ * @returns {boolean}
175
+ */
176
+ function isBrandingApplied(branding) {
177
+ return Boolean(
178
+ (branding.displayName && String(branding.displayName).trim()) ||
179
+ (branding.description && String(branding.description).trim())
180
+ );
181
+ }
182
+
183
+ /**
184
+ * Map discovery (+ optional adapter) into packaging fields.
185
+ *
186
+ * @param {Object} params
187
+ * @param {Object} params.discovery
188
+ * @param {Object} [params.adapter]
189
+ * @param {string} params.dataplaneBaseUrl
190
+ * @param {Object} [params.branding]
191
+ * @returns {Object}
192
+ */
193
+ function mapDiscoveryToPackageContext(params) {
194
+ const discovery = params.discovery;
195
+ if (!discovery || typeof discovery !== 'object') {
196
+ throw new Error('discovery document is required');
197
+ }
198
+ const identity = discovery.identity || {};
199
+ const branding = params.branding && typeof params.branding === 'object' ? params.branding : {};
200
+ const adapter = params.adapter || null;
201
+ const connection = resolveMcpConnection(discovery, adapter, params.dataplaneBaseUrl);
202
+ const authCaps = Array.isArray(discovery.authenticationCapabilities)
203
+ ? discovery.authenticationCapabilities.map((c) => c.capability || c).filter(Boolean)
204
+ : [];
205
+ return {
206
+ systemKey: identity.systemKey || RUNTIME_ORCHESTRATION_SYSTEM_KEY,
207
+ displayName: resolvePackageDisplayName(branding, identity, adapter),
208
+ description: resolvePackageDescription(branding, identity, adapter, discovery),
209
+ governedToolNames: extractGovernedToolNames(discovery),
210
+ mcpServerUrl: connection.mcpServerUrl,
211
+ installReady: connection.installReady,
212
+ blocker: connection.blocker,
213
+ authentication: mapAdapterAuthentication(adapter?.authentication || null),
214
+ authenticationCapabilities: authCaps,
215
+ publication: {
216
+ mcpReady: discovery.publication?.mcpReady === true,
217
+ environment: discovery.publication?.environment || null
218
+ },
219
+ brandingApplied: isBrandingApplied(branding)
220
+ };
221
+ }
222
+
223
+ /**
224
+ * @param {Object} response
225
+ * @returns {Object}
226
+ */
227
+ function unwrapApiData(response) {
228
+ if (response && typeof response === 'object' && response.data && typeof response.data === 'object') {
229
+ if (response.data.kind || response.data.identity || response.data.adapter) {
230
+ return response.data;
231
+ }
232
+ }
233
+ return response;
234
+ }
235
+
236
+ /**
237
+ * @param {string} dataplaneBaseUrl
238
+ * @param {Object} authConfig
239
+ * @param {string} [systemKey]
240
+ * @returns {Promise<Object>}
241
+ */
242
+ async function fetchExternalAiDiscovery(
243
+ dataplaneBaseUrl,
244
+ authConfig,
245
+ systemKey = RUNTIME_ORCHESTRATION_SYSTEM_KEY
246
+ ) {
247
+ const client = new ApiClient(normalizeBaseUrl(dataplaneBaseUrl), authConfig || {});
248
+ const response = await client.get(`/api/v1/external/systems/${systemKey}/discovery`);
249
+ const discovery = unwrapApiData(response);
250
+ if (!discovery || !discovery.kind) {
251
+ throw new Error(`Discovery document missing for systemKey=${systemKey}`);
252
+ }
253
+ return discovery;
254
+ }
255
+
256
+ /**
257
+ * @param {string} dataplaneBaseUrl
258
+ * @param {Object} authConfig
259
+ * @param {string} adapterKey microsoft-plugin | chatgpt-setup
260
+ * @param {string} [systemKey]
261
+ * @returns {Promise<Object>}
262
+ */
263
+ async function fetchExternalAiDiscoveryAdapter(
264
+ dataplaneBaseUrl,
265
+ authConfig,
266
+ adapterKey,
267
+ systemKey = RUNTIME_ORCHESTRATION_SYSTEM_KEY
268
+ ) {
269
+ const client = new ApiClient(normalizeBaseUrl(dataplaneBaseUrl), authConfig || {});
270
+ const response = await client.get(
271
+ `/api/v1/external/systems/${systemKey}/discovery/adapters/${adapterKey}`
272
+ );
273
+ return unwrapApiData(response);
274
+ }
275
+
276
+ /**
277
+ * @param {string} channelTarget
278
+ * @returns {string|null}
279
+ */
280
+ function adapterKeyForChannel(channelTarget) {
281
+ if (channelTarget === 'microsoft-copilot') return 'microsoft-plugin';
282
+ if (channelTarget === 'chatgpt') return 'chatgpt-setup';
283
+ return null;
284
+ }
285
+
286
+ /**
287
+ * Fetch discovery (+ adapter when known) and map package context.
288
+ *
289
+ * @param {Object} params
290
+ * @returns {Promise<Object>}
291
+ */
292
+ async function loadChannelPackageContext(params) {
293
+ const systemKey = params.runtimeSystemKey || RUNTIME_ORCHESTRATION_SYSTEM_KEY;
294
+ const discovery = await fetchExternalAiDiscovery(
295
+ params.dataplaneBaseUrl,
296
+ params.authConfig,
297
+ systemKey
298
+ );
299
+ const adapterKey = adapterKeyForChannel(params.channelTarget);
300
+ let adapter = null;
301
+ if (adapterKey) {
302
+ try {
303
+ adapter = await fetchExternalAiDiscoveryAdapter(
304
+ params.dataplaneBaseUrl,
305
+ params.authConfig,
306
+ adapterKey,
307
+ systemKey
308
+ );
309
+ } catch {
310
+ adapter = null;
311
+ }
312
+ }
313
+ return mapDiscoveryToPackageContext({
314
+ discovery,
315
+ adapter,
316
+ dataplaneBaseUrl: params.dataplaneBaseUrl,
317
+ branding: params.branding
318
+ });
319
+ }
320
+
321
+ module.exports = {
322
+ GATEWAY_TOOLS_CATALOG_MARKER,
323
+ isGatewayToolsCatalogUrl,
324
+ resolveDiscoveryUrl,
325
+ mapDiscoveryToPackageContext,
326
+ fetchExternalAiDiscovery,
327
+ fetchExternalAiDiscoveryAdapter,
328
+ adapterKeyForChannel,
329
+ loadChannelPackageContext
330
+ };
@@ -72,7 +72,7 @@ async function fetchCommunicationSystem(ctx) {
72
72
  if (!system || typeof system !== 'object') {
73
73
  throw new Error(
74
74
  `Communication external system "${ctx.channelTarget}" was not found. ` +
75
- `Run: aifabrix deploy ${ctx.channelTarget}`
75
+ `Run: aifabrix channel add ${ctx.channelTarget} --name "…" --force`
76
76
  );
77
77
  }
78
78
  const kind = system.systemKind || system.systemKindKey;
@@ -19,6 +19,12 @@ const {
19
19
  COPILOT_RUNTIME_TRANSPORT,
20
20
  COPILOT_RUNTIME_AUTH_MODE
21
21
  } = require('./chatgpt-runtime-contract');
22
+ const { applyMicrosoftCopilotDisplayLimits } = require('./microsoft-copilot-limits');
23
+ const {
24
+ resolveCopilotEntraScopes,
25
+ evaluateCopilotEntraSsoReadiness
26
+ } = require('./microsoft-copilot-entra-sso');
27
+ const { applyCopilotEntraSsoFormFields } = require('./microsoft-copilot-entra-sso-form');
22
28
 
23
29
  /** Default Slack slash prefix — matches dataplane `templates/slack/command_catalog.yaml`. */
24
30
  const DEFAULT_SLACK_COMMAND_PREFIX = 'af';
@@ -152,15 +158,62 @@ function buildDeterministicGuid(seed) {
152
158
  * @param {Object} resolved
153
159
  * @param {Object} params
154
160
  */
155
- function applyMicrosoftCopilotRuntimeValues(resolved, params) {
161
+ /**
162
+ * Apply discovery-driven package context (plan 167.0).
163
+ * @param {Object} resolved
164
+ * @param {Object} [packageContext]
165
+ */
166
+ function applyDiscoveryPackageContext(resolved, packageContext) {
167
+ if (!packageContext || typeof packageContext !== 'object') {
168
+ resolved.installReady = false;
169
+ resolved.discoveryBlocker = resolved.discoveryBlocker || 'discoveryUnavailable';
170
+ return;
171
+ }
172
+ if (Array.isArray(packageContext.governedToolNames) && packageContext.governedToolNames.length > 0) {
173
+ resolved.layerBToolNames = [...packageContext.governedToolNames];
174
+ }
175
+ if (packageContext.mcpServerUrl) {
176
+ resolved.mcpUrl = packageContext.mcpServerUrl;
177
+ }
178
+ if (packageContext.displayName && !resolved.brandingNameOverride) {
179
+ // Keep CLI --name as appName; discovery fills defaults only when requested upstream.
180
+ }
181
+ if (packageContext.description && !resolved.appDescription) {
182
+ resolved.appDescription = packageContext.description;
183
+ }
184
+ resolved.installReady = packageContext.installReady === true;
185
+ resolved.discoveryBlocker = packageContext.blocker || null;
186
+ resolved.discoveryAuthentication = packageContext.authentication || null;
187
+ resolved.discoveryAuthenticationCapabilities = packageContext.authenticationCapabilities || [];
188
+ }
189
+
190
+ /**
191
+ * @param {Object} resolved
192
+ * @returns {void}
193
+ */
194
+ function applyCopilotRuntimeDomain(resolved) {
195
+ resolved.runtimeDomain = '';
196
+ try {
197
+ resolved.runtimeDomain = new URL(resolved.mcpUrl).hostname;
198
+ } catch {
199
+ try {
200
+ resolved.runtimeDomain = new URL(resolved.dataplaneBaseUrl).hostname;
201
+ } catch {
202
+ resolved.runtimeDomain = '';
203
+ }
204
+ }
205
+ }
206
+
207
+ /**
208
+ * @param {Object} resolved
209
+ * @param {Object} params
210
+ * @returns {void}
211
+ */
212
+ function applyCopilotPackageIdentity(resolved, params) {
156
213
  resolved.runtimeSystemKey = COPILOT_RUNTIME_SYSTEM_KEY;
157
214
  resolved.runtimeTransport = COPILOT_RUNTIME_TRANSPORT;
158
215
  resolved.runtimeAuthMode = COPILOT_RUNTIME_AUTH_MODE;
159
216
  resolved.layerBToolNames = Array.isArray(params.layerBToolNames) ? [...params.layerBToolNames] : [];
160
- resolved.scopes = Array.isArray(params.scopes)
161
- ? [...params.scopes]
162
- : ['openid', 'profile', 'offline_access'];
163
- resolved.scopesJson = JSON.stringify(resolved.scopes);
164
217
  resolved.packageVersion = typeof params.packageVersion === 'string' ? params.packageVersion : '1.0.0';
165
218
  resolved.packageAppId = buildDeterministicGuid(
166
219
  `${resolved.channelTarget}:${resolved.baseName}:${resolved.environment}:app`
@@ -168,16 +221,49 @@ function applyMicrosoftCopilotRuntimeValues(resolved, params) {
168
221
  resolved.pluginId = buildDeterministicGuid(
169
222
  `${resolved.channelTarget}:${resolved.baseName}:${resolved.environment}:plugin`
170
223
  );
171
- resolved.runtimeDomain = '';
172
- try {
173
- resolved.runtimeDomain = new URL(resolved.mcpUrl).hostname;
174
- } catch {
175
- try {
176
- resolved.runtimeDomain = new URL(resolved.dataplaneBaseUrl).hostname;
177
- } catch {
178
- resolved.runtimeDomain = '';
179
- }
224
+ const referenceId =
225
+ typeof params.oauthPluginVaultReferenceId === 'string' &&
226
+ params.oauthPluginVaultReferenceId.trim()
227
+ ? params.oauthPluginVaultReferenceId.trim()
228
+ : '';
229
+ resolved.oauthPluginVaultReferenceId = referenceId || null;
230
+ }
231
+
232
+ /**
233
+ * @param {Object} resolved
234
+ * @param {Object} params
235
+ * @returns {void}
236
+ */
237
+ function applyMicrosoftCopilotRuntimeValues(resolved, params) {
238
+ applyCopilotPackageIdentity(resolved, params);
239
+ applyDiscoveryPackageContext(resolved, params.packageContext);
240
+ resolved.scopes = resolveCopilotEntraScopes({
241
+ explicitScopes: params.scopes,
242
+ packageContext: params.packageContext,
243
+ entraProvider: params.entraProvider
244
+ });
245
+ resolved.scopesJson = JSON.stringify(resolved.scopes);
246
+ const readiness = evaluateCopilotEntraSsoReadiness({
247
+ packageContext: params.packageContext,
248
+ oauthPluginVaultReferenceId: resolved.oauthPluginVaultReferenceId,
249
+ scopes: resolved.scopes,
250
+ entraProvider: params.entraProvider || null,
251
+ mcpServerUrl: resolved.mcpUrl || null
252
+ });
253
+ resolved.installReady = readiness.installReady;
254
+ resolved.discoveryBlocker = readiness.blocker;
255
+ resolved.packageReady = readiness.packageReady;
256
+ resolved.packageBlocker = readiness.packageBlocker;
257
+ resolved.microsoftPublicationPending = readiness.microsoftPublicationPending !== false;
258
+ resolved.discoveryAuthentication = readiness.packageAuthentication;
259
+ if (params.entraProvider?.issuer) {
260
+ resolved.entraIssuer = params.entraProvider.issuer;
180
261
  }
262
+ if (params.iconColorPath) resolved.iconColorPath = params.iconColorPath;
263
+ if (params.iconOutlinePath) resolved.iconOutlinePath = params.iconOutlinePath;
264
+ applyCopilotRuntimeDomain(resolved);
265
+ applyCopilotEntraSsoFormFields(resolved, params);
266
+ applyMicrosoftCopilotDisplayLimits(resolved);
181
267
  }
182
268
 
183
269
  /**
@@ -254,6 +340,7 @@ function resolveChannelRuntimeValues(params) {
254
340
 
255
341
  if (params.channelTarget === 'slack') {
256
342
  applySlackRuntimeValues(resolved, params);
343
+ applyDiscoveryPackageContext(resolved, params.packageContext);
257
344
  }
258
345
 
259
346
  if (params.channelTarget === 'microsoft-copilot') {
@@ -262,6 +349,11 @@ function resolveChannelRuntimeValues(params) {
262
349
 
263
350
  if (params.channelTarget === 'chatgpt') {
264
351
  applyChatGptRuntimeValues(resolved, params);
352
+ applyDiscoveryPackageContext(resolved, params.packageContext);
353
+ }
354
+
355
+ if (params.channelTarget === 'teams-bot') {
356
+ applyDiscoveryPackageContext(resolved, params.packageContext);
265
357
  }
266
358
 
267
359
  return resolved;
@@ -277,6 +369,7 @@ module.exports = {
277
369
  applySlackRuntimeValues,
278
370
  applyMicrosoftCopilotRuntimeValues,
279
371
  applyChatGptRuntimeValues,
372
+ applyDiscoveryPackageContext,
280
373
  resolveSlackOAuthManifestFields,
281
374
  resolveChannelRuntimeValues,
282
375
  DEFAULT_SLACK_COMMAND_PREFIX
@@ -55,10 +55,10 @@ function displayApprovalGuideSection(approvalGuide) {
55
55
  const heading =
56
56
  approvalGuide.approvalRequired === false
57
57
  ? 'Go-live steps'
58
- : 'Platform admin approval required';
58
+ : 'Organisation publish / Teams admin approval required';
59
59
  logger.log(formatWarningLine(heading));
60
60
  if (approvalGuide.portalUrl) {
61
- logger.log(metadata('Admin console:'));
61
+ logger.log(metadata('Developer Portal:'));
62
62
  logger.log(chalk.cyan(approvalGuide.portalUrl));
63
63
  }
64
64
  logger.log('');
@@ -125,7 +125,40 @@ function displayChannelCoreFields(result) {
125
125
  logger.log(metadata(`Artifact: ${result.artifactPath}`));
126
126
  }
127
127
  if (result.installGuidePath) {
128
- logger.log(metadata(`Install guide: ${result.installGuidePath}`));
128
+ logger.log(metadata(`Install guide (README): ${result.installGuidePath}`));
129
+ }
130
+ if (result.zipPath) {
131
+ logger.log(metadata(`Package ZIP: ${result.zipPath}`));
132
+ }
133
+ displayReadinessLines(result);
134
+ }
135
+
136
+ /**
137
+ * Split package/Runtime readiness from Microsoft organisation publication.
138
+ * @param {Object} result
139
+ */
140
+ function displayReadinessLines(result) {
141
+ const packageReady =
142
+ result.packageReady === true ||
143
+ (result.packageReady === undefined && result.installReady === true);
144
+ const packageBlocker = result.packageBlocker || result.blocker || null;
145
+ if (packageReady) {
146
+ logger.log(metadata('Package / Runtime ready: yes'));
147
+ } else if (packageBlocker) {
148
+ logger.log(metadata(`Package / Runtime ready: no (${packageBlocker})`));
149
+ } else if (result.installReady === false) {
150
+ logger.log(metadata('Package / Runtime ready: no'));
151
+ }
152
+ if (result.microsoftPublicationPending === true) {
153
+ logger.log(
154
+ metadata(
155
+ 'Microsoft publication: pending (Developer Portal import → Publish to your org → Teams admin approval)'
156
+ )
157
+ );
158
+ } else if (result.microsoftPublicationPending === false) {
159
+ logger.log(metadata('Microsoft publication: cleared'));
160
+ } else if (result.installReady === false && result.blocker && !packageBlocker) {
161
+ logger.log(metadata(`Install ready: no (${result.blocker})`));
129
162
  }
130
163
  }
131
164
 
@@ -0,0 +1,138 @@
1
+ /**
2
+ * microsoft-copilot channel-add helpers (Entra SSO form context for operator errors).
3
+ *
4
+ * @fileoverview Keep add-channel.js under size limits
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { resolveChannelAuthContext } = require('./channel-auth-context');
12
+ const {
13
+ normalizeBaseChannelName,
14
+ resolveChannelDisplayName
15
+ } = require('./channel-display-name');
16
+ const { getDefaultChannelDescription } = require('./channel-targets');
17
+ const { resolveAppDescription } = require('./channel-description');
18
+ const { loadChannelPackageContext } = require('./channel-discovery');
19
+ const { getInboundMicrosoftEntraIdProvider } = require('../api/token-providers.api');
20
+ const { getKeycloakEntraIdentityProvider } = require('../api/keycloak.api');
21
+ const { resolveChannelRuntimeValues } = require('./channel-runtime-resolver');
22
+ const { assertOauthPluginVaultReferenceId } = require('./microsoft-copilot-entra-sso');
23
+
24
+ /**
25
+ * @param {Object} options
26
+ * @returns {boolean}
27
+ */
28
+ function hasOauthPluginVaultReference(options) {
29
+ return (
30
+ typeof options.oauthPluginVaultReferenceId === 'string' &&
31
+ Boolean(options.oauthPluginVaultReferenceId.trim())
32
+ );
33
+ }
34
+
35
+ /**
36
+ * Load Trust source + Keycloak Entra IdP for Copilot packaging (null on miss/error).
37
+ * @param {Object} ctx
38
+ * @returns {Promise<{ entraProvider: Object|null, entraIdentityProvider: Object|null }>}
39
+ */
40
+ async function loadMicrosoftCopilotEntraContext(ctx) {
41
+ let entraProvider = null;
42
+ let entraIdentityProvider = null;
43
+ if (!ctx?.controllerUrl) {
44
+ return { entraProvider, entraIdentityProvider };
45
+ }
46
+ try {
47
+ entraProvider = await getInboundMicrosoftEntraIdProvider(ctx.controllerUrl, ctx.authConfig);
48
+ } catch {
49
+ entraProvider = null;
50
+ }
51
+ try {
52
+ entraIdentityProvider = await getKeycloakEntraIdentityProvider(
53
+ ctx.controllerUrl,
54
+ ctx.authConfig
55
+ );
56
+ } catch {
57
+ entraIdentityProvider = null;
58
+ }
59
+ return { entraProvider, entraIdentityProvider };
60
+ }
61
+
62
+ /**
63
+ * @param {Object} params
64
+ * @returns {Promise<Object>}
65
+ */
66
+ async function resolveCopilotFormContextForError(params) {
67
+ const { options, ctx, name, baseName, description } = params;
68
+ let packageContext = null;
69
+ try {
70
+ packageContext = await loadChannelPackageContext({
71
+ dataplaneBaseUrl: ctx.dataplaneUrl,
72
+ authConfig: ctx.authConfig,
73
+ channelTarget: 'microsoft-copilot',
74
+ branding: { displayName: name, description }
75
+ });
76
+ } catch {
77
+ packageContext = null;
78
+ }
79
+ const { entraProvider, entraIdentityProvider } = await loadMicrosoftCopilotEntraContext(ctx);
80
+ return resolveChannelRuntimeValues({
81
+ dataplaneBaseUrl: ctx.dataplaneUrl,
82
+ channelTarget: 'microsoft-copilot',
83
+ appName: name,
84
+ baseName,
85
+ environment: ctx.environment,
86
+ appDescription: description,
87
+ packageContext,
88
+ entraProvider,
89
+ entraIdentityProvider,
90
+ oauthPluginVaultReferenceId: options.oauthPluginVaultReferenceId
91
+ });
92
+ }
93
+
94
+ /**
95
+ * @param {Object} params
96
+ * @throws {Error}
97
+ */
98
+ async function assertMicrosoftCopilotOauthReference(params) {
99
+ const { options } = params;
100
+ if (hasOauthPluginVaultReference(options)) {
101
+ return;
102
+ }
103
+ const formContext = await resolveCopilotFormContextForError(params);
104
+ assertOauthPluginVaultReferenceId({ ...options, formContext });
105
+ }
106
+
107
+ /**
108
+ * Fail before force upload when Teams Entra SSO auth-config id is missing.
109
+ * @param {Object} options
110
+ * @throws {Error}
111
+ */
112
+ async function assertMicrosoftCopilotOauthReferenceBeforeForce(options) {
113
+ if (hasOauthPluginVaultReference(options)) {
114
+ return;
115
+ }
116
+ const ctx = await resolveChannelAuthContext('microsoft-copilot', { silent: options.quiet });
117
+ const baseName = normalizeBaseChannelName(options.name);
118
+ const name = resolveChannelDisplayName(baseName, ctx.environment);
119
+ const rawDescription =
120
+ options.description && String(options.description).trim()
121
+ ? String(options.description).trim()
122
+ : getDefaultChannelDescription();
123
+ await assertMicrosoftCopilotOauthReference({
124
+ options,
125
+ ctx,
126
+ name,
127
+ baseName,
128
+ description: resolveAppDescription('microsoft-copilot', rawDescription)
129
+ });
130
+ }
131
+
132
+ module.exports = {
133
+ hasOauthPluginVaultReference,
134
+ loadMicrosoftCopilotEntraContext,
135
+ resolveCopilotFormContextForError,
136
+ assertMicrosoftCopilotOauthReference,
137
+ assertMicrosoftCopilotOauthReferenceBeforeForce
138
+ };