@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.
- package/lib/api/datasources-extended.api.js +8 -8
- package/lib/api/keycloak.api.js +92 -0
- package/lib/api/token-providers.api.js +77 -0
- package/lib/api/types/keycloak.types.js +23 -0
- package/lib/api/types/token-providers.types.js +65 -0
- package/lib/app/deploy-config.js +3 -2
- package/lib/app/down.js +16 -1
- package/lib/channels/add-channel.js +134 -57
- package/lib/channels/approval-guides/chatgpt.js +1 -1
- package/lib/channels/approval-guides/index.js +3 -0
- package/lib/channels/approval-guides/microsoft-copilot.js +27 -14
- package/lib/channels/channel-add-ensure.js +128 -0
- package/lib/channels/channel-add-existing.js +72 -0
- package/lib/channels/channel-artifact-generator.js +11 -1
- package/lib/channels/channel-branding.js +94 -0
- package/lib/channels/channel-credential.js +8 -0
- package/lib/channels/channel-discovery.js +330 -0
- package/lib/channels/channel-external-system.js +1 -1
- package/lib/channels/channel-runtime-resolver.js +107 -14
- package/lib/channels/channel-status-display.js +36 -3
- package/lib/channels/microsoft-copilot-channel-add.js +138 -0
- package/lib/channels/microsoft-copilot-entra-sso-form.js +251 -0
- package/lib/channels/microsoft-copilot-entra-sso.js +285 -0
- package/lib/channels/microsoft-copilot-icons.js +74 -0
- package/lib/channels/microsoft-copilot-limits.js +103 -0
- package/lib/channels/microsoft-copilot-package.js +178 -52
- package/lib/channels/microsoft-copilot-validate.js +227 -0
- package/lib/cli/attach-default-help-examples.js +102 -0
- package/lib/cli/index.js +4 -0
- package/lib/cli/infra-guided.js +13 -6
- package/lib/cli/setup-credential-deployment.js +7 -5
- package/lib/cli/setup-infra.js +31 -4
- package/lib/cli/setup-utility-validate.js +1 -0
- package/lib/commands/channel-add-setup.js +28 -9
- package/lib/commands/channel.js +14 -10
- package/lib/commands/credential-push.js +37 -5
- package/lib/commands/integration-definition.js +158 -0
- package/lib/commands/package-score.js +72 -0
- package/lib/commands/repair-auth-apply.js +19 -5
- package/lib/commands/repair-datasource-sync-operations.js +4 -1
- package/lib/commands/role-assistant-test-output.js +200 -0
- package/lib/commands/role-assistant.js +163 -166
- package/lib/datasource/capability/capability-resolve.js +15 -5
- package/lib/datasource/capability/copy-operations.js +14 -4
- package/lib/deployment/deployer-poll-run.js +2 -2
- package/lib/deployment/deployer.js +1 -1
- package/lib/deployment/environment.js +2 -2
- package/lib/external-system/deploy.js +11 -21
- package/lib/infrastructure/index.js +19 -4
- package/lib/integration-definition/apply.js +476 -0
- package/lib/integration-definition/canonical-json.js +51 -0
- package/lib/integration-definition/emit-suite-bid.js +94 -0
- package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
- package/lib/integration-definition/enrich-openapi-operations.js +433 -0
- package/lib/integration-definition/from-discovery.js +339 -0
- package/lib/integration-definition/hash.js +37 -0
- package/lib/integration-definition/index.js +37 -0
- package/lib/integration-definition/resource-type-catalog.js +59 -0
- package/lib/integration-definition/system-kind-enum.js +40 -0
- package/lib/integration-definition/validate.js +148 -0
- package/lib/lab/package-parity-score.js +417 -0
- package/lib/programmatic/builder-api-request.js +5 -1
- package/lib/programmatic/builder-help-governance.js +3 -2
- package/lib/programmatic/capability-registry.js +20 -0
- package/lib/programmatic/help-content/channel-onboarding.md +35 -0
- package/lib/programmatic/help-content/cip-overview.md +27 -0
- package/lib/programmatic/help-content/connected-system-ui.md +70 -0
- package/lib/programmatic/help-content/dimensions-guide.md +23 -0
- package/lib/programmatic/help-content/enterprise-sync-fabrix.md +123 -0
- package/lib/programmatic/help-content/overview.md +25 -0
- package/lib/programmatic/help-content/subscription-guide.md +27 -0
- package/lib/programmatic/help-content/workflow.md +50 -0
- package/lib/programmatic/openapi-component-schemas.js +7 -1
- package/lib/programmatic/openapi-descriptions.js +46 -0
- package/lib/programmatic/openapi-envelope-examples.js +280 -0
- package/lib/programmatic/openapi-operation-bindings.js +50 -0
- package/lib/programmatic/openapi-request-schemas.js +40 -0
- package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
- package/lib/programmatic/openapi-result-schemas.js +76 -99
- package/lib/programmatic/openapi-schema-descriptions-core.js +25 -4
- package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-operations.js +43 -0
- package/lib/programmatic/openapi-schema-helpers.js +9 -9
- package/lib/programmatic/openapi-spec.js +11 -1
- package/lib/programmatic/orchestration-openapi-meta.js +10 -0
- package/lib/programmatic/persist-supplied-openapi.js +180 -0
- package/lib/programmatic/route-handler-map.js +35 -1
- package/lib/programmatic/run-channel-package.js +236 -0
- package/lib/programmatic/run-integration-definition.js +77 -0
- package/lib/role-assistant/evidence-sync.js +55 -7
- package/lib/role-assistant/knowledge-sync-logging.js +58 -0
- package/lib/role-assistant/knowledge-sync.js +88 -93
- package/lib/role-assistant/test-cases-bid.js +155 -0
- package/lib/role-assistant/test-cases-evaluate.js +9 -1
- package/lib/role-assistant/test-cases-honesty.js +24 -10
- package/lib/role-assistant/test-cases.js +28 -0
- package/lib/role-assistant/test-runner-attachments.js +118 -0
- package/lib/role-assistant/test-runner-bid-apply.js +201 -0
- package/lib/role-assistant/test-runner-bid-document.js +278 -0
- package/lib/role-assistant/test-runner-start-payload.js +96 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +249 -20
- package/lib/role-assistant/test-runner-workhub-helpers.js +100 -0
- package/lib/role-assistant/test-runner-workhub-soft.js +86 -0
- package/lib/role-assistant/test-runner-workhub.js +152 -107
- package/lib/role-assistant/test-runner.js +5 -63
- package/lib/role-assistant/test-suite.js +112 -0
- package/lib/schema/application-schema.json +56 -6
- package/lib/schema/external-datasource.schema.json +103 -17
- package/lib/schema/external-system.schema.json +54 -3
- package/lib/schema/integration-definition.schema.json +195 -0
- package/lib/utils/authentication-shape.js +6 -0
- package/lib/utils/compose-generator.js +10 -1
- package/lib/utils/compose-traefik-extra-routes.js +107 -0
- package/lib/utils/docker-build.js +2 -2
- package/lib/utils/help-builder.js +3 -1
- package/lib/utils/promote-dev-docker-image.js +102 -18
- package/lib/utils/transient-developer-id.js +28 -0
- package/package.json +14 -16
- package/templates/README.md +12 -1
- package/templates/applications/dataplane/application.yaml +10 -2
- package/templates/applications/dataplane/env.template +7 -6
- package/templates/applications/miso-controller/application.yaml +23 -8
- package/templates/applications/miso-controller/env.template +8 -2
- package/templates/applications/miso-controller/rbac.yaml +1 -0
- package/templates/channels/README.md +11 -1
- package/templates/channels/brand/color-192.png +0 -0
- package/templates/channels/brand/dark/128.png +0 -0
- package/templates/channels/brand/dark/192.png +0 -0
- package/templates/channels/brand/dark/256.png +0 -0
- package/templates/channels/brand/dark/32.png +0 -0
- package/templates/channels/brand/dark/512.png +0 -0
- package/templates/channels/brand/dark/64.png +0 -0
- package/templates/channels/brand/light/128.png +0 -0
- package/templates/channels/brand/light/192.png +0 -0
- package/templates/channels/brand/light/256.png +0 -0
- package/templates/channels/brand/light/32.png +0 -0
- package/templates/channels/brand/light/512.png +0 -0
- package/templates/channels/brand/light/64.png +0 -0
- package/templates/channels/brand/mono-white/128.png +0 -0
- package/templates/channels/brand/mono-white/192.png +0 -0
- package/templates/channels/brand/mono-white/256.png +0 -0
- package/templates/channels/brand/mono-white/32.png +0 -0
- package/templates/channels/brand/mono-white/512.png +0 -0
- package/templates/channels/brand/mono-white/64.png +0 -0
- package/templates/channels/brand/outline-32.png +0 -0
- package/templates/channels/chatgpt/install.template.md.hbs +6 -4
- package/templates/channels/chatgpt/integration/README.md +4 -5
- package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +14 -16
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
- package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +30 -0
- package/templates/channels/microsoft-copilot/fi-fi.template.json.hbs +7 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +93 -22
- package/templates/channels/microsoft-copilot/integration/README.md +2 -3
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -19
- package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +15 -0
- package/templates/channels/slack/integration/README.md +2 -4
- package/templates/channels/teams-bot/install.template.md.hbs +4 -0
- package/templates/channels/teams-bot/integration/README.md +3 -5
- package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
- package/templates/python/docker-compose.hbs +36 -0
- package/templates/typescript/docker-compose.hbs +36 -0
- package/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +0 -374
- package/.cursor/process/README.md +0 -9
- package/.cursor/rules/anchor-docs.mdc +0 -15
- package/.cursor/rules/assistant-driven-integration-build.mdc +0 -40
- package/.cursor/rules/cli-layout.mdc +0 -81
- package/.cursor/rules/cli-product-neutral.mdc +0 -43
- package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +0 -49
- package/.cursor/rules/docs-rules.mdc +0 -32
- package/.cursor/rules/plan-rules/application-lifecycle.mdc +0 -30
- package/.cursor/rules/plan-rules/plan-frontmatter.mdc +0 -35
- package/.cursor/rules/plan-rules/plan-lifecycle.mdc +0 -31
- package/.cursor/rules/plan-rules/plan-validation-gate.mdc +0 -34
- package/.cursor/rules/project-rules.mdc +0 -913
- package/.cursor/skills/README.md +0 -15
- package/.dockerignore +0 -52
- package/.eslintignore +0 -22
- package/.markdownlint.json +0 -14
- package/anchor-docs/README.md +0 -10
- package/anchor-docs/_TEMPLATE +0 -24
- package/babel.config.js +0 -6
- package/bin/builder-api-cleanup.js +0 -26
- package/bin/builder-api-nest.js +0 -10
- package/bin/builder-api-spike.js +0 -19
- package/enterprise-knowledge/README.md +0 -50
- package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +0 -22
- package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +0 -15
- package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +0 -69
- package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +0 -52
- package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +0 -29
- package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +0 -2
- package/integration/README.md +0 -81
- package/integration/github/README.md +0 -53
- package/integration/github/application.json +0 -21
- package/integration/github/deploy.js +0 -72
- package/integration/github/env.template +0 -11
- package/integration/github/github-datasource-issue-comments.json +0 -239
- package/integration/github/github-datasource-issues.json +0 -399
- package/integration/github/github-datasource-repository.json +0 -354
- package/integration/github/github-deploy.json +0 -1202
- package/integration/github/github-system.json +0 -82
- package/integration/hubspot-test/README.md +0 -159
- package/integration/hubspot-test/application.json +0 -54
- package/integration/hubspot-test/companies.json +0 -2048
- package/integration/hubspot-test/create-hubspot.js +0 -523
- package/integration/hubspot-test/env.template +0 -4
- package/integration/hubspot-test/hubspot-test-datasource-company.json +0 -138
- package/integration/hubspot-test/hubspot-test-datasource-contact.json +0 -146
- package/integration/hubspot-test/hubspot-test-datasource-deal.json +0 -146
- package/integration/hubspot-test/hubspot-test-datasource-users.json +0 -76
- package/integration/hubspot-test/hubspot-test-deploy.json +0 -2160
- package/integration/hubspot-test/hubspot-test-system.json +0 -74
- package/integration/hubspot-test/rbac.json +0 -166
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +0 -20
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +0 -9
- package/integration/hubspot-test/test-artifacts/wizard-invalid-add-datasource.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-invalid-app-name.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-create.yaml +0 -7
- package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-select.yaml +0 -7
- package/integration/hubspot-test/test-artifacts/wizard-invalid-known-platform.yaml +0 -4
- package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-app.yaml +0 -4
- package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
- package/integration/hubspot-test/test-artifacts/wizard-invalid-mode.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-file.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-url.yaml +0 -4
- package/integration/hubspot-test/test-artifacts/wizard-invalid-source.yaml +0 -4
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-array-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-test.yaml +0 -5
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +0 -5
- package/integration/hubspot-test/test-dataplane-down-helpers.js +0 -249
- package/integration/hubspot-test/test-dataplane-down-tests.js +0 -386
- package/integration/hubspot-test/test-dataplane-down.js +0 -209
- package/integration/hubspot-test/test.js +0 -1588
- package/integration/hubspot-test/wizard-hubspot-e2e.yaml +0 -16
- package/integration/hubspot-test/wizard-hubspot-platform.yaml +0 -8
- package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +0 -23
- package/integration/roundtrip-test-local/README.md +0 -143
- package/integration/roundtrip-test-local/application.yaml +0 -13
- package/integration/roundtrip-test-local/env.template +0 -15
- package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +0 -14
- package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +0 -61
- package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +0 -25
- package/integration/roundtrip-test-local2/README.md +0 -143
- package/integration/roundtrip-test-local2/application.yaml +0 -13
- package/integration/roundtrip-test-local2/env.template +0 -15
- package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +0 -31
- package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +0 -86
- package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +0 -25
- package/integration/test/wizard.yaml +0 -8
- package/jest.config.coverage.js +0 -37
- package/jest.config.default.js +0 -11
- package/jest.config.integration.fixtures.js +0 -22
- package/jest.config.integration.js +0 -33
- package/jest.config.isolated.js +0 -11
- package/jest.config.manual.js +0 -30
- package/jest.global-live-fabrix-hooks.js +0 -36
- package/jest.isolated-projects.js +0 -260
- package/jest.projects.js +0 -76
- package/packages/builder-api/README.md +0 -120
- package/packages/builder-api/package.json +0 -30
- package/packages/builder-api/src/app.module.ts +0 -7
- package/packages/builder-api/src/auth/auth.module.ts +0 -5
- package/packages/builder-api/src/external-system/external-system.module.ts +0 -5
- package/packages/builder-api/src/main.ts +0 -16
- package/packages/builder-api/tsconfig.json +0 -15
- package/scripts/check-builder-api-architecture-grep.js +0 -79
- package/scripts/check-datasource-test-run-schema-sync.js +0 -34
- package/scripts/ci-fix.sh +0 -19
- package/scripts/ci-simulate.sh +0 -19
- package/scripts/diagnose-cli.js +0 -150
- package/scripts/docker_image_content_gate.js +0 -47
- package/scripts/docker_image_content_gate.sh +0 -5
- package/scripts/export-builder-api-evidence.js +0 -262
- package/scripts/install-local.js +0 -490
- package/scripts/lib/builder-api-docker-staging-copy.js +0 -60
- package/scripts/lib/builder-api-docker-staging-core.js +0 -183
- package/scripts/lib/docker-image-content-gate-core.js +0 -166
- package/scripts/lib/plan-actor.js +0 -109
- package/scripts/lib/plan-config.js +0 -20
- package/scripts/lib/plan-gates.js +0 -154
- package/scripts/lib/plan-meta.js +0 -104
- package/scripts/lib/plan-parse.js +0 -162
- package/scripts/lib/plan-prevalidate-gates.js +0 -337
- package/scripts/lib/plan-run-core.js +0 -125
- package/scripts/lib/plan-run-report.js +0 -119
- package/scripts/plan-approve.js +0 -24
- package/scripts/plan-archive.js +0 -49
- package/scripts/plan-run.js +0 -13
- package/scripts/plan-set-actor.js +0 -21
- package/scripts/plan-validate.js +0 -80
- package/scripts/pnpm/af.mjs +0 -110
- package/scripts/pnpm/check-quiet.mjs +0 -32
- package/scripts/pnpm/dev-install.mjs +0 -29
- package/scripts/pnpm/dev-reload.mjs +0 -18
- package/scripts/pnpm/help.mjs +0 -55
- package/scripts/pnpm/lib/dev-reload-core.mjs +0 -189
- package/scripts/pnpm/lib/dev-reload-log-capture.mjs +0 -67
- package/scripts/pnpm/lib/dev-reload-log-utils.mjs +0 -32
- package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +0 -124
- package/scripts/pnpm/lib/resolve-app-url.mjs +0 -183
- package/scripts/pnpm/lib/spinner.mjs +0 -42
- package/scripts/pnpm-global-remove.js +0 -48
- package/scripts/stage_builder_api_for_docker.js +0 -65
- package/scripts/sync-builder-api-template.js +0 -44
- 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
|
+
};
|