@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,251 @@
1
+ /**
2
+ * Microsoft Copilot Entra SSO form field resolution + template rendering.
3
+ *
4
+ * @fileoverview Template-backed Teams Entra SSO registration guidance
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 Handlebars = require('handlebars');
14
+ const { BUILDER_ROOT } = require('./channel-integration-template');
15
+
16
+ const ENTRA_SSO_REGISTRATION_URL =
17
+ 'https://dev.teams.microsoft.com/tools/entra-configuration';
18
+ const TEMPLATES_DIR = path.join(BUILDER_ROOT, 'templates', 'channels', 'microsoft-copilot');
19
+
20
+ /**
21
+ * @param {string} issuer
22
+ * @returns {string|null}
23
+ */
24
+ function extractTenantIdFromIssuer(issuer) {
25
+ if (typeof issuer !== 'string' || !issuer.trim()) {
26
+ return null;
27
+ }
28
+ const match = issuer.match(
29
+ /login\.microsoftonline\.com\/([0-9a-fA-F-]{36})(?:\/|$)/i
30
+ );
31
+ return match ? match[1] : null;
32
+ }
33
+
34
+ /**
35
+ * @param {string} [url]
36
+ * @returns {string|null}
37
+ */
38
+ function originFromUrl(url) {
39
+ if (typeof url !== 'string' || !url.trim()) {
40
+ return null;
41
+ }
42
+ try {
43
+ const parsed = new URL(url.trim());
44
+ return `${parsed.protocol}//${parsed.host}`;
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Prefer Keycloak status enterpriseAppId (Entra SSO application/client id).
52
+ * @param {Object|null} entraIdentityProvider
53
+ * @param {Object|null} entraProvider
54
+ * @param {Object|null} auth
55
+ * @returns {string|null}
56
+ */
57
+ function resolveEntraSsoClientId(entraIdentityProvider, entraProvider, auth) {
58
+ if (
59
+ entraIdentityProvider &&
60
+ typeof entraIdentityProvider.enterpriseAppId === 'string' &&
61
+ entraIdentityProvider.enterpriseAppId.trim()
62
+ ) {
63
+ return entraIdentityProvider.enterpriseAppId.trim();
64
+ }
65
+ if (entraProvider && typeof entraProvider.clientId === 'string' && entraProvider.clientId.trim()) {
66
+ return entraProvider.clientId.trim();
67
+ }
68
+ if (auth && typeof auth.clientId === 'string' && auth.clientId.trim()) {
69
+ return auth.clientId.trim();
70
+ }
71
+ return null;
72
+ }
73
+
74
+ /**
75
+ * @param {Object} resolved
76
+ * @param {Object|null} entraIdentityProvider
77
+ * @param {Object|null} entraProvider
78
+ * @param {Object|null} auth
79
+ * @returns {string|null}
80
+ */
81
+ function resolveEntraTenantId(resolved, entraIdentityProvider, entraProvider, auth) {
82
+ if (
83
+ entraIdentityProvider &&
84
+ typeof entraIdentityProvider.tenantId === 'string' &&
85
+ entraIdentityProvider.tenantId.trim()
86
+ ) {
87
+ return entraIdentityProvider.tenantId.trim();
88
+ }
89
+ return extractTenantIdFromIssuer(
90
+ resolved.entraIssuer || entraProvider?.issuer || auth?.issuer || null
91
+ );
92
+ }
93
+
94
+ /**
95
+ * @param {Object} resolved
96
+ * @param {Object} [params]
97
+ * @returns {void}
98
+ */
99
+ function applyCopilotEntraSsoFormFields(resolved, params = {}) {
100
+ const entraProvider = params.entraProvider || null;
101
+ const entraIdentityProvider = params.entraIdentityProvider || null;
102
+ const auth = resolved.discoveryAuthentication || null;
103
+ resolved.entraSsoRegistrationUrl = ENTRA_SSO_REGISTRATION_URL;
104
+ // Teams Entra SSO form Base URL = exact Remote MCP URL from package discovery.
105
+ const mcpUrl =
106
+ (typeof resolved.mcpUrl === 'string' && resolved.mcpUrl.trim()) || null;
107
+ resolved.entraSsoBaseUrl = mcpUrl || originFromUrl(resolved.dataplaneBaseUrl) || null;
108
+ resolved.entraSsoClientId = resolveEntraSsoClientId(
109
+ entraIdentityProvider,
110
+ entraProvider,
111
+ auth
112
+ );
113
+ const scopes = Array.isArray(resolved.scopes) ? resolved.scopes : [];
114
+ resolved.entraSsoScope = scopes.length > 0 ? scopes.join(', ') : null;
115
+ resolved.entraTenantId = resolveEntraTenantId(
116
+ resolved,
117
+ entraIdentityProvider,
118
+ entraProvider,
119
+ auth
120
+ );
121
+ if (entraIdentityProvider?.enterpriseAppId) {
122
+ resolved.entraEnterpriseAppId = String(entraIdentityProvider.enterpriseAppId).trim();
123
+ }
124
+ applyWebApplicationInfoFields(resolved, params);
125
+ }
126
+
127
+ const TEAMS_AUTH_CONFIG_GUID =
128
+ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
129
+
130
+ /**
131
+ * @param {string|unknown} value
132
+ * @returns {string}
133
+ */
134
+ function trimNonEmptyString(value) {
135
+ return typeof value === 'string' ? value.trim() : '';
136
+ }
137
+
138
+ /**
139
+ * Teams Entra SSO Application ID URI: api://auth-{authConfigId}/{clientId}.
140
+ * Only when oauthPluginVaultReferenceId is a GUID (Teams auth-config id).
141
+ * @param {string|null} authConfigId
142
+ * @param {string|null} clientId
143
+ * @returns {string|null}
144
+ */
145
+ function buildTeamsWebApplicationInfoResource(authConfigId, clientId) {
146
+ const configId = trimNonEmptyString(authConfigId);
147
+ const appId = trimNonEmptyString(clientId);
148
+ if (!configId || !TEAMS_AUTH_CONFIG_GUID.test(configId) || !appId) {
149
+ return null;
150
+ }
151
+ return `api://auth-${configId}/${appId}`;
152
+ }
153
+
154
+ /**
155
+ * @param {Object} resolved
156
+ * @returns {string|null}
157
+ */
158
+ function resolveWebApplicationClientId(resolved) {
159
+ return (
160
+ trimNonEmptyString(resolved.entraSsoClientId) ||
161
+ trimNonEmptyString(resolved.entraEnterpriseAppId) ||
162
+ null
163
+ );
164
+ }
165
+
166
+ /**
167
+ * Preserve / derive Teams webApplicationInfo for Entra SSO packages.
168
+ * Prefer explicit params, then prior package values, then GUID auth-config + SSO client id.
169
+ * Do not invent resource URIs from opaque (non-GUID) vault references.
170
+ * @param {Object} resolved
171
+ * @param {Object} params
172
+ * @returns {void}
173
+ */
174
+ function applyWebApplicationInfoFields(resolved, params = {}) {
175
+ const prior = params.priorWebApplicationInfo || null;
176
+ const explicitId = trimNonEmptyString(params.webApplicationInfoId);
177
+ const explicitResource = trimNonEmptyString(params.webApplicationInfoResource);
178
+ const priorId = trimNonEmptyString(prior && prior.id);
179
+ const priorResource = trimNonEmptyString(prior && prior.resource);
180
+ const clientId = resolveWebApplicationClientId(resolved);
181
+ const derivedResource = buildTeamsWebApplicationInfoResource(
182
+ resolved.oauthPluginVaultReferenceId,
183
+ clientId
184
+ );
185
+ const id = explicitId || priorId || (derivedResource ? clientId : null);
186
+ const resource = explicitResource || priorResource || derivedResource || null;
187
+ if (id && resource) {
188
+ resolved.webApplicationInfoId = id;
189
+ resolved.webApplicationInfoResource = resource;
190
+ }
191
+ }
192
+
193
+ /**
194
+ * @param {string} fileName
195
+ * @returns {string}
196
+ */
197
+ function readCopilotTemplate(fileName) {
198
+ const templatePath = path.join(TEMPLATES_DIR, fileName);
199
+ if (!fs.existsSync(templatePath)) {
200
+ throw new Error(`Microsoft Copilot template not found: ${templatePath}`);
201
+ }
202
+ return fs.readFileSync(templatePath, 'utf8');
203
+ }
204
+
205
+ /**
206
+ * @param {string} fileName
207
+ * @param {Object} context
208
+ * @returns {string}
209
+ */
210
+ function renderCopilotChannelTemplate(fileName, context) {
211
+ const hb = Handlebars.create();
212
+ hb.registerPartial('entraSsoRegistration', readCopilotTemplate('entra-sso-registration.template.md.hbs'));
213
+ const template = hb.compile(readCopilotTemplate(fileName));
214
+ return template(context).trim();
215
+ }
216
+
217
+ /**
218
+ * Operator-facing error when Teams Entra SSO auth-config id is missing.
219
+ * Prose lives in templates — not inline CLI copy.
220
+ * @param {Object|string} [context]
221
+ * @returns {string}
222
+ */
223
+ function formatOauthPluginVaultReferenceRequiredError(context = {}) {
224
+ const values = typeof context === 'string' ? { appName: context, baseName: context } : context || {};
225
+ const appName =
226
+ (typeof values.appName === 'string' && values.appName.trim()) ||
227
+ (typeof values.baseName === 'string' && values.baseName.trim()) ||
228
+ 'Your Assistant';
229
+ const baseName =
230
+ (typeof values.baseName === 'string' && values.baseName.trim()) || appName;
231
+ return renderCopilotChannelTemplate('oauth-plugin-vault-required.template.md.hbs', {
232
+ ...values,
233
+ appName,
234
+ baseName,
235
+ channelTarget: 'microsoft-copilot',
236
+ entraSsoRegistrationUrl: values.entraSsoRegistrationUrl || ENTRA_SSO_REGISTRATION_URL
237
+ });
238
+ }
239
+
240
+ module.exports = {
241
+ ENTRA_SSO_REGISTRATION_URL,
242
+ extractTenantIdFromIssuer,
243
+ originFromUrl,
244
+ resolveEntraSsoClientId,
245
+ resolveEntraTenantId,
246
+ applyCopilotEntraSsoFormFields,
247
+ applyWebApplicationInfoFields,
248
+ buildTeamsWebApplicationInfoResource,
249
+ renderCopilotChannelTemplate,
250
+ formatOauthPluginVaultReferenceRequiredError
251
+ };
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Microsoft Copilot Entra SSO / trustedIssuer packaging helpers (plan 169.0).
3
+ *
4
+ * @fileoverview Readiness, scopes, and auth selection for microsoft-copilot
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const TRUSTED_ISSUER = 'trustedIssuer';
12
+ const CUSTOMER_OAUTH = 'customerOAuth';
13
+ const OPENID_ONLY = new Set(['openid', 'profile', 'offline_access']);
14
+
15
+ /**
16
+ * @param {*} value
17
+ * @returns {boolean}
18
+ */
19
+ function isPresent(value) {
20
+ return value !== undefined && value !== null && value !== '';
21
+ }
22
+
23
+ /**
24
+ * @param {string} channelTarget
25
+ * @returns {boolean}
26
+ */
27
+ function skipsChannelOauthCredential(channelTarget) {
28
+ return channelTarget === 'microsoft-copilot';
29
+ }
30
+
31
+ /**
32
+ * @param {unknown} scopes
33
+ * @returns {string[]}
34
+ */
35
+ function normalizeScopeList(scopes) {
36
+ if (!Array.isArray(scopes)) {
37
+ return [];
38
+ }
39
+ return scopes.map((s) => String(s).trim()).filter(Boolean);
40
+ }
41
+
42
+ /**
43
+ * True when scopes are empty or only OIDC login scopes (insufficient for Runtime MCP).
44
+ * @param {string[]} scopes
45
+ * @returns {boolean}
46
+ */
47
+ function isOpenIdOnlyOrEmptyScopes(scopes) {
48
+ const list = normalizeScopeList(scopes);
49
+ if (list.length === 0) {
50
+ return true;
51
+ }
52
+ return list.every((scope) => OPENID_ONLY.has(scope));
53
+ }
54
+
55
+ /**
56
+ * @param {Object|null|undefined} auth
57
+ * @returns {string|null}
58
+ */
59
+ function resolveRecommendedCapability(auth) {
60
+ const value = auth && typeof auth.recommendedCapability === 'string'
61
+ ? auth.recommendedCapability.trim()
62
+ : '';
63
+ return value || null;
64
+ }
65
+
66
+ /**
67
+ * Map adapter auth for Copilot packaging: keep trustedIssuer public fields;
68
+ * drop Keycloak/customerOAuth AS metadata from the package surface.
69
+ * @param {Object|null} auth
70
+ * @returns {Object|null}
71
+ */
72
+ function mapCopilotPackageAuthentication(auth) {
73
+ if (!auth || typeof auth !== 'object') {
74
+ return null;
75
+ }
76
+ const recommended = resolveRecommendedCapability(auth);
77
+ const base = {
78
+ recommendedCapability: recommended,
79
+ issuer: auth.issuer || null,
80
+ openidConfigurationUrl: auth.openidConfigurationUrl || null,
81
+ protectedResourceMetadataUrl: auth.protectedResourceMetadataUrl || null,
82
+ resource: auth.resource || auth.apiResource || null,
83
+ operatorNote: auth.operatorNote || null,
84
+ pkceRequired: auth.pkceRequired === true,
85
+ audience: isPresent(auth.audience) ? auth.audience : null,
86
+ scopes: normalizeScopeList(auth.scopes || auth.allowedScopes)
87
+ };
88
+ if (recommended === TRUSTED_ISSUER) {
89
+ return {
90
+ ...base,
91
+ authorizationServerMetadataUrl: null,
92
+ clientRegistrationModes: []
93
+ };
94
+ }
95
+ return {
96
+ ...base,
97
+ authorizationServerMetadataUrl: auth.authorizationServerMetadataUrl || null,
98
+ clientRegistrationModes: Array.isArray(auth.clientRegistrationModes)
99
+ ? [...auth.clientRegistrationModes]
100
+ : []
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Resolve Runtime MCP Entra scopes — never default to openid/profile/offline_access.
106
+ * @param {Object} params
107
+ * @param {string[]} [params.explicitScopes]
108
+ * @param {Object|null} [params.packageContext]
109
+ * @param {Object|null} [params.entraProvider]
110
+ * @returns {string[]}
111
+ */
112
+ function resolveCopilotEntraScopes(params) {
113
+ const explicit = normalizeScopeList(params.explicitScopes);
114
+ if (explicit.length > 0 && !isOpenIdOnlyOrEmptyScopes(explicit)) {
115
+ return explicit;
116
+ }
117
+ const fromProvider = normalizeScopeList(params.entraProvider?.allowedScopes);
118
+ if (fromProvider.length > 0 && !isOpenIdOnlyOrEmptyScopes(fromProvider)) {
119
+ return fromProvider;
120
+ }
121
+ const auth = params.packageContext?.authentication || null;
122
+ const fromAuth = normalizeScopeList(auth?.scopes || auth?.allowedScopes);
123
+ if (fromAuth.length > 0 && !isOpenIdOnlyOrEmptyScopes(fromAuth)) {
124
+ return fromAuth;
125
+ }
126
+ const resource = typeof auth?.resource === 'string' ? auth.resource.trim() : '';
127
+ if (resource.startsWith('api://')) {
128
+ return [resource];
129
+ }
130
+ return [];
131
+ }
132
+
133
+ /**
134
+ * Package/Runtime readiness blocker (not Microsoft org-publish state).
135
+ * Having a Remote MCP URL is enough for packaging even when live remote status
136
+ * is temporarily unavailable — Microsoft publication is tracked separately.
137
+ * @param {Object} params
138
+ * @returns {string|null}
139
+ */
140
+ function firstCopilotEntraBlocker(params) {
141
+ const {
142
+ discoveryReady,
143
+ discoveryBlocker,
144
+ mcpServerUrl,
145
+ recommended,
146
+ referenceId,
147
+ issuer,
148
+ audience,
149
+ scopes
150
+ } = params;
151
+ const hasMcpUrl = typeof mcpServerUrl === 'string' && mcpServerUrl.trim();
152
+ if (!hasMcpUrl && !discoveryReady) {
153
+ return discoveryBlocker || 'remoteMcpUnavailable';
154
+ }
155
+ if (recommended !== TRUSTED_ISSUER) {
156
+ return recommended === CUSTOMER_OAUTH
157
+ ? 'customerOAuthNotAllowed'
158
+ : 'trustedIssuerRequired';
159
+ }
160
+ if (!referenceId) {
161
+ return 'oauthPluginVaultReferenceRequired';
162
+ }
163
+ if (!issuer) {
164
+ return 'entraIssuerRequired';
165
+ }
166
+ if (!isPresent(audience)) {
167
+ return 'runtimeMcpAudienceRequired';
168
+ }
169
+ if (isOpenIdOnlyOrEmptyScopes(scopes)) {
170
+ return 'runtimeMcpScopeRequired';
171
+ }
172
+ return null;
173
+ }
174
+
175
+ /**
176
+ * @param {Object|null} auth
177
+ * @param {Object|null} entraProvider
178
+ * @returns {{ audience: * , issuer: string|null }}
179
+ */
180
+ function resolveAudienceAndIssuer(auth, entraProvider) {
181
+ const audience = isPresent(entraProvider?.audience)
182
+ ? entraProvider.audience
183
+ : isPresent(auth?.audience)
184
+ ? auth.audience
185
+ : null;
186
+ const issuer =
187
+ (entraProvider && entraProvider.issuer) || (auth && auth.issuer) || null;
188
+ return { audience, issuer };
189
+ }
190
+
191
+ /**
192
+ * @param {Object} params
193
+ * @returns {{
194
+ * installReady: boolean,
195
+ * blocker: string|null,
196
+ * packageReady: boolean,
197
+ * packageBlocker: string|null,
198
+ * microsoftPublicationPending: boolean,
199
+ * packageAuthentication: Object|null
200
+ * }}
201
+ */
202
+ function evaluateCopilotEntraSsoReadiness(params) {
203
+ const packageContext = params.packageContext || null;
204
+ const auth = mapCopilotPackageAuthentication(packageContext?.authentication || null);
205
+ const recommended = resolveRecommendedCapability(auth);
206
+ const referenceId =
207
+ typeof params.oauthPluginVaultReferenceId === 'string'
208
+ ? params.oauthPluginVaultReferenceId.trim()
209
+ : '';
210
+ const scopes = normalizeScopeList(params.scopes);
211
+ const { audience, issuer } = resolveAudienceAndIssuer(auth, params.entraProvider || null);
212
+ const mcpServerUrl =
213
+ (typeof packageContext?.mcpServerUrl === 'string' && packageContext.mcpServerUrl.trim()) ||
214
+ (typeof params.mcpServerUrl === 'string' && params.mcpServerUrl.trim()) ||
215
+ null;
216
+
217
+ if (auth) {
218
+ auth.issuer = issuer;
219
+ auth.audience = audience;
220
+ auth.scopes = scopes;
221
+ }
222
+
223
+ const packageBlocker = firstCopilotEntraBlocker({
224
+ discoveryReady: packageContext?.installReady === true,
225
+ discoveryBlocker: packageContext?.blocker || null,
226
+ mcpServerUrl,
227
+ recommended,
228
+ referenceId,
229
+ issuer,
230
+ audience,
231
+ scopes
232
+ });
233
+ const packageReady = packageBlocker === null;
234
+ return {
235
+ installReady: packageReady,
236
+ blocker: packageBlocker,
237
+ packageReady,
238
+ packageBlocker,
239
+ microsoftPublicationPending: true,
240
+ packageAuthentication: auth
241
+ };
242
+ }
243
+
244
+ const {
245
+ formatOauthPluginVaultReferenceRequiredError
246
+ } = require('./microsoft-copilot-entra-sso-form');
247
+
248
+ /**
249
+ * @param {Object} [options]
250
+ * @throws {Error}
251
+ */
252
+ function assertOauthPluginVaultReferenceId(options = {}) {
253
+ const ref = options.oauthPluginVaultReferenceId;
254
+ if (typeof ref === 'string' && ref.trim()) {
255
+ return;
256
+ }
257
+ throw new Error(formatOauthPluginVaultReferenceRequiredError(options.formContext || options));
258
+ }
259
+
260
+ /**
261
+ * @param {Object} [runtimeValues]
262
+ * @throws {Error}
263
+ */
264
+ function assertCopilotPackageOauthPluginVaultReference(runtimeValues = {}) {
265
+ const ref = runtimeValues.oauthPluginVaultReferenceId;
266
+ if (typeof ref === 'string' && ref.trim()) {
267
+ return;
268
+ }
269
+ throw new Error(formatOauthPluginVaultReferenceRequiredError(runtimeValues));
270
+ }
271
+
272
+ module.exports = {
273
+ TRUSTED_ISSUER,
274
+ CUSTOMER_OAUTH,
275
+ skipsChannelOauthCredential,
276
+ normalizeScopeList,
277
+ isOpenIdOnlyOrEmptyScopes,
278
+ resolveRecommendedCapability,
279
+ mapCopilotPackageAuthentication,
280
+ resolveCopilotEntraScopes,
281
+ evaluateCopilotEntraSsoReadiness,
282
+ formatOauthPluginVaultReferenceRequiredError,
283
+ assertOauthPluginVaultReferenceId,
284
+ assertCopilotPackageOauthPluginVaultReference
285
+ };
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Valid Microsoft Teams / Copilot package icons (192×192 color, 32×32 outline).
3
+ *
4
+ * Defaults from templates/channels/brand (plan 167.0):
5
+ * - color-192.png ← light/192 (color app icon)
6
+ * - outline-32.png ← mono-white/32 (Teams outline)
7
+ * Variant packs: light/, dark/, mono-white/ at 32–512.
8
+ *
9
+ * @fileoverview Loads AI Fabrix brand PNGs from templates/channels/brand
10
+ * @author AI Fabrix Team
11
+ * @version 2.1.0
12
+ */
13
+
14
+ 'use strict';
15
+
16
+ const path = require('path');
17
+ const fs = require('../internal/fs-real-sync');
18
+ const { BUILDER_ROOT } = require('./channel-integration-template');
19
+
20
+ const BRAND_DIR = path.join(BUILDER_ROOT, 'templates', 'channels', 'brand');
21
+ const DEFAULT_COLOR_PATH = path.join(BRAND_DIR, 'color-192.png');
22
+ const DEFAULT_OUTLINE_PATH = path.join(BRAND_DIR, 'outline-32.png');
23
+
24
+ /**
25
+ * @param {string} filePath
26
+ * @returns {Buffer}
27
+ */
28
+ function readPngFile(filePath) {
29
+ if (!fs.existsSync(filePath)) {
30
+ throw new Error(`Brand icon not found: ${filePath}`);
31
+ }
32
+ return fs.readFileSync(filePath);
33
+ }
34
+
35
+ /**
36
+ * @param {{ colorPath?: string, outlinePath?: string }} [options]
37
+ * @returns {{ color: Buffer, outline: Buffer }}
38
+ */
39
+ function getMicrosoftCopilotIcons(options = {}) {
40
+ const colorPath =
41
+ typeof options.colorPath === 'string' && options.colorPath.trim()
42
+ ? options.colorPath.trim()
43
+ : DEFAULT_COLOR_PATH;
44
+ const outlinePath =
45
+ typeof options.outlinePath === 'string' && options.outlinePath.trim()
46
+ ? options.outlinePath.trim()
47
+ : DEFAULT_OUTLINE_PATH;
48
+ return {
49
+ color: readPngFile(colorPath),
50
+ outline: readPngFile(outlinePath)
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Read PNG IHDR width/height without external deps.
56
+ * @param {Buffer} buf
57
+ * @returns {{ width: number, height: number } | null}
58
+ */
59
+ function readPngDimensions(buf) {
60
+ if (!Buffer.isBuffer(buf) || buf.length < 24) return null;
61
+ if (buf.toString('hex', 0, 8) !== '89504e470d0a1a0a') return null;
62
+ return {
63
+ width: buf.readUInt32BE(16),
64
+ height: buf.readUInt32BE(20)
65
+ };
66
+ }
67
+
68
+ module.exports = {
69
+ getMicrosoftCopilotIcons,
70
+ readPngDimensions,
71
+ BRAND_DIR,
72
+ DEFAULT_COLOR_PATH,
73
+ DEFAULT_OUTLINE_PATH
74
+ };
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Microsoft Copilot / Teams package string limits.
3
+ *
4
+ * @fileoverview Truncate and normalize Copilot package display fields to schema limits
5
+ * @author AI Fabrix Team
6
+ * @version 1.1.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const TEAMS_NAME_SHORT_MAX = 30;
12
+ const TEAMS_NAME_FULL_MAX = 100;
13
+ const TEAMS_DESCRIPTION_SHORT_MAX = 80;
14
+ const TEAMS_DESCRIPTION_FULL_MAX = 4000;
15
+ const PLUGIN_NAME_FOR_HUMAN_MAX = 20;
16
+ const PLUGIN_DESCRIPTION_FOR_HUMAN_MAX = 100;
17
+
18
+ const FI_DEFAULT_SHORT =
19
+ 'Yrityksen tekoälyavustaja työntekijöille AI Fabrix Runtime -ympäristössä.';
20
+ const FI_DEFAULT_FULL =
21
+ 'Yrityksen tekoälyavustaja työntekijöille. Hyväksytyt avustajat, yritystieto, työnkulut ja hallitut toiminnot AI Fabrixin kautta.';
22
+
23
+ /**
24
+ * @param {string} text
25
+ * @param {number} max
26
+ * @returns {string}
27
+ */
28
+ function truncateForSchema(text, max) {
29
+ const value = String(text ?? '').trim();
30
+ if (!value) return '';
31
+ if (value.length <= max) return value;
32
+ if (max <= 1) return value.slice(0, max);
33
+ return `${value.slice(0, max - 1)}…`;
34
+ }
35
+
36
+ /**
37
+ * Plugin namespace: lowercase alphanumeric/hyphen only (plugin schema ^[A-Za-z0-9-]+$).
38
+ * @param {string} baseName
39
+ * @returns {string}
40
+ */
41
+ function buildPluginNamespace(baseName) {
42
+ const raw = String(baseName ?? 'aifabrix')
43
+ .trim()
44
+ .toLowerCase()
45
+ .replace(/[^a-z0-9-]+/g, '-')
46
+ .replace(/-+/g, '-')
47
+ .replace(/^-|-$/g, '');
48
+ const ns = raw || 'aifabrix';
49
+ return truncateForSchema(ns, 32).replace(/[^a-z0-9-]/g, '') || 'aifabrix';
50
+ }
51
+
52
+ /**
53
+ * @param {Object} runtimeValues
54
+ * @returns {Object}
55
+ */
56
+ function applyMicrosoftCopilotDisplayLimits(runtimeValues) {
57
+ const appDescription = String(runtimeValues.appDescription || '').trim();
58
+ const baseName = String(runtimeValues.baseName || runtimeValues.appName || 'Assistant').trim();
59
+ const appName = String(runtimeValues.appName || baseName).trim();
60
+ // Teams short + full both use the environment-scoped display name (e.g. "Onni (dev)").
61
+ runtimeValues.shortName = truncateForSchema(appName, TEAMS_NAME_SHORT_MAX);
62
+ runtimeValues.fullName = truncateForSchema(appName, TEAMS_NAME_FULL_MAX);
63
+ runtimeValues.shortDescription = truncateForSchema(
64
+ appDescription || 'Enterprise AI assistant via AI Fabrix Runtime.',
65
+ TEAMS_DESCRIPTION_SHORT_MAX
66
+ );
67
+ runtimeValues.fullDescription = truncateForSchema(
68
+ appDescription || runtimeValues.shortDescription,
69
+ TEAMS_DESCRIPTION_FULL_MAX
70
+ );
71
+ // Plugin name_for_human matches the env-scoped display name (e.g. "Onni (dev)").
72
+ runtimeValues.pluginNameForHuman = truncateForSchema(appName, PLUGIN_NAME_FOR_HUMAN_MAX);
73
+ runtimeValues.pluginDescriptionForHuman = truncateForSchema(
74
+ appDescription || runtimeValues.shortDescription,
75
+ PLUGIN_DESCRIPTION_FOR_HUMAN_MAX
76
+ );
77
+ runtimeValues.pluginNamespace = buildPluginNamespace(baseName);
78
+ const fiShort =
79
+ typeof runtimeValues.fiShortDescription === 'string' && runtimeValues.fiShortDescription.trim()
80
+ ? runtimeValues.fiShortDescription.trim()
81
+ : FI_DEFAULT_SHORT;
82
+ const fiFull =
83
+ typeof runtimeValues.fiFullDescription === 'string' && runtimeValues.fiFullDescription.trim()
84
+ ? runtimeValues.fiFullDescription.trim()
85
+ : FI_DEFAULT_FULL;
86
+ runtimeValues.fiShortDescription = truncateForSchema(fiShort, TEAMS_DESCRIPTION_SHORT_MAX);
87
+ runtimeValues.fiFullDescription = truncateForSchema(fiFull, TEAMS_DESCRIPTION_FULL_MAX);
88
+ return runtimeValues;
89
+ }
90
+
91
+ module.exports = {
92
+ TEAMS_NAME_SHORT_MAX,
93
+ TEAMS_NAME_FULL_MAX,
94
+ TEAMS_DESCRIPTION_SHORT_MAX,
95
+ TEAMS_DESCRIPTION_FULL_MAX,
96
+ PLUGIN_NAME_FOR_HUMAN_MAX,
97
+ PLUGIN_DESCRIPTION_FOR_HUMAN_MAX,
98
+ FI_DEFAULT_SHORT,
99
+ FI_DEFAULT_FULL,
100
+ truncateForSchema,
101
+ buildPluginNamespace,
102
+ applyMicrosoftCopilotDisplayLimits
103
+ };