@aifabrix/builder 2.52.0 → 2.52.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/jest.isolated-projects.js +25 -2
- package/jest.projects.js +4 -1
- 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/down.js +16 -1
- package/lib/channels/add-channel.js +126 -19
- package/lib/channels/approval-guides/chatgpt.js +1 -1
- package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
- package/lib/channels/channel-add-ensure.js +128 -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 +103 -14
- package/lib/channels/channel-status-display.js +7 -1
- package/lib/channels/microsoft-copilot-channel-add.js +138 -0
- package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
- package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
- package/lib/channels/microsoft-copilot-icons.js +74 -0
- package/lib/channels/microsoft-copilot-limits.js +84 -0
- package/lib/channels/microsoft-copilot-package.js +130 -49
- package/lib/channels/microsoft-copilot-validate.js +197 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/infra-guided.js +13 -6
- package/lib/cli/setup-infra.js +31 -4
- package/lib/commands/channel-add-setup.js +28 -9
- package/lib/commands/channel.js +14 -10
- package/lib/commands/integration-definition.js +158 -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 +125 -166
- package/lib/datasource/capability/capability-resolve.js +15 -5
- package/lib/datasource/capability/copy-operations.js +14 -4
- package/lib/infrastructure/index.js +19 -4
- package/lib/integration-definition/apply.js +478 -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 +434 -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/openapi-enrichment/hubspot-crm-companies.json +205 -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/programmatic/builder-api-request.js +5 -1
- package/lib/programmatic/builder-help-governance.js +3 -2
- package/lib/programmatic/capability-registry.js +19 -0
- package/lib/programmatic/openapi-component-schemas.js +6 -1
- package/lib/programmatic/openapi-descriptions.js +40 -0
- package/lib/programmatic/openapi-envelope-examples.js +269 -0
- package/lib/programmatic/openapi-operation-bindings.js +45 -0
- package/lib/programmatic/openapi-request-schemas.js +14 -0
- package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
- package/lib/programmatic/openapi-result-schemas.js +46 -99
- package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
- package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -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/route-handler-map.js +29 -1
- package/lib/programmatic/run-channel-package.js +236 -0
- package/lib/programmatic/run-integration-definition.js +69 -0
- 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-bid-apply.js +122 -0
- package/lib/role-assistant/test-runner-bid-document.js +278 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +245 -20
- package/lib/role-assistant/test-runner-workhub.js +100 -18
- package/lib/role-assistant/test-suite.js +119 -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/docker-build.js +3 -3
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/promote-dev-docker-image.js +97 -18
- package/lib/utils/transient-developer-id.js +28 -0
- package/package.json +1 -1
- package/scripts/pnpm/af.mjs +24 -9
- package/scripts/pnpm/help.mjs +1 -1
- package/templates/applications/dataplane/application.yaml +3 -2
- package/templates/applications/dataplane/env.template +7 -6
- package/templates/applications/miso-controller/application.yaml +23 -9
- package/templates/applications/miso-controller/env.template +8 -2
- 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 +10 -16
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
- package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
- 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 +7 -7
- package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -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/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
|
@@ -19,6 +19,12 @@ const {
|
|
|
19
19
|
COPILOT_RUNTIME_TRANSPORT,
|
|
20
20
|
COPILOT_RUNTIME_AUTH_MODE
|
|
21
21
|
} = require('./chatgpt-runtime-contract');
|
|
22
|
+
const { applyMicrosoftCopilotDisplayLimits } = require('./microsoft-copilot-limits');
|
|
23
|
+
const {
|
|
24
|
+
resolveCopilotEntraScopes,
|
|
25
|
+
evaluateCopilotEntraSsoReadiness
|
|
26
|
+
} = require('./microsoft-copilot-entra-sso');
|
|
27
|
+
const { applyCopilotEntraSsoFormFields } = require('./microsoft-copilot-entra-sso-form');
|
|
22
28
|
|
|
23
29
|
/** Default Slack slash prefix — matches dataplane `templates/slack/command_catalog.yaml`. */
|
|
24
30
|
const DEFAULT_SLACK_COMMAND_PREFIX = 'af';
|
|
@@ -152,15 +158,62 @@ function buildDeterministicGuid(seed) {
|
|
|
152
158
|
* @param {Object} resolved
|
|
153
159
|
* @param {Object} params
|
|
154
160
|
*/
|
|
155
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Apply discovery-driven package context (plan 167.0).
|
|
163
|
+
* @param {Object} resolved
|
|
164
|
+
* @param {Object} [packageContext]
|
|
165
|
+
*/
|
|
166
|
+
function applyDiscoveryPackageContext(resolved, packageContext) {
|
|
167
|
+
if (!packageContext || typeof packageContext !== 'object') {
|
|
168
|
+
resolved.installReady = false;
|
|
169
|
+
resolved.discoveryBlocker = resolved.discoveryBlocker || 'discoveryUnavailable';
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (Array.isArray(packageContext.governedToolNames) && packageContext.governedToolNames.length > 0) {
|
|
173
|
+
resolved.layerBToolNames = [...packageContext.governedToolNames];
|
|
174
|
+
}
|
|
175
|
+
if (packageContext.mcpServerUrl) {
|
|
176
|
+
resolved.mcpUrl = packageContext.mcpServerUrl;
|
|
177
|
+
}
|
|
178
|
+
if (packageContext.displayName && !resolved.brandingNameOverride) {
|
|
179
|
+
// Keep CLI --name as appName; discovery fills defaults only when requested upstream.
|
|
180
|
+
}
|
|
181
|
+
if (packageContext.description && !resolved.appDescription) {
|
|
182
|
+
resolved.appDescription = packageContext.description;
|
|
183
|
+
}
|
|
184
|
+
resolved.installReady = packageContext.installReady === true;
|
|
185
|
+
resolved.discoveryBlocker = packageContext.blocker || null;
|
|
186
|
+
resolved.discoveryAuthentication = packageContext.authentication || null;
|
|
187
|
+
resolved.discoveryAuthenticationCapabilities = packageContext.authenticationCapabilities || [];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @param {Object} resolved
|
|
192
|
+
* @returns {void}
|
|
193
|
+
*/
|
|
194
|
+
function applyCopilotRuntimeDomain(resolved) {
|
|
195
|
+
resolved.runtimeDomain = '';
|
|
196
|
+
try {
|
|
197
|
+
resolved.runtimeDomain = new URL(resolved.mcpUrl).hostname;
|
|
198
|
+
} catch {
|
|
199
|
+
try {
|
|
200
|
+
resolved.runtimeDomain = new URL(resolved.dataplaneBaseUrl).hostname;
|
|
201
|
+
} catch {
|
|
202
|
+
resolved.runtimeDomain = '';
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @param {Object} resolved
|
|
209
|
+
* @param {Object} params
|
|
210
|
+
* @returns {void}
|
|
211
|
+
*/
|
|
212
|
+
function applyCopilotPackageIdentity(resolved, params) {
|
|
156
213
|
resolved.runtimeSystemKey = COPILOT_RUNTIME_SYSTEM_KEY;
|
|
157
214
|
resolved.runtimeTransport = COPILOT_RUNTIME_TRANSPORT;
|
|
158
215
|
resolved.runtimeAuthMode = COPILOT_RUNTIME_AUTH_MODE;
|
|
159
216
|
resolved.layerBToolNames = Array.isArray(params.layerBToolNames) ? [...params.layerBToolNames] : [];
|
|
160
|
-
resolved.scopes = Array.isArray(params.scopes)
|
|
161
|
-
? [...params.scopes]
|
|
162
|
-
: ['openid', 'profile', 'offline_access'];
|
|
163
|
-
resolved.scopesJson = JSON.stringify(resolved.scopes);
|
|
164
217
|
resolved.packageVersion = typeof params.packageVersion === 'string' ? params.packageVersion : '1.0.0';
|
|
165
218
|
resolved.packageAppId = buildDeterministicGuid(
|
|
166
219
|
`${resolved.channelTarget}:${resolved.baseName}:${resolved.environment}:app`
|
|
@@ -168,16 +221,45 @@ function applyMicrosoftCopilotRuntimeValues(resolved, params) {
|
|
|
168
221
|
resolved.pluginId = buildDeterministicGuid(
|
|
169
222
|
`${resolved.channelTarget}:${resolved.baseName}:${resolved.environment}:plugin`
|
|
170
223
|
);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
224
|
+
const referenceId =
|
|
225
|
+
typeof params.oauthPluginVaultReferenceId === 'string' &&
|
|
226
|
+
params.oauthPluginVaultReferenceId.trim()
|
|
227
|
+
? params.oauthPluginVaultReferenceId.trim()
|
|
228
|
+
: '';
|
|
229
|
+
resolved.oauthPluginVaultReferenceId = referenceId || null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @param {Object} resolved
|
|
234
|
+
* @param {Object} params
|
|
235
|
+
* @returns {void}
|
|
236
|
+
*/
|
|
237
|
+
function applyMicrosoftCopilotRuntimeValues(resolved, params) {
|
|
238
|
+
applyCopilotPackageIdentity(resolved, params);
|
|
239
|
+
applyDiscoveryPackageContext(resolved, params.packageContext);
|
|
240
|
+
resolved.scopes = resolveCopilotEntraScopes({
|
|
241
|
+
explicitScopes: params.scopes,
|
|
242
|
+
packageContext: params.packageContext,
|
|
243
|
+
entraProvider: params.entraProvider
|
|
244
|
+
});
|
|
245
|
+
resolved.scopesJson = JSON.stringify(resolved.scopes);
|
|
246
|
+
const readiness = evaluateCopilotEntraSsoReadiness({
|
|
247
|
+
packageContext: params.packageContext,
|
|
248
|
+
oauthPluginVaultReferenceId: resolved.oauthPluginVaultReferenceId,
|
|
249
|
+
scopes: resolved.scopes,
|
|
250
|
+
entraProvider: params.entraProvider || null
|
|
251
|
+
});
|
|
252
|
+
resolved.installReady = readiness.installReady;
|
|
253
|
+
resolved.discoveryBlocker = readiness.blocker;
|
|
254
|
+
resolved.discoveryAuthentication = readiness.packageAuthentication;
|
|
255
|
+
if (params.entraProvider?.issuer) {
|
|
256
|
+
resolved.entraIssuer = params.entraProvider.issuer;
|
|
180
257
|
}
|
|
258
|
+
if (params.iconColorPath) resolved.iconColorPath = params.iconColorPath;
|
|
259
|
+
if (params.iconOutlinePath) resolved.iconOutlinePath = params.iconOutlinePath;
|
|
260
|
+
applyCopilotRuntimeDomain(resolved);
|
|
261
|
+
applyCopilotEntraSsoFormFields(resolved, params);
|
|
262
|
+
applyMicrosoftCopilotDisplayLimits(resolved);
|
|
181
263
|
}
|
|
182
264
|
|
|
183
265
|
/**
|
|
@@ -254,6 +336,7 @@ function resolveChannelRuntimeValues(params) {
|
|
|
254
336
|
|
|
255
337
|
if (params.channelTarget === 'slack') {
|
|
256
338
|
applySlackRuntimeValues(resolved, params);
|
|
339
|
+
applyDiscoveryPackageContext(resolved, params.packageContext);
|
|
257
340
|
}
|
|
258
341
|
|
|
259
342
|
if (params.channelTarget === 'microsoft-copilot') {
|
|
@@ -262,6 +345,11 @@ function resolveChannelRuntimeValues(params) {
|
|
|
262
345
|
|
|
263
346
|
if (params.channelTarget === 'chatgpt') {
|
|
264
347
|
applyChatGptRuntimeValues(resolved, params);
|
|
348
|
+
applyDiscoveryPackageContext(resolved, params.packageContext);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (params.channelTarget === 'teams-bot') {
|
|
352
|
+
applyDiscoveryPackageContext(resolved, params.packageContext);
|
|
265
353
|
}
|
|
266
354
|
|
|
267
355
|
return resolved;
|
|
@@ -277,6 +365,7 @@ module.exports = {
|
|
|
277
365
|
applySlackRuntimeValues,
|
|
278
366
|
applyMicrosoftCopilotRuntimeValues,
|
|
279
367
|
applyChatGptRuntimeValues,
|
|
368
|
+
applyDiscoveryPackageContext,
|
|
280
369
|
resolveSlackOAuthManifestFields,
|
|
281
370
|
resolveChannelRuntimeValues,
|
|
282
371
|
DEFAULT_SLACK_COMMAND_PREFIX
|
|
@@ -125,7 +125,13 @@ function displayChannelCoreFields(result) {
|
|
|
125
125
|
logger.log(metadata(`Artifact: ${result.artifactPath}`));
|
|
126
126
|
}
|
|
127
127
|
if (result.installGuidePath) {
|
|
128
|
-
logger.log(metadata(`Install guide: ${result.installGuidePath}`));
|
|
128
|
+
logger.log(metadata(`Install guide (README): ${result.installGuidePath}`));
|
|
129
|
+
}
|
|
130
|
+
if (result.zipPath) {
|
|
131
|
+
logger.log(metadata(`Package ZIP: ${result.zipPath}`));
|
|
132
|
+
}
|
|
133
|
+
if (result.installReady === false && result.blocker) {
|
|
134
|
+
logger.log(metadata(`Install ready: no (${result.blocker})`));
|
|
129
135
|
}
|
|
130
136
|
}
|
|
131
137
|
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* microsoft-copilot channel-add helpers (Entra SSO form context for operator errors).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Keep add-channel.js under size limits
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { resolveChannelAuthContext } = require('./channel-auth-context');
|
|
12
|
+
const {
|
|
13
|
+
normalizeBaseChannelName,
|
|
14
|
+
resolveChannelDisplayName
|
|
15
|
+
} = require('./channel-display-name');
|
|
16
|
+
const { getDefaultChannelDescription } = require('./channel-targets');
|
|
17
|
+
const { resolveAppDescription } = require('./channel-description');
|
|
18
|
+
const { loadChannelPackageContext } = require('./channel-discovery');
|
|
19
|
+
const { getInboundMicrosoftEntraIdProvider } = require('../api/token-providers.api');
|
|
20
|
+
const { getKeycloakEntraIdentityProvider } = require('../api/keycloak.api');
|
|
21
|
+
const { resolveChannelRuntimeValues } = require('./channel-runtime-resolver');
|
|
22
|
+
const { assertOauthPluginVaultReferenceId } = require('./microsoft-copilot-entra-sso');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {Object} options
|
|
26
|
+
* @returns {boolean}
|
|
27
|
+
*/
|
|
28
|
+
function hasOauthPluginVaultReference(options) {
|
|
29
|
+
return (
|
|
30
|
+
typeof options.oauthPluginVaultReferenceId === 'string' &&
|
|
31
|
+
Boolean(options.oauthPluginVaultReferenceId.trim())
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Load Trust source + Keycloak Entra IdP for Copilot packaging (null on miss/error).
|
|
37
|
+
* @param {Object} ctx
|
|
38
|
+
* @returns {Promise<{ entraProvider: Object|null, entraIdentityProvider: Object|null }>}
|
|
39
|
+
*/
|
|
40
|
+
async function loadMicrosoftCopilotEntraContext(ctx) {
|
|
41
|
+
let entraProvider = null;
|
|
42
|
+
let entraIdentityProvider = null;
|
|
43
|
+
if (!ctx?.controllerUrl) {
|
|
44
|
+
return { entraProvider, entraIdentityProvider };
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
entraProvider = await getInboundMicrosoftEntraIdProvider(ctx.controllerUrl, ctx.authConfig);
|
|
48
|
+
} catch {
|
|
49
|
+
entraProvider = null;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
entraIdentityProvider = await getKeycloakEntraIdentityProvider(
|
|
53
|
+
ctx.controllerUrl,
|
|
54
|
+
ctx.authConfig
|
|
55
|
+
);
|
|
56
|
+
} catch {
|
|
57
|
+
entraIdentityProvider = null;
|
|
58
|
+
}
|
|
59
|
+
return { entraProvider, entraIdentityProvider };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {Object} params
|
|
64
|
+
* @returns {Promise<Object>}
|
|
65
|
+
*/
|
|
66
|
+
async function resolveCopilotFormContextForError(params) {
|
|
67
|
+
const { options, ctx, name, baseName, description } = params;
|
|
68
|
+
let packageContext = null;
|
|
69
|
+
try {
|
|
70
|
+
packageContext = await loadChannelPackageContext({
|
|
71
|
+
dataplaneBaseUrl: ctx.dataplaneUrl,
|
|
72
|
+
authConfig: ctx.authConfig,
|
|
73
|
+
channelTarget: 'microsoft-copilot',
|
|
74
|
+
branding: { displayName: name, description }
|
|
75
|
+
});
|
|
76
|
+
} catch {
|
|
77
|
+
packageContext = null;
|
|
78
|
+
}
|
|
79
|
+
const { entraProvider, entraIdentityProvider } = await loadMicrosoftCopilotEntraContext(ctx);
|
|
80
|
+
return resolveChannelRuntimeValues({
|
|
81
|
+
dataplaneBaseUrl: ctx.dataplaneUrl,
|
|
82
|
+
channelTarget: 'microsoft-copilot',
|
|
83
|
+
appName: name,
|
|
84
|
+
baseName,
|
|
85
|
+
environment: ctx.environment,
|
|
86
|
+
appDescription: description,
|
|
87
|
+
packageContext,
|
|
88
|
+
entraProvider,
|
|
89
|
+
entraIdentityProvider,
|
|
90
|
+
oauthPluginVaultReferenceId: options.oauthPluginVaultReferenceId
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {Object} params
|
|
96
|
+
* @throws {Error}
|
|
97
|
+
*/
|
|
98
|
+
async function assertMicrosoftCopilotOauthReference(params) {
|
|
99
|
+
const { options } = params;
|
|
100
|
+
if (hasOauthPluginVaultReference(options)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const formContext = await resolveCopilotFormContextForError(params);
|
|
104
|
+
assertOauthPluginVaultReferenceId({ ...options, formContext });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Fail before force upload when Teams Entra SSO auth-config id is missing.
|
|
109
|
+
* @param {Object} options
|
|
110
|
+
* @throws {Error}
|
|
111
|
+
*/
|
|
112
|
+
async function assertMicrosoftCopilotOauthReferenceBeforeForce(options) {
|
|
113
|
+
if (hasOauthPluginVaultReference(options)) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const ctx = await resolveChannelAuthContext('microsoft-copilot', { silent: options.quiet });
|
|
117
|
+
const baseName = normalizeBaseChannelName(options.name);
|
|
118
|
+
const name = resolveChannelDisplayName(baseName, ctx.environment);
|
|
119
|
+
const rawDescription =
|
|
120
|
+
options.description && String(options.description).trim()
|
|
121
|
+
? String(options.description).trim()
|
|
122
|
+
: getDefaultChannelDescription();
|
|
123
|
+
await assertMicrosoftCopilotOauthReference({
|
|
124
|
+
options,
|
|
125
|
+
ctx,
|
|
126
|
+
name,
|
|
127
|
+
baseName,
|
|
128
|
+
description: resolveAppDescription('microsoft-copilot', rawDescription)
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = {
|
|
133
|
+
hasOauthPluginVaultReference,
|
|
134
|
+
loadMicrosoftCopilotEntraContext,
|
|
135
|
+
resolveCopilotFormContextForError,
|
|
136
|
+
assertMicrosoftCopilotOauthReference,
|
|
137
|
+
assertMicrosoftCopilotOauthReferenceBeforeForce
|
|
138
|
+
};
|
|
@@ -0,0 +1,180 @@
|
|
|
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
|
+
resolved.entraSsoBaseUrl =
|
|
105
|
+
originFromUrl(resolved.dataplaneBaseUrl) || originFromUrl(resolved.mcpUrl) || null;
|
|
106
|
+
resolved.entraSsoClientId = resolveEntraSsoClientId(
|
|
107
|
+
entraIdentityProvider,
|
|
108
|
+
entraProvider,
|
|
109
|
+
auth
|
|
110
|
+
);
|
|
111
|
+
const scopes = Array.isArray(resolved.scopes) ? resolved.scopes : [];
|
|
112
|
+
resolved.entraSsoScope = scopes.length > 0 ? scopes.join(', ') : null;
|
|
113
|
+
resolved.entraTenantId = resolveEntraTenantId(
|
|
114
|
+
resolved,
|
|
115
|
+
entraIdentityProvider,
|
|
116
|
+
entraProvider,
|
|
117
|
+
auth
|
|
118
|
+
);
|
|
119
|
+
if (entraIdentityProvider?.enterpriseAppId) {
|
|
120
|
+
resolved.entraEnterpriseAppId = String(entraIdentityProvider.enterpriseAppId).trim();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @param {string} fileName
|
|
126
|
+
* @returns {string}
|
|
127
|
+
*/
|
|
128
|
+
function readCopilotTemplate(fileName) {
|
|
129
|
+
const templatePath = path.join(TEMPLATES_DIR, fileName);
|
|
130
|
+
if (!fs.existsSync(templatePath)) {
|
|
131
|
+
throw new Error(`Microsoft Copilot template not found: ${templatePath}`);
|
|
132
|
+
}
|
|
133
|
+
return fs.readFileSync(templatePath, 'utf8');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @param {string} fileName
|
|
138
|
+
* @param {Object} context
|
|
139
|
+
* @returns {string}
|
|
140
|
+
*/
|
|
141
|
+
function renderCopilotChannelTemplate(fileName, context) {
|
|
142
|
+
const hb = Handlebars.create();
|
|
143
|
+
hb.registerPartial('entraSsoRegistration', readCopilotTemplate('entra-sso-registration.template.md.hbs'));
|
|
144
|
+
const template = hb.compile(readCopilotTemplate(fileName));
|
|
145
|
+
return template(context).trim();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Operator-facing error when Teams Entra SSO auth-config id is missing.
|
|
150
|
+
* Prose lives in templates — not inline CLI copy.
|
|
151
|
+
* @param {Object|string} [context]
|
|
152
|
+
* @returns {string}
|
|
153
|
+
*/
|
|
154
|
+
function formatOauthPluginVaultReferenceRequiredError(context = {}) {
|
|
155
|
+
const values = typeof context === 'string' ? { appName: context, baseName: context } : context || {};
|
|
156
|
+
const appName =
|
|
157
|
+
(typeof values.appName === 'string' && values.appName.trim()) ||
|
|
158
|
+
(typeof values.baseName === 'string' && values.baseName.trim()) ||
|
|
159
|
+
'Your Assistant';
|
|
160
|
+
const baseName =
|
|
161
|
+
(typeof values.baseName === 'string' && values.baseName.trim()) || appName;
|
|
162
|
+
return renderCopilotChannelTemplate('oauth-plugin-vault-required.template.md.hbs', {
|
|
163
|
+
...values,
|
|
164
|
+
appName,
|
|
165
|
+
baseName,
|
|
166
|
+
channelTarget: 'microsoft-copilot',
|
|
167
|
+
entraSsoRegistrationUrl: values.entraSsoRegistrationUrl || ENTRA_SSO_REGISTRATION_URL
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
module.exports = {
|
|
172
|
+
ENTRA_SSO_REGISTRATION_URL,
|
|
173
|
+
extractTenantIdFromIssuer,
|
|
174
|
+
originFromUrl,
|
|
175
|
+
resolveEntraSsoClientId,
|
|
176
|
+
resolveEntraTenantId,
|
|
177
|
+
applyCopilotEntraSsoFormFields,
|
|
178
|
+
renderCopilotChannelTemplate,
|
|
179
|
+
formatOauthPluginVaultReferenceRequiredError
|
|
180
|
+
};
|