@aifabrix/builder 2.51.1 → 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/app/run-container-start.js +4 -0
- package/lib/app/run-deploy-job.js +135 -27
- package/lib/app/run-env-compose.js +32 -0
- package/lib/app/run-env-recovery.js +35 -0
- package/lib/app/run-helpers.js +5 -6
- package/lib/app/run.js +11 -3
- package/lib/channels/add-channel.js +126 -19
- package/lib/channels/approval-guides/chatgpt.js +12 -9
- 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/channel-targets.js +18 -1
- package/lib/channels/chatgpt-runtime-contract.js +1 -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-footers.js +1 -1
- package/lib/cli/infra-guided-public-url-warn.js +55 -0
- package/lib/cli/infra-guided.js +78 -62
- package/lib/cli/setup-app.help.js +4 -0
- package/lib/cli/setup-app.test-commands.js +59 -28
- package/lib/cli/setup-external-system.js +50 -12
- package/lib/cli/setup-infra.js +31 -4
- package/lib/commands/channel-add-setup.js +35 -10
- package/lib/commands/channel.js +27 -12
- package/lib/commands/deploy-gate-orchestrator.js +3 -2
- package/lib/commands/governance-verify-external.js +2 -5
- package/lib/commands/integration-definition.js +158 -0
- package/lib/commands/lifecycle-external.js +14 -7
- 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 +131 -114
- package/lib/commands/test-governance-external.js +2 -5
- package/lib/commands/up-miso.js +61 -24
- package/lib/commands/upload-data-sync-finalize.js +20 -3
- package/lib/commands/verify-governance-external.js +2 -5
- package/lib/commands/verify-operations-external.js +2 -5
- package/lib/commands/verify-operations-step-plan.js +31 -0
- package/lib/commands/verify-trust-command-action.js +2 -5
- package/lib/datasource/capability/capability-resolve.js +15 -5
- package/lib/datasource/capability/copy-operations.js +14 -4
- package/lib/datasource/datasource-identifier.js +4 -3
- package/lib/datasource/integration-context.js +3 -3
- package/lib/datasource/resolve-app.js +90 -8
- package/lib/datasource/test-e2e.js +2 -2
- package/lib/datasource/unified-validation-run-body.js +15 -1
- package/lib/datasource/unified-validation-run-resolve.js +2 -2
- package/lib/external-system/deploy-helpers.js +9 -5
- package/lib/external-system/deploy.js +2 -1
- package/lib/external-system/test-system-level.js +21 -3
- package/lib/external-system/test.js +4 -4
- package/lib/generator/builders.js +9 -15
- 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-markdown-frontmatter.js +75 -0
- package/lib/role-assistant/knowledge-sync-logging.js +58 -0
- package/lib/role-assistant/knowledge-sync.js +104 -89
- 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 +289 -46
- package/lib/role-assistant/test-runner-workhub.js +121 -25
- package/lib/role-assistant/test-suite.js +119 -0
- package/lib/schema/application-schema.json +64 -32
- package/lib/schema/external-datasource.schema.json +103 -17
- package/lib/schema/external-system.schema.json +54 -3
- package/lib/schema/infra.parameter.yaml +0 -20
- package/lib/schema/integration-definition.schema.json +195 -0
- package/lib/utils/authentication-shape.js +6 -0
- package/lib/utils/certify-external-workspace.js +40 -0
- package/lib/utils/docker-build.js +3 -3
- package/lib/utils/docker-run-ephemeral.js +77 -17
- package/lib/utils/health-check-public-warn.js +31 -13
- package/lib/utils/health-check.js +11 -1
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/paths.js +13 -15
- package/lib/utils/promote-dev-docker-image.js +97 -18
- package/lib/utils/role-assistant-paths.js +76 -14
- package/lib/utils/transient-developer-id.js +28 -0
- package/lib/utils/upload-sync-options.js +7 -4
- package/lib/validation/reserved-system-key-prefixes.js +56 -0
- package/lib/validation/validate.js +54 -19
- package/package.json +1 -1
- package/scripts/pnpm/af.mjs +24 -9
- package/scripts/pnpm/help.mjs +1 -1
- package/templates/applications/builder-api/application.yaml +3 -3
- package/templates/applications/dataplane/application.yaml +5 -5
- package/templates/applications/dataplane/env.template +8 -7
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/application.yaml +2 -2
- package/templates/applications/keycloak/env.template +5 -2
- package/templates/applications/miso-controller/application.yaml +25 -11
- package/templates/applications/miso-controller/env.template +24 -5
- 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 +76 -36
- package/templates/channels/chatgpt/integration/README.md +4 -4
- package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
- package/templates/channels/chatgpt/manifest.template.json.hbs +3 -3
- 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/install.template.md.hbs +6 -1
- 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 +10 -0
- package/templates/typescript/docker-compose.hbs +10 -0
- /package/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
auditCredentialUpdated,
|
|
19
19
|
auditFailed
|
|
20
20
|
} = require('./channel-audit');
|
|
21
|
+
const { skipsChannelOauthCredential } = require('./microsoft-copilot-entra-sso');
|
|
21
22
|
|
|
22
23
|
/** @type {Record<string, { method: string, displayName: string, credentialConfig: Object }>} */
|
|
23
24
|
const CREDENTIAL_SPECS = {
|
|
@@ -171,6 +172,13 @@ async function pushIntegrationSecrets(ctx, channelTarget) {
|
|
|
171
172
|
*/
|
|
172
173
|
async function ensureChannelCredential(params) {
|
|
173
174
|
const { ctx, channelTarget, target, force = false } = params;
|
|
175
|
+
if (skipsChannelOauthCredential(channelTarget)) {
|
|
176
|
+
return {
|
|
177
|
+
skipped: true,
|
|
178
|
+
reason: 'entraSsoTrustedIssuer',
|
|
179
|
+
credentialKey: target.credentialKey
|
|
180
|
+
};
|
|
181
|
+
}
|
|
174
182
|
const existing = await findCredentialByKey(ctx, target.credentialKey);
|
|
175
183
|
if (existing) {
|
|
176
184
|
auditCredentialUpdated(channelTarget, {
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External AI Discovery mapping for channel packaging (plan 167.0).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Pure discovery → package context; optional Dataplane fetch
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { ApiClient } = require('../api');
|
|
12
|
+
const { RUNTIME_ORCHESTRATION_SYSTEM_KEY } = require('./chatgpt-runtime-contract');
|
|
13
|
+
const { normalizeBaseUrl, joinUrl } = require('./channel-runtime-resolver');
|
|
14
|
+
|
|
15
|
+
const GATEWAY_TOOLS_CATALOG_MARKER = '/api/v1/mcp/servers/tools';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} pathOrUrl
|
|
19
|
+
* @returns {boolean}
|
|
20
|
+
*/
|
|
21
|
+
function isGatewayToolsCatalogUrl(pathOrUrl) {
|
|
22
|
+
const value = String(pathOrUrl || '');
|
|
23
|
+
return value.includes(GATEWAY_TOOLS_CATALOG_MARKER);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Normalize discovery paths that include a public `/data` prefix.
|
|
28
|
+
* @param {string} dataplaneBaseUrl
|
|
29
|
+
* @param {string|null|undefined} pathOrUrl
|
|
30
|
+
* @returns {string|null}
|
|
31
|
+
*/
|
|
32
|
+
function resolveDiscoveryUrl(dataplaneBaseUrl, pathOrUrl) {
|
|
33
|
+
if (!pathOrUrl || typeof pathOrUrl !== 'string') {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const trimmed = pathOrUrl.trim();
|
|
37
|
+
if (!trimmed) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
if (isGatewayToolsCatalogUrl(trimmed)) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
'Gateway tools catalog must not be used as MCP server URL for channel packages'
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
46
|
+
return trimmed;
|
|
47
|
+
}
|
|
48
|
+
let relative = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
49
|
+
if (relative.startsWith('/data/api/')) {
|
|
50
|
+
relative = relative.slice('/data'.length);
|
|
51
|
+
}
|
|
52
|
+
return joinUrl(normalizeBaseUrl(dataplaneBaseUrl), relative);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {Object} discovery
|
|
57
|
+
* @param {string} key
|
|
58
|
+
* @returns {Object|null}
|
|
59
|
+
*/
|
|
60
|
+
function findEntryPoint(discovery, key) {
|
|
61
|
+
const entries = Array.isArray(discovery?.entryPoints) ? discovery.entryPoints : [];
|
|
62
|
+
return entries.find((ep) => ep && ep.key === key) || null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {Object} discovery
|
|
67
|
+
* @returns {string[]}
|
|
68
|
+
*/
|
|
69
|
+
function extractGovernedToolNames(discovery) {
|
|
70
|
+
const fromRuntime = discovery?.runtimeDiscovery?.governedToolNames;
|
|
71
|
+
if (Array.isArray(fromRuntime) && fromRuntime.length > 0) {
|
|
72
|
+
return fromRuntime.map((name) => String(name)).filter(Boolean);
|
|
73
|
+
}
|
|
74
|
+
const tools = Array.isArray(discovery?.tools) ? discovery.tools : [];
|
|
75
|
+
return tools.map((tool) => (tool && tool.name ? String(tool.name) : '')).filter(Boolean);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {Object} discovery
|
|
80
|
+
* @param {Object|null} adapter
|
|
81
|
+
* @param {string} dataplaneBaseUrl
|
|
82
|
+
* @returns {{ mcpServerUrl: string|null, installReady: boolean, blocker: string|null }}
|
|
83
|
+
*/
|
|
84
|
+
function resolveMcpConnection(discovery, adapter, dataplaneBaseUrl) {
|
|
85
|
+
const adapterUrl = adapter?.connection?.mcpServerUrl || null;
|
|
86
|
+
const remote = findEntryPoint(discovery, 'remoteMcp');
|
|
87
|
+
const fromEntry =
|
|
88
|
+
resolveDiscoveryUrl(dataplaneBaseUrl, remote?.absoluteUrl) ||
|
|
89
|
+
resolveDiscoveryUrl(dataplaneBaseUrl, remote?.path);
|
|
90
|
+
const mcpServerUrl = resolveDiscoveryUrl(dataplaneBaseUrl, adapterUrl) || fromEntry;
|
|
91
|
+
if (mcpServerUrl && isGatewayToolsCatalogUrl(mcpServerUrl)) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
'Gateway tools catalog must not be used as MCP server URL for channel packages'
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const mcpReady = discovery?.publication?.mcpReady === true;
|
|
97
|
+
const connectable = adapter ? adapter.connectable === true : mcpReady;
|
|
98
|
+
const remoteAvailable = discovery?.transport?.remoteMcp?.status === 'available';
|
|
99
|
+
const installReady = Boolean(mcpReady && connectable && remoteAvailable && mcpServerUrl);
|
|
100
|
+
const blocker =
|
|
101
|
+
adapter?.blocker ||
|
|
102
|
+
(!installReady ? 'remoteMcpUnavailable' : null);
|
|
103
|
+
return { mcpServerUrl, installReady, blocker };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {Object} branding
|
|
108
|
+
* @param {Object} identity
|
|
109
|
+
* @param {Object|null} adapter
|
|
110
|
+
* @returns {string}
|
|
111
|
+
*/
|
|
112
|
+
function resolvePackageDisplayName(branding, identity, adapter) {
|
|
113
|
+
if (typeof branding.displayName === 'string' && branding.displayName.trim()) {
|
|
114
|
+
return branding.displayName.trim();
|
|
115
|
+
}
|
|
116
|
+
return (
|
|
117
|
+
identity.displayName ||
|
|
118
|
+
adapter?.nameForHuman ||
|
|
119
|
+
adapter?.displayName ||
|
|
120
|
+
identity.systemKey ||
|
|
121
|
+
RUNTIME_ORCHESTRATION_SYSTEM_KEY
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @param {Object} branding
|
|
127
|
+
* @param {Object} identity
|
|
128
|
+
* @param {Object|null} adapter
|
|
129
|
+
* @param {Object} discovery
|
|
130
|
+
* @returns {string}
|
|
131
|
+
*/
|
|
132
|
+
function resolvePackageDescription(branding, identity, adapter, discovery) {
|
|
133
|
+
if (typeof branding.description === 'string' && branding.description.trim()) {
|
|
134
|
+
return branding.description.trim();
|
|
135
|
+
}
|
|
136
|
+
return (
|
|
137
|
+
identity.description ||
|
|
138
|
+
adapter?.descriptionForHuman ||
|
|
139
|
+
adapter?.description ||
|
|
140
|
+
discovery.operatingModel?.summary ||
|
|
141
|
+
''
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @param {Object|null} auth
|
|
147
|
+
* @returns {Object|null}
|
|
148
|
+
*/
|
|
149
|
+
function mapAdapterAuthentication(auth) {
|
|
150
|
+
if (!auth) return null;
|
|
151
|
+
return {
|
|
152
|
+
recommendedCapability: auth.recommendedCapability || null,
|
|
153
|
+
issuer: auth.issuer || null,
|
|
154
|
+
openidConfigurationUrl: auth.openidConfigurationUrl || null,
|
|
155
|
+
authorizationServerMetadataUrl: auth.authorizationServerMetadataUrl || null,
|
|
156
|
+
protectedResourceMetadataUrl: auth.protectedResourceMetadataUrl || null,
|
|
157
|
+
resource: auth.resource || auth.apiResource || null,
|
|
158
|
+
audience: auth.audience !== undefined && auth.audience !== null ? auth.audience : null,
|
|
159
|
+
scopes: Array.isArray(auth.scopes)
|
|
160
|
+
? [...auth.scopes]
|
|
161
|
+
: Array.isArray(auth.allowedScopes)
|
|
162
|
+
? [...auth.allowedScopes]
|
|
163
|
+
: [],
|
|
164
|
+
operatorNote: auth.operatorNote || null,
|
|
165
|
+
pkceRequired: auth.pkceRequired === true,
|
|
166
|
+
clientRegistrationModes: Array.isArray(auth.clientRegistrationModes)
|
|
167
|
+
? [...auth.clientRegistrationModes]
|
|
168
|
+
: []
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @param {Object} branding
|
|
174
|
+
* @returns {boolean}
|
|
175
|
+
*/
|
|
176
|
+
function isBrandingApplied(branding) {
|
|
177
|
+
return Boolean(
|
|
178
|
+
(branding.displayName && String(branding.displayName).trim()) ||
|
|
179
|
+
(branding.description && String(branding.description).trim())
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Map discovery (+ optional adapter) into packaging fields.
|
|
185
|
+
*
|
|
186
|
+
* @param {Object} params
|
|
187
|
+
* @param {Object} params.discovery
|
|
188
|
+
* @param {Object} [params.adapter]
|
|
189
|
+
* @param {string} params.dataplaneBaseUrl
|
|
190
|
+
* @param {Object} [params.branding]
|
|
191
|
+
* @returns {Object}
|
|
192
|
+
*/
|
|
193
|
+
function mapDiscoveryToPackageContext(params) {
|
|
194
|
+
const discovery = params.discovery;
|
|
195
|
+
if (!discovery || typeof discovery !== 'object') {
|
|
196
|
+
throw new Error('discovery document is required');
|
|
197
|
+
}
|
|
198
|
+
const identity = discovery.identity || {};
|
|
199
|
+
const branding = params.branding && typeof params.branding === 'object' ? params.branding : {};
|
|
200
|
+
const adapter = params.adapter || null;
|
|
201
|
+
const connection = resolveMcpConnection(discovery, adapter, params.dataplaneBaseUrl);
|
|
202
|
+
const authCaps = Array.isArray(discovery.authenticationCapabilities)
|
|
203
|
+
? discovery.authenticationCapabilities.map((c) => c.capability || c).filter(Boolean)
|
|
204
|
+
: [];
|
|
205
|
+
return {
|
|
206
|
+
systemKey: identity.systemKey || RUNTIME_ORCHESTRATION_SYSTEM_KEY,
|
|
207
|
+
displayName: resolvePackageDisplayName(branding, identity, adapter),
|
|
208
|
+
description: resolvePackageDescription(branding, identity, adapter, discovery),
|
|
209
|
+
governedToolNames: extractGovernedToolNames(discovery),
|
|
210
|
+
mcpServerUrl: connection.mcpServerUrl,
|
|
211
|
+
installReady: connection.installReady,
|
|
212
|
+
blocker: connection.blocker,
|
|
213
|
+
authentication: mapAdapterAuthentication(adapter?.authentication || null),
|
|
214
|
+
authenticationCapabilities: authCaps,
|
|
215
|
+
publication: {
|
|
216
|
+
mcpReady: discovery.publication?.mcpReady === true,
|
|
217
|
+
environment: discovery.publication?.environment || null
|
|
218
|
+
},
|
|
219
|
+
brandingApplied: isBrandingApplied(branding)
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* @param {Object} response
|
|
225
|
+
* @returns {Object}
|
|
226
|
+
*/
|
|
227
|
+
function unwrapApiData(response) {
|
|
228
|
+
if (response && typeof response === 'object' && response.data && typeof response.data === 'object') {
|
|
229
|
+
if (response.data.kind || response.data.identity || response.data.adapter) {
|
|
230
|
+
return response.data;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return response;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @param {string} dataplaneBaseUrl
|
|
238
|
+
* @param {Object} authConfig
|
|
239
|
+
* @param {string} [systemKey]
|
|
240
|
+
* @returns {Promise<Object>}
|
|
241
|
+
*/
|
|
242
|
+
async function fetchExternalAiDiscovery(
|
|
243
|
+
dataplaneBaseUrl,
|
|
244
|
+
authConfig,
|
|
245
|
+
systemKey = RUNTIME_ORCHESTRATION_SYSTEM_KEY
|
|
246
|
+
) {
|
|
247
|
+
const client = new ApiClient(normalizeBaseUrl(dataplaneBaseUrl), authConfig || {});
|
|
248
|
+
const response = await client.get(`/api/v1/external/systems/${systemKey}/discovery`);
|
|
249
|
+
const discovery = unwrapApiData(response);
|
|
250
|
+
if (!discovery || !discovery.kind) {
|
|
251
|
+
throw new Error(`Discovery document missing for systemKey=${systemKey}`);
|
|
252
|
+
}
|
|
253
|
+
return discovery;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* @param {string} dataplaneBaseUrl
|
|
258
|
+
* @param {Object} authConfig
|
|
259
|
+
* @param {string} adapterKey microsoft-plugin | chatgpt-setup
|
|
260
|
+
* @param {string} [systemKey]
|
|
261
|
+
* @returns {Promise<Object>}
|
|
262
|
+
*/
|
|
263
|
+
async function fetchExternalAiDiscoveryAdapter(
|
|
264
|
+
dataplaneBaseUrl,
|
|
265
|
+
authConfig,
|
|
266
|
+
adapterKey,
|
|
267
|
+
systemKey = RUNTIME_ORCHESTRATION_SYSTEM_KEY
|
|
268
|
+
) {
|
|
269
|
+
const client = new ApiClient(normalizeBaseUrl(dataplaneBaseUrl), authConfig || {});
|
|
270
|
+
const response = await client.get(
|
|
271
|
+
`/api/v1/external/systems/${systemKey}/discovery/adapters/${adapterKey}`
|
|
272
|
+
);
|
|
273
|
+
return unwrapApiData(response);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @param {string} channelTarget
|
|
278
|
+
* @returns {string|null}
|
|
279
|
+
*/
|
|
280
|
+
function adapterKeyForChannel(channelTarget) {
|
|
281
|
+
if (channelTarget === 'microsoft-copilot') return 'microsoft-plugin';
|
|
282
|
+
if (channelTarget === 'chatgpt') return 'chatgpt-setup';
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Fetch discovery (+ adapter when known) and map package context.
|
|
288
|
+
*
|
|
289
|
+
* @param {Object} params
|
|
290
|
+
* @returns {Promise<Object>}
|
|
291
|
+
*/
|
|
292
|
+
async function loadChannelPackageContext(params) {
|
|
293
|
+
const systemKey = params.runtimeSystemKey || RUNTIME_ORCHESTRATION_SYSTEM_KEY;
|
|
294
|
+
const discovery = await fetchExternalAiDiscovery(
|
|
295
|
+
params.dataplaneBaseUrl,
|
|
296
|
+
params.authConfig,
|
|
297
|
+
systemKey
|
|
298
|
+
);
|
|
299
|
+
const adapterKey = adapterKeyForChannel(params.channelTarget);
|
|
300
|
+
let adapter = null;
|
|
301
|
+
if (adapterKey) {
|
|
302
|
+
try {
|
|
303
|
+
adapter = await fetchExternalAiDiscoveryAdapter(
|
|
304
|
+
params.dataplaneBaseUrl,
|
|
305
|
+
params.authConfig,
|
|
306
|
+
adapterKey,
|
|
307
|
+
systemKey
|
|
308
|
+
);
|
|
309
|
+
} catch {
|
|
310
|
+
adapter = null;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return mapDiscoveryToPackageContext({
|
|
314
|
+
discovery,
|
|
315
|
+
adapter,
|
|
316
|
+
dataplaneBaseUrl: params.dataplaneBaseUrl,
|
|
317
|
+
branding: params.branding
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
module.exports = {
|
|
322
|
+
GATEWAY_TOOLS_CATALOG_MARKER,
|
|
323
|
+
isGatewayToolsCatalogUrl,
|
|
324
|
+
resolveDiscoveryUrl,
|
|
325
|
+
mapDiscoveryToPackageContext,
|
|
326
|
+
fetchExternalAiDiscovery,
|
|
327
|
+
fetchExternalAiDiscoveryAdapter,
|
|
328
|
+
adapterKeyForChannel,
|
|
329
|
+
loadChannelPackageContext
|
|
330
|
+
};
|
|
@@ -72,7 +72,7 @@ async function fetchCommunicationSystem(ctx) {
|
|
|
72
72
|
if (!system || typeof system !== 'object') {
|
|
73
73
|
throw new Error(
|
|
74
74
|
`Communication external system "${ctx.channelTarget}" was not found. ` +
|
|
75
|
-
`Run: aifabrix
|
|
75
|
+
`Run: aifabrix channel add ${ctx.channelTarget} --name "…" --force`
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
78
|
const kind = system.systemKind || system.systemKindKey;
|
|
@@ -19,6 +19,12 @@ const {
|
|
|
19
19
|
COPILOT_RUNTIME_TRANSPORT,
|
|
20
20
|
COPILOT_RUNTIME_AUTH_MODE
|
|
21
21
|
} = require('./chatgpt-runtime-contract');
|
|
22
|
+
const { applyMicrosoftCopilotDisplayLimits } = require('./microsoft-copilot-limits');
|
|
23
|
+
const {
|
|
24
|
+
resolveCopilotEntraScopes,
|
|
25
|
+
evaluateCopilotEntraSsoReadiness
|
|
26
|
+
} = require('./microsoft-copilot-entra-sso');
|
|
27
|
+
const { applyCopilotEntraSsoFormFields } = require('./microsoft-copilot-entra-sso-form');
|
|
22
28
|
|
|
23
29
|
/** Default Slack slash prefix — matches dataplane `templates/slack/command_catalog.yaml`. */
|
|
24
30
|
const DEFAULT_SLACK_COMMAND_PREFIX = 'af';
|
|
@@ -152,15 +158,62 @@ function buildDeterministicGuid(seed) {
|
|
|
152
158
|
* @param {Object} resolved
|
|
153
159
|
* @param {Object} params
|
|
154
160
|
*/
|
|
155
|
-
|
|
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
|
|
|
@@ -113,10 +113,27 @@ function getDefaultChannelDescription() {
|
|
|
113
113
|
);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Human-readable supported channel list for CLI help and error hints.
|
|
118
|
+
* @returns {string}
|
|
119
|
+
*/
|
|
120
|
+
function formatSupportedChannelTargetsHelp() {
|
|
121
|
+
const stable = CHANNEL_TARGET_KEYS.filter((key) => !CHANNEL_TARGETS[key].experimental);
|
|
122
|
+
const experimental = CHANNEL_TARGET_KEYS.filter((key) => CHANNEL_TARGETS[key].experimental);
|
|
123
|
+
const lines = ['Supported channels:', ` ${stable.join(', ')}`];
|
|
124
|
+
if (experimental.length > 0) {
|
|
125
|
+
lines.push(
|
|
126
|
+
` ${experimental.join(', ')} (experimental — scaffold with --allow-experimental)`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return lines.join('\n');
|
|
130
|
+
}
|
|
131
|
+
|
|
116
132
|
module.exports = {
|
|
117
133
|
CHANNEL_TARGETS,
|
|
118
134
|
CHANNEL_TARGET_KEYS,
|
|
119
135
|
isChannelTargetKey,
|
|
120
136
|
getChannelTarget,
|
|
121
|
-
getDefaultChannelDescription
|
|
137
|
+
getDefaultChannelDescription,
|
|
138
|
+
formatSupportedChannelTargetsHelp
|
|
122
139
|
};
|
|
@@ -12,7 +12,7 @@ const RUNTIME_ORCHESTRATION_SYSTEM_KEY = 'runtime-orchestration';
|
|
|
12
12
|
|
|
13
13
|
const CHATGPT_RUNTIME_SYSTEM_KEY = RUNTIME_ORCHESTRATION_SYSTEM_KEY;
|
|
14
14
|
const CHATGPT_RUNTIME_TRANSPORT = 'streamable-http';
|
|
15
|
-
const CHATGPT_RUNTIME_AUTH_MODE = '
|
|
15
|
+
const CHATGPT_RUNTIME_AUTH_MODE = 'oauth21-authorization-code-pkce-s256';
|
|
16
16
|
|
|
17
17
|
const COPILOT_RUNTIME_SYSTEM_KEY = RUNTIME_ORCHESTRATION_SYSTEM_KEY;
|
|
18
18
|
const COPILOT_RUNTIME_TRANSPORT = 'streamable-http';
|