@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
|
@@ -0,0 +1,264 @@
|
|
|
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
|
+
* @param {Object} params
|
|
135
|
+
* @returns {string|null}
|
|
136
|
+
*/
|
|
137
|
+
function firstCopilotEntraBlocker(params) {
|
|
138
|
+
const {
|
|
139
|
+
discoveryReady,
|
|
140
|
+
discoveryBlocker,
|
|
141
|
+
recommended,
|
|
142
|
+
referenceId,
|
|
143
|
+
issuer,
|
|
144
|
+
audience,
|
|
145
|
+
scopes
|
|
146
|
+
} = params;
|
|
147
|
+
if (!discoveryReady) {
|
|
148
|
+
return discoveryBlocker || 'remoteMcpUnavailable';
|
|
149
|
+
}
|
|
150
|
+
if (recommended !== TRUSTED_ISSUER) {
|
|
151
|
+
return recommended === CUSTOMER_OAUTH
|
|
152
|
+
? 'customerOAuthNotAllowed'
|
|
153
|
+
: 'trustedIssuerRequired';
|
|
154
|
+
}
|
|
155
|
+
if (!referenceId) {
|
|
156
|
+
return 'oauthPluginVaultReferenceRequired';
|
|
157
|
+
}
|
|
158
|
+
if (!issuer) {
|
|
159
|
+
return 'entraIssuerRequired';
|
|
160
|
+
}
|
|
161
|
+
if (!isPresent(audience)) {
|
|
162
|
+
return 'runtimeMcpAudienceRequired';
|
|
163
|
+
}
|
|
164
|
+
if (isOpenIdOnlyOrEmptyScopes(scopes)) {
|
|
165
|
+
return 'runtimeMcpScopeRequired';
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @param {Object|null} auth
|
|
172
|
+
* @param {Object|null} entraProvider
|
|
173
|
+
* @returns {{ audience: * , issuer: string|null }}
|
|
174
|
+
*/
|
|
175
|
+
function resolveAudienceAndIssuer(auth, entraProvider) {
|
|
176
|
+
const audience = isPresent(entraProvider?.audience)
|
|
177
|
+
? entraProvider.audience
|
|
178
|
+
: isPresent(auth?.audience)
|
|
179
|
+
? auth.audience
|
|
180
|
+
: null;
|
|
181
|
+
const issuer =
|
|
182
|
+
(entraProvider && entraProvider.issuer) || (auth && auth.issuer) || null;
|
|
183
|
+
return { audience, issuer };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @param {Object} params
|
|
188
|
+
* @returns {{ installReady: boolean, blocker: string|null, packageAuthentication: Object|null }}
|
|
189
|
+
*/
|
|
190
|
+
function evaluateCopilotEntraSsoReadiness(params) {
|
|
191
|
+
const packageContext = params.packageContext || null;
|
|
192
|
+
const auth = mapCopilotPackageAuthentication(packageContext?.authentication || null);
|
|
193
|
+
const recommended = resolveRecommendedCapability(auth);
|
|
194
|
+
const referenceId =
|
|
195
|
+
typeof params.oauthPluginVaultReferenceId === 'string'
|
|
196
|
+
? params.oauthPluginVaultReferenceId.trim()
|
|
197
|
+
: '';
|
|
198
|
+
const scopes = normalizeScopeList(params.scopes);
|
|
199
|
+
const { audience, issuer } = resolveAudienceAndIssuer(auth, params.entraProvider || null);
|
|
200
|
+
|
|
201
|
+
if (auth) {
|
|
202
|
+
auth.issuer = issuer;
|
|
203
|
+
auth.audience = audience;
|
|
204
|
+
auth.scopes = scopes;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const blocker = firstCopilotEntraBlocker({
|
|
208
|
+
discoveryReady: packageContext?.installReady === true,
|
|
209
|
+
discoveryBlocker: packageContext?.blocker || null,
|
|
210
|
+
recommended,
|
|
211
|
+
referenceId,
|
|
212
|
+
issuer,
|
|
213
|
+
audience,
|
|
214
|
+
scopes
|
|
215
|
+
});
|
|
216
|
+
return {
|
|
217
|
+
installReady: blocker === null,
|
|
218
|
+
blocker,
|
|
219
|
+
packageAuthentication: auth
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const {
|
|
224
|
+
formatOauthPluginVaultReferenceRequiredError
|
|
225
|
+
} = require('./microsoft-copilot-entra-sso-form');
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* @param {Object} [options]
|
|
229
|
+
* @throws {Error}
|
|
230
|
+
*/
|
|
231
|
+
function assertOauthPluginVaultReferenceId(options = {}) {
|
|
232
|
+
const ref = options.oauthPluginVaultReferenceId;
|
|
233
|
+
if (typeof ref === 'string' && ref.trim()) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
throw new Error(formatOauthPluginVaultReferenceRequiredError(options.formContext || options));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @param {Object} [runtimeValues]
|
|
241
|
+
* @throws {Error}
|
|
242
|
+
*/
|
|
243
|
+
function assertCopilotPackageOauthPluginVaultReference(runtimeValues = {}) {
|
|
244
|
+
const ref = runtimeValues.oauthPluginVaultReferenceId;
|
|
245
|
+
if (typeof ref === 'string' && ref.trim()) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
throw new Error(formatOauthPluginVaultReferenceRequiredError(runtimeValues));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
module.exports = {
|
|
252
|
+
TRUSTED_ISSUER,
|
|
253
|
+
CUSTOMER_OAUTH,
|
|
254
|
+
skipsChannelOauthCredential,
|
|
255
|
+
normalizeScopeList,
|
|
256
|
+
isOpenIdOnlyOrEmptyScopes,
|
|
257
|
+
resolveRecommendedCapability,
|
|
258
|
+
mapCopilotPackageAuthentication,
|
|
259
|
+
resolveCopilotEntraScopes,
|
|
260
|
+
evaluateCopilotEntraSsoReadiness,
|
|
261
|
+
formatOauthPluginVaultReferenceRequiredError,
|
|
262
|
+
assertOauthPluginVaultReferenceId,
|
|
263
|
+
assertCopilotPackageOauthPluginVaultReference
|
|
264
|
+
};
|
|
@@ -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,84 @@
|
|
|
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.0.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
|
+
/**
|
|
19
|
+
* @param {string} text
|
|
20
|
+
* @param {number} max
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*/
|
|
23
|
+
function truncateForSchema(text, max) {
|
|
24
|
+
const value = String(text ?? '').trim();
|
|
25
|
+
if (!value) return '';
|
|
26
|
+
if (value.length <= max) return value;
|
|
27
|
+
if (max <= 1) return value.slice(0, max);
|
|
28
|
+
return `${value.slice(0, max - 1)}…`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Plugin namespace: lowercase alphanumeric/hyphen only (plugin schema ^[A-Za-z0-9-]+$).
|
|
33
|
+
* @param {string} baseName
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
36
|
+
function buildPluginNamespace(baseName) {
|
|
37
|
+
const raw = String(baseName ?? 'aifabrix')
|
|
38
|
+
.trim()
|
|
39
|
+
.toLowerCase()
|
|
40
|
+
.replace(/[^a-z0-9-]+/g, '-')
|
|
41
|
+
.replace(/-+/g, '-')
|
|
42
|
+
.replace(/^-|-$/g, '');
|
|
43
|
+
const ns = raw || 'aifabrix';
|
|
44
|
+
return truncateForSchema(ns, 32).replace(/[^a-z0-9-]/g, '') || 'aifabrix';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {Object} runtimeValues
|
|
49
|
+
* @returns {Object}
|
|
50
|
+
*/
|
|
51
|
+
function applyMicrosoftCopilotDisplayLimits(runtimeValues) {
|
|
52
|
+
const appDescription = String(runtimeValues.appDescription || '').trim();
|
|
53
|
+
const baseName = String(runtimeValues.baseName || runtimeValues.appName || 'Assistant').trim();
|
|
54
|
+
const appName = String(runtimeValues.appName || baseName).trim();
|
|
55
|
+
runtimeValues.shortName = truncateForSchema(baseName, TEAMS_NAME_SHORT_MAX);
|
|
56
|
+
runtimeValues.fullName = truncateForSchema(appName, TEAMS_NAME_FULL_MAX);
|
|
57
|
+
runtimeValues.shortDescription = truncateForSchema(
|
|
58
|
+
appDescription || 'Enterprise AI assistant via AI Fabrix Runtime.',
|
|
59
|
+
TEAMS_DESCRIPTION_SHORT_MAX
|
|
60
|
+
);
|
|
61
|
+
runtimeValues.fullDescription = truncateForSchema(
|
|
62
|
+
appDescription || runtimeValues.shortDescription,
|
|
63
|
+
TEAMS_DESCRIPTION_FULL_MAX
|
|
64
|
+
);
|
|
65
|
+
runtimeValues.pluginNameForHuman = truncateForSchema(baseName, PLUGIN_NAME_FOR_HUMAN_MAX);
|
|
66
|
+
runtimeValues.pluginDescriptionForHuman = truncateForSchema(
|
|
67
|
+
appDescription || runtimeValues.shortDescription,
|
|
68
|
+
PLUGIN_DESCRIPTION_FOR_HUMAN_MAX
|
|
69
|
+
);
|
|
70
|
+
runtimeValues.pluginNamespace = buildPluginNamespace(baseName);
|
|
71
|
+
return runtimeValues;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
TEAMS_NAME_SHORT_MAX,
|
|
76
|
+
TEAMS_NAME_FULL_MAX,
|
|
77
|
+
TEAMS_DESCRIPTION_SHORT_MAX,
|
|
78
|
+
TEAMS_DESCRIPTION_FULL_MAX,
|
|
79
|
+
PLUGIN_NAME_FOR_HUMAN_MAX,
|
|
80
|
+
PLUGIN_DESCRIPTION_FOR_HUMAN_MAX,
|
|
81
|
+
truncateForSchema,
|
|
82
|
+
buildPluginNamespace,
|
|
83
|
+
applyMicrosoftCopilotDisplayLimits
|
|
84
|
+
};
|
|
@@ -9,12 +9,16 @@
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
11
|
const crypto = require('crypto');
|
|
12
|
-
const fs = require('fs');
|
|
12
|
+
const fs = require('../internal/fs-real-sync');
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const AdmZip = require('adm-zip');
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
15
|
+
const { getMicrosoftCopilotIcons } = require('./microsoft-copilot-icons');
|
|
16
|
+
const { assertValidMicrosoftCopilotPackage } = require('./microsoft-copilot-validate');
|
|
17
|
+
const { applyMicrosoftCopilotDisplayLimits } = require('./microsoft-copilot-limits');
|
|
18
|
+
const {
|
|
19
|
+
assertCopilotPackageOauthPluginVaultReference
|
|
20
|
+
} = require('./microsoft-copilot-entra-sso');
|
|
21
|
+
const { renderCopilotChannelTemplate } = require('./microsoft-copilot-entra-sso-form');
|
|
18
22
|
|
|
19
23
|
/**
|
|
20
24
|
* @param {string} outputPath
|
|
@@ -37,16 +41,20 @@ function buildCopilotOutputPaths(outputPath, runtimeValues) {
|
|
|
37
41
|
zipFileName,
|
|
38
42
|
zipPath: path.join(buildDir, zipFileName),
|
|
39
43
|
validationPath: path.join(installDir, 'validation.json'),
|
|
40
|
-
installPath: path.join(
|
|
44
|
+
installPath: path.join(rootDir, 'INSTALL.md'),
|
|
45
|
+
installReadmePath: path.join(installDir, 'README.md')
|
|
41
46
|
};
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
/**
|
|
50
|
+
* @param {Object} [runtimeValues]
|
|
45
51
|
* @returns {{ color: Buffer, outline: Buffer }}
|
|
46
52
|
*/
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
function resolvePackageIcons(runtimeValues) {
|
|
54
|
+
return getMicrosoftCopilotIcons({
|
|
55
|
+
colorPath: runtimeValues?.iconColorPath,
|
|
56
|
+
outlinePath: runtimeValues?.iconOutlinePath
|
|
57
|
+
});
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
/**
|
|
@@ -67,7 +75,8 @@ function renderCopilotTemplates(runtimeValues, renderTemplateFile, templatesDir)
|
|
|
67
75
|
runtimeValues
|
|
68
76
|
)
|
|
69
77
|
};
|
|
70
|
-
|
|
78
|
+
// INSTALL prose + Entra SSO form table live in templates (partial entraSsoRegistration).
|
|
79
|
+
const installText = renderCopilotChannelTemplate('install.template.md.hbs', runtimeValues);
|
|
71
80
|
return { sourceFiles, installText };
|
|
72
81
|
}
|
|
73
82
|
|
|
@@ -112,6 +121,9 @@ function buildCopilotValidation(runtimeValues, paths, sourceFileNames) {
|
|
|
112
121
|
runtimeMcpUrl: runtimeValues.mcpUrl,
|
|
113
122
|
runtimeTransport: runtimeValues.runtimeTransport,
|
|
114
123
|
expectedLayerBToolNames: runtimeValues.layerBToolNames || [],
|
|
124
|
+
governedToolNames: runtimeValues.layerBToolNames || [],
|
|
125
|
+
installReady: runtimeValues.installReady === true,
|
|
126
|
+
blocker: runtimeValues.discoveryBlocker || null,
|
|
115
127
|
boundaries: {
|
|
116
128
|
onePackagePerEnvironment: true,
|
|
117
129
|
topLevelVendorToolsExposed: false,
|
|
@@ -142,6 +154,65 @@ function computeCopilotArtifactHash(parts) {
|
|
|
142
154
|
return hash.digest('hex');
|
|
143
155
|
}
|
|
144
156
|
|
|
157
|
+
/**
|
|
158
|
+
* @param {Object} runtimeValues
|
|
159
|
+
* @returns {Object}
|
|
160
|
+
*/
|
|
161
|
+
function buildCopilotSummaryRuntime(runtimeValues) {
|
|
162
|
+
return {
|
|
163
|
+
runtimeSystemKey: runtimeValues.runtimeSystemKey,
|
|
164
|
+
remoteMcpUrl: runtimeValues.mcpUrl,
|
|
165
|
+
transport: runtimeValues.runtimeTransport,
|
|
166
|
+
assistantCatalogUrl: runtimeValues.assistantCatalogUrl,
|
|
167
|
+
roleAssistantResolution: 'dynamic-runtime',
|
|
168
|
+
layerBToolNames: runtimeValues.layerBToolNames || [],
|
|
169
|
+
governedToolNames: runtimeValues.layerBToolNames || []
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @param {Object} runtimeValues
|
|
175
|
+
* @returns {Object}
|
|
176
|
+
*/
|
|
177
|
+
function buildCopilotSummaryAuthentication(runtimeValues) {
|
|
178
|
+
const discovery = runtimeValues.discoveryAuthentication || null;
|
|
179
|
+
const serialized = discovery ? JSON.stringify(discovery) : '';
|
|
180
|
+
if (/authorizationServerMetadataUrl":"[^"]+"/i.test(serialized)) {
|
|
181
|
+
throw new Error('Keycloak authorizationServerMetadata must not appear in Copilot package summary');
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
mode: runtimeValues.runtimeAuthMode,
|
|
185
|
+
pluginAuthType: 'OAuthPluginVault',
|
|
186
|
+
oauthPluginVaultReferenceId: runtimeValues.oauthPluginVaultReferenceId || null,
|
|
187
|
+
recommendedCapability: discovery?.recommendedCapability || null,
|
|
188
|
+
entraIssuer: runtimeValues.entraIssuer || discovery?.issuer || null,
|
|
189
|
+
entraEnterpriseAppId: runtimeValues.entraEnterpriseAppId || null,
|
|
190
|
+
entraTenantId: runtimeValues.entraTenantId || null,
|
|
191
|
+
entraSsoClientId: runtimeValues.entraSsoClientId || null,
|
|
192
|
+
scopes: runtimeValues.scopes || [],
|
|
193
|
+
discovery,
|
|
194
|
+
note:
|
|
195
|
+
'Microsoft Entra SSO via OAuthPluginVault (Teams auth-config reference_id); ' +
|
|
196
|
+
'miso-controller performs exchange — package carries public specs only; no Fabrix OAuth secrets'
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @param {Object} runtimeValues
|
|
202
|
+
* @param {Object} paths
|
|
203
|
+
* @returns {Object}
|
|
204
|
+
*/
|
|
205
|
+
function buildCopilotSummaryPackage(runtimeValues, paths) {
|
|
206
|
+
return {
|
|
207
|
+
appId: runtimeValues.packageAppId,
|
|
208
|
+
version: runtimeValues.packageVersion,
|
|
209
|
+
sourceFiles: ['manifest.json', 'declarativeAgent.json', 'ai-plugin.json', 'color.png', 'outline.png'],
|
|
210
|
+
zipFileName: paths.zipFileName,
|
|
211
|
+
installGuide: 'INSTALL.md',
|
|
212
|
+
validationFile: path.relative(paths.rootDir, paths.validationPath)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
145
216
|
/**
|
|
146
217
|
* @param {Object} runtimeValues
|
|
147
218
|
* @param {Object} paths
|
|
@@ -156,27 +227,16 @@ function buildCopilotSummary(runtimeValues, paths, validation, artifactHash) {
|
|
|
156
227
|
baseName: runtimeValues.baseName,
|
|
157
228
|
description: runtimeValues.appDescription,
|
|
158
229
|
environment: runtimeValues.environment,
|
|
159
|
-
runtime:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
credentialKey: runtimeValues.credentialKey,
|
|
170
|
-
credentialPortalUrl: runtimeValues.credentialPortalUrl,
|
|
171
|
-
scopes: runtimeValues.scopes || []
|
|
172
|
-
},
|
|
173
|
-
package: {
|
|
174
|
-
appId: runtimeValues.packageAppId,
|
|
175
|
-
version: runtimeValues.packageVersion,
|
|
176
|
-
sourceFiles: ['manifest.json', 'declarativeAgent.json', 'ai-plugin.json', 'color.png', 'outline.png'],
|
|
177
|
-
zipFileName: paths.zipFileName,
|
|
178
|
-
installGuide: path.relative(paths.rootDir, paths.installPath),
|
|
179
|
-
validationFile: path.relative(paths.rootDir, paths.validationPath)
|
|
230
|
+
runtime: buildCopilotSummaryRuntime(runtimeValues),
|
|
231
|
+
authentication: buildCopilotSummaryAuthentication(runtimeValues),
|
|
232
|
+
package: buildCopilotSummaryPackage(runtimeValues, paths),
|
|
233
|
+
installReady: runtimeValues.installReady === true,
|
|
234
|
+
blocker: runtimeValues.discoveryBlocker || null,
|
|
235
|
+
links: {
|
|
236
|
+
readme: paths.installPath,
|
|
237
|
+
zip: paths.zipPath,
|
|
238
|
+
filesRoot: paths.sourceDir,
|
|
239
|
+
validation: paths.validationPath
|
|
180
240
|
},
|
|
181
241
|
probeTiers: {
|
|
182
242
|
builderValidated: true,
|
|
@@ -211,24 +271,21 @@ function writeCopilotArtifacts(params) {
|
|
|
211
271
|
fs.writeFileSync(path.join(paths.sourceDir, 'outline.png'), icons.outline);
|
|
212
272
|
fs.writeFileSync(paths.zipPath, zipBuffer);
|
|
213
273
|
fs.writeFileSync(paths.installPath, installText, 'utf8');
|
|
274
|
+
if (paths.installReadmePath) {
|
|
275
|
+
fs.writeFileSync(paths.installReadmePath, installText, 'utf8');
|
|
276
|
+
}
|
|
214
277
|
fs.writeFileSync(paths.validationPath, validationText, 'utf8');
|
|
215
278
|
fs.writeFileSync(paths.summaryPath, summaryText, 'utf8');
|
|
216
279
|
}
|
|
217
280
|
|
|
218
281
|
/**
|
|
219
282
|
* @param {Object} params
|
|
220
|
-
* @param {Object}
|
|
221
|
-
* @param {
|
|
222
|
-
* @param {
|
|
223
|
-
* @
|
|
224
|
-
* @param {(content: string) => void} params.assertNoSecretsInArtifact
|
|
225
|
-
* @param {(templatePath: string, context: Object) => string} params.renderTemplateFile
|
|
226
|
-
* @returns {{ outputPath: string, artifactHash: string, content: string, installPath: string }}
|
|
283
|
+
* @param {Object} runtimeValues
|
|
284
|
+
* @param {Object} paths
|
|
285
|
+
* @param {{ color: Buffer, outline: Buffer }} icons
|
|
286
|
+
* @returns {{ summary: Object, summaryText: string, zipBuffer: Buffer, sourceFiles: Object, installText: string, validationText: string }}
|
|
227
287
|
*/
|
|
228
|
-
function
|
|
229
|
-
const runtimeValues = params.runtimeValues || {};
|
|
230
|
-
const paths = buildCopilotOutputPaths(params.outputPath, runtimeValues);
|
|
231
|
-
const icons = getPlaceholderIcons();
|
|
288
|
+
function buildCopilotPackagePayload(params, runtimeValues, paths, icons) {
|
|
232
289
|
const { sourceFiles, installText } = renderCopilotTemplates(
|
|
233
290
|
runtimeValues,
|
|
234
291
|
params.renderTemplateFile,
|
|
@@ -236,6 +293,7 @@ function generateMicrosoftCopilotPackage(params) {
|
|
|
236
293
|
);
|
|
237
294
|
assertNoSecretsInSourceFiles(sourceFiles, params.assertNoSecretsInArtifact);
|
|
238
295
|
params.assertNoSecretsInArtifact(installText);
|
|
296
|
+
assertValidMicrosoftCopilotPackage(sourceFiles, icons);
|
|
239
297
|
const zipBuffer = buildCopilotZipBuffer(sourceFiles, icons);
|
|
240
298
|
const validation = buildCopilotValidation(runtimeValues, paths, Object.keys(sourceFiles));
|
|
241
299
|
const validationText = JSON.stringify(validation, null, 2);
|
|
@@ -250,22 +308,45 @@ function generateMicrosoftCopilotPackage(params) {
|
|
|
250
308
|
const summary = buildCopilotSummary(runtimeValues, paths, validation, artifactHash);
|
|
251
309
|
const summaryText = JSON.stringify(summary, null, 2);
|
|
252
310
|
params.assertNoSecretsInArtifact(summaryText);
|
|
311
|
+
return { summary, summaryText, zipBuffer, sourceFiles, installText, validationText };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* @param {Object} params
|
|
316
|
+
* @param {Object} params.runtimeValues
|
|
317
|
+
* @param {string} params.templatesDir
|
|
318
|
+
* @param {string} params.outputPath
|
|
319
|
+
* @param {boolean} [params.dryRun]
|
|
320
|
+
* @param {(content: string) => void} params.assertNoSecretsInArtifact
|
|
321
|
+
* @param {(templatePath: string, context: Object) => string} params.renderTemplateFile
|
|
322
|
+
* @returns {{ outputPath: string, artifactHash: string, content: string, installPath: string }}
|
|
323
|
+
*/
|
|
324
|
+
function generateMicrosoftCopilotPackage(params) {
|
|
325
|
+
const runtimeValues = applyMicrosoftCopilotDisplayLimits({ ...(params.runtimeValues || {}) });
|
|
326
|
+
assertCopilotPackageOauthPluginVaultReference(runtimeValues);
|
|
327
|
+
const paths = buildCopilotOutputPaths(params.outputPath, runtimeValues);
|
|
328
|
+
const icons = resolvePackageIcons(runtimeValues);
|
|
329
|
+
const payload = buildCopilotPackagePayload(params, runtimeValues, paths, icons);
|
|
253
330
|
if (!params.dryRun) {
|
|
254
331
|
writeCopilotArtifacts({
|
|
255
332
|
paths,
|
|
256
|
-
sourceFiles,
|
|
333
|
+
sourceFiles: payload.sourceFiles,
|
|
257
334
|
icons,
|
|
258
|
-
zipBuffer,
|
|
259
|
-
installText,
|
|
260
|
-
validationText,
|
|
261
|
-
summaryText
|
|
335
|
+
zipBuffer: payload.zipBuffer,
|
|
336
|
+
installText: payload.installText,
|
|
337
|
+
validationText: payload.validationText,
|
|
338
|
+
summaryText: payload.summaryText
|
|
262
339
|
});
|
|
263
340
|
}
|
|
264
341
|
return {
|
|
265
342
|
outputPath: paths.summaryPath,
|
|
266
|
-
artifactHash,
|
|
267
|
-
content: summaryText,
|
|
268
|
-
installPath: paths.installPath
|
|
343
|
+
artifactHash: payload.summary.artifact.hash,
|
|
344
|
+
content: payload.summaryText,
|
|
345
|
+
installPath: paths.installPath,
|
|
346
|
+
zipPath: paths.zipPath,
|
|
347
|
+
links: payload.summary.links,
|
|
348
|
+
installReady: payload.summary.installReady === true,
|
|
349
|
+
blocker: payload.summary.blocker || null
|
|
269
350
|
};
|
|
270
351
|
}
|
|
271
352
|
|