@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
|
@@ -67,6 +67,11 @@ const ISOLATED_SUITE_ENTRIES = [
|
|
|
67
67
|
['role-assistant-knowledge-sync', ['**/tests/lib/role-assistant/knowledge-sync.test.js']],
|
|
68
68
|
['role-assistant-evidence-sync', ['**/tests/lib/role-assistant/evidence-sync.test.js']],
|
|
69
69
|
['role-assistant-test-cases', ['**/tests/lib/role-assistant/test-cases.test.js']],
|
|
70
|
+
['role-assistant-test-suite', ['**/tests/lib/role-assistant/test-suite.test.js']],
|
|
71
|
+
[
|
|
72
|
+
'role-assistant-workhub-answers',
|
|
73
|
+
['**/tests/lib/role-assistant/test-runner-workhub-answers.test.js']
|
|
74
|
+
],
|
|
70
75
|
['governance-fixture-subjects', ['**/tests/lib/commands/governance-fixture-subjects.test.js']],
|
|
71
76
|
['enterprise-knowledge-generate-manifest', ['**/tests/lib/commands/enterprise-knowledge-generate-manifest.test.js']],
|
|
72
77
|
['plan-prevalidate-gates', ['**/tests/lib/plan-prevalidate-gates.test.js']],
|
|
@@ -152,7 +157,19 @@ const ISOLATED_SUITE_ENTRIES = [
|
|
|
152
157
|
[
|
|
153
158
|
'**/tests/lib/channels/channel-integration-generator.test.js',
|
|
154
159
|
'**/tests/lib/channels/channel-artifact-generator.test.js',
|
|
155
|
-
'**/tests/lib/channels/channel-integration-template.test.js'
|
|
160
|
+
'**/tests/lib/channels/channel-integration-template.test.js',
|
|
161
|
+
'**/tests/lib/channels/add-channel.test.js',
|
|
162
|
+
'**/tests/lib/channels/microsoft-copilot-package-quality.test.js',
|
|
163
|
+
'**/tests/lib/channels/channel-branding.test.js'
|
|
164
|
+
]
|
|
165
|
+
],
|
|
166
|
+
[
|
|
167
|
+
'integration-definition-real-fs',
|
|
168
|
+
[
|
|
169
|
+
'**/tests/lib/validation/integration-definition-schema.test.js',
|
|
170
|
+
'**/tests/lib/integration-definition/apply.test.js',
|
|
171
|
+
'**/tests/lib/integration-definition/from-discovery.test.js',
|
|
172
|
+
'**/tests/lib/integration-definition/emit-suite-bid.test.js'
|
|
156
173
|
]
|
|
157
174
|
],
|
|
158
175
|
[
|
|
@@ -175,6 +192,10 @@ const ISOLATED_SUITE_ENTRIES = [
|
|
|
175
192
|
'**/tests/lib/scripts/sync-builder-api-template.test.js',
|
|
176
193
|
'**/tests/lib/evidence/evidence-service.test.js'
|
|
177
194
|
]
|
|
195
|
+
],
|
|
196
|
+
[
|
|
197
|
+
'enrich-openapi-operations',
|
|
198
|
+
['**/tests/lib/integration-definition/enrich-openapi-operations.test.js']
|
|
178
199
|
]
|
|
179
200
|
];
|
|
180
201
|
|
|
@@ -224,7 +245,9 @@ function buildDefaultTestPathIgnorePatterns(includeLocalTests) {
|
|
|
224
245
|
'/tests/integration/',
|
|
225
246
|
'\\\\tests\\\\integration\\\\',
|
|
226
247
|
'/tests/manual/',
|
|
227
|
-
'\\\\tests\\\\manual\\\\'
|
|
248
|
+
'\\\\tests\\\\manual\\\\',
|
|
249
|
+
'/builder/',
|
|
250
|
+
'\\\\builder\\\\'
|
|
228
251
|
];
|
|
229
252
|
const globs = [
|
|
230
253
|
...ISOLATED_SUITE_ENTRIES.flatMap((entry) => entry[1]),
|
package/jest.projects.js
CHANGED
|
@@ -52,7 +52,10 @@ const defaultProject = {
|
|
|
52
52
|
],
|
|
53
53
|
testPathIgnorePatterns: buildDefaultTestPathIgnorePatterns(),
|
|
54
54
|
modulePathIgnorePatterns: [
|
|
55
|
-
'<rootDir>/docker-builder-api-staging/'
|
|
55
|
+
'<rootDir>/docker-builder-api-staging/',
|
|
56
|
+
// Generated app dirs under builder/ (except builder-api) — avoid haste/transform
|
|
57
|
+
// picking up junk written by generator/cli suites (e.g. builder/testapp).
|
|
58
|
+
'<rootDir>/builder/(?!builder-api(?:/|$))'
|
|
56
59
|
],
|
|
57
60
|
setupFiles: ['<rootDir>/tests/capture-real-fs.js'],
|
|
58
61
|
setupFilesAfterEnv: ['<rootDir>/tests/setup.js'],
|
|
@@ -69,28 +69,28 @@ async function detachPolicy(dataplaneUrl, sourceIdOrKey, policyIdOrKey, authConf
|
|
|
69
69
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
70
70
|
return await client.delete(`/api/v1/external/${sourceIdOrKey}/policies/${policyIdOrKey}`);
|
|
71
71
|
}
|
|
72
|
-
/** @requiresPermission {Dataplane} external-
|
|
72
|
+
/** @requiresPermission {Dataplane} external-data-source:read (sync under /data-storage) */
|
|
73
73
|
async function listSyncJobs(dataplaneUrl, sourceIdOrKey, authConfig, options = {}) {
|
|
74
74
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
75
|
-
return await client.get(`/api/v1/
|
|
75
|
+
return await client.get(`/api/v1/data-storage/${sourceIdOrKey}/sync`, { params: options });
|
|
76
76
|
}
|
|
77
77
|
async function createSyncJob(dataplaneUrl, sourceIdOrKey, authConfig, syncData) {
|
|
78
78
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
79
|
-
return await client.post(`/api/v1/
|
|
79
|
+
return await client.post(`/api/v1/data-storage/${sourceIdOrKey}/sync`, { body: syncData });
|
|
80
80
|
}
|
|
81
|
-
/** @requiresPermission {Dataplane} external-
|
|
81
|
+
/** @requiresPermission {Dataplane} external-data-source:read */
|
|
82
82
|
async function getSyncJob(dataplaneUrl, sourceIdOrKey, syncJobId, authConfig) {
|
|
83
83
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
84
|
-
return await client.get(`/api/v1/
|
|
84
|
+
return await client.get(`/api/v1/data-storage/${sourceIdOrKey}/sync/${syncJobId}`);
|
|
85
85
|
}
|
|
86
|
-
/** @requiresPermission {Dataplane} external-
|
|
86
|
+
/** @requiresPermission {Dataplane} external-data-source:sync */
|
|
87
87
|
async function updateSyncJob(dataplaneUrl, sourceIdOrKey, syncJobId, authConfig, updateData) {
|
|
88
88
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
89
|
-
return await client.put(`/api/v1/
|
|
89
|
+
return await client.put(`/api/v1/data-storage/${sourceIdOrKey}/sync/${syncJobId}`, { body: updateData });
|
|
90
90
|
}
|
|
91
91
|
async function executeSyncJob(dataplaneUrl, sourceIdOrKey, syncJobId, authConfig) {
|
|
92
92
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
93
|
-
return await client.post(`/api/v1/
|
|
93
|
+
return await client.post(`/api/v1/data-storage/${sourceIdOrKey}/sync/${syncJobId}/execute`);
|
|
94
94
|
}
|
|
95
95
|
/** @requiresPermission {Dataplane} external-data-source:sync */
|
|
96
96
|
async function validateDocuments(dataplaneUrl, sourceIdOrKey, authConfig, validateData) {
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Keycloak status API (Controller) — Entra IdP ids for packaging
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { ApiClient } = require('./index');
|
|
10
|
+
|
|
11
|
+
const KEYCLOAK_STATUS_PATH = '/api/v1/keycloak/status';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} response
|
|
15
|
+
* @returns {import('./types/keycloak.types').KeycloakStatus|null}
|
|
16
|
+
*/
|
|
17
|
+
function extractKeycloakStatus(response) {
|
|
18
|
+
if (!response || typeof response !== 'object' || response.success === false) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const body = response.data ?? response;
|
|
22
|
+
const data = body?.data !== undefined ? body.data : body;
|
|
23
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return data;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {unknown} value
|
|
31
|
+
* @returns {string|null}
|
|
32
|
+
*/
|
|
33
|
+
function trimNonEmptyString(value) {
|
|
34
|
+
if (typeof value !== 'string') {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const trimmed = value.trim();
|
|
38
|
+
return trimmed || null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {import('./types/keycloak.types').KeycloakStatus|null} status
|
|
43
|
+
* @returns {import('./types/keycloak.types').KeycloakEntraIdentityProvider|null}
|
|
44
|
+
*/
|
|
45
|
+
function extractEntraIdentityProvider(status) {
|
|
46
|
+
const raw = status?.entraIdentityProvider;
|
|
47
|
+
if (!raw || typeof raw !== 'object') {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const enterpriseAppId = trimNonEmptyString(raw.enterpriseAppId);
|
|
51
|
+
if (!enterpriseAppId) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const tenantId = trimNonEmptyString(raw.tenantId);
|
|
55
|
+
return tenantId ? { enterpriseAppId, tenantId } : { enterpriseAppId };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* GET /api/v1/keycloak/status
|
|
60
|
+
* @requiresPermission {Controller} admin:keycloak
|
|
61
|
+
* @async
|
|
62
|
+
* @param {string} controllerUrl - Miso Controller base URL
|
|
63
|
+
* @param {Object} authConfig - Authentication configuration (device Bearer)
|
|
64
|
+
* @returns {Promise<import('./types/keycloak.types').KeycloakStatus|null>}
|
|
65
|
+
*/
|
|
66
|
+
async function getKeycloakStatus(controllerUrl, authConfig) {
|
|
67
|
+
const client = new ApiClient(controllerUrl, authConfig);
|
|
68
|
+
const response = await client.get(KEYCLOAK_STATUS_PATH);
|
|
69
|
+
return extractKeycloakStatus(response);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Resolve Entra SSO enterprise app id from Keycloak status, or null when missing.
|
|
74
|
+
* Does not invent ids.
|
|
75
|
+
* @requiresPermission {Controller} admin:keycloak
|
|
76
|
+
* @async
|
|
77
|
+
* @param {string} controllerUrl - Miso Controller base URL
|
|
78
|
+
* @param {Object} authConfig - Authentication configuration
|
|
79
|
+
* @returns {Promise<import('./types/keycloak.types').KeycloakEntraIdentityProvider|null>}
|
|
80
|
+
*/
|
|
81
|
+
async function getKeycloakEntraIdentityProvider(controllerUrl, authConfig) {
|
|
82
|
+
const status = await getKeycloakStatus(controllerUrl, authConfig);
|
|
83
|
+
return extractEntraIdentityProvider(status);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
KEYCLOAK_STATUS_PATH,
|
|
88
|
+
getKeycloakStatus,
|
|
89
|
+
getKeycloakEntraIdentityProvider,
|
|
90
|
+
extractKeycloakStatus,
|
|
91
|
+
extractEntraIdentityProvider
|
|
92
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Admin token providers API (Controller Trust sources)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { ApiClient } = require('./index');
|
|
10
|
+
|
|
11
|
+
const ADMIN_BASE = '/api/v1/admin/token-providers';
|
|
12
|
+
const MICROSOFT_ENTRA_ID_KEY = 'microsoft-entra-id';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {Object} response
|
|
16
|
+
* @returns {import('./types/token-providers.types').TokenProvider[]}
|
|
17
|
+
*/
|
|
18
|
+
function extractTokenProviders(response) {
|
|
19
|
+
const body = response?.data ?? response;
|
|
20
|
+
const data = body?.data ?? body;
|
|
21
|
+
if (Array.isArray(data)) {
|
|
22
|
+
return data;
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(data?.items)) {
|
|
25
|
+
return data.items;
|
|
26
|
+
}
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* List admin token providers (paginated).
|
|
32
|
+
* GET /api/v1/admin/token-providers
|
|
33
|
+
* @requiresPermission {Controller} token-provider:read
|
|
34
|
+
* @async
|
|
35
|
+
* @param {string} controllerUrl - Miso Controller base URL
|
|
36
|
+
* @param {Object} authConfig - Authentication configuration (device Bearer)
|
|
37
|
+
* @param {import('./types/token-providers.types').ListAdminTokenProvidersOptions} options
|
|
38
|
+
* @returns {Promise<Object>} Controller list response
|
|
39
|
+
* @throws {Error} If direction is missing or request fails
|
|
40
|
+
*/
|
|
41
|
+
async function listAdminTokenProviders(controllerUrl, authConfig, options = {}) {
|
|
42
|
+
const direction = options?.direction;
|
|
43
|
+
if (direction !== 'inbound' && direction !== 'outbound') {
|
|
44
|
+
throw new Error('direction is required and must be "inbound" or "outbound"');
|
|
45
|
+
}
|
|
46
|
+
const client = new ApiClient(controllerUrl, authConfig);
|
|
47
|
+
const params = { ...options, direction };
|
|
48
|
+
return await client.get(ADMIN_BASE, { params });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolve inbound Microsoft Entra ID Trust source, or null when missing.
|
|
53
|
+
* Does not invent issuer, audience, or scopes.
|
|
54
|
+
* @requiresPermission {Controller} token-provider:read
|
|
55
|
+
* @async
|
|
56
|
+
* @param {string} controllerUrl - Miso Controller base URL
|
|
57
|
+
* @param {Object} authConfig - Authentication configuration
|
|
58
|
+
* @returns {Promise<import('./types/token-providers.types').TokenProvider|null>}
|
|
59
|
+
*/
|
|
60
|
+
async function getInboundMicrosoftEntraIdProvider(controllerUrl, authConfig) {
|
|
61
|
+
const response = await listAdminTokenProviders(controllerUrl, authConfig, {
|
|
62
|
+
direction: 'inbound',
|
|
63
|
+
search: MICROSOFT_ENTRA_ID_KEY,
|
|
64
|
+
page: 1,
|
|
65
|
+
pageSize: 50
|
|
66
|
+
});
|
|
67
|
+
const items = extractTokenProviders(response);
|
|
68
|
+
const exact = items.find((item) => item && item.key === MICROSOFT_ENTRA_ID_KEY);
|
|
69
|
+
return exact || null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
listAdminTokenProviders,
|
|
74
|
+
getInboundMicrosoftEntraIdProvider,
|
|
75
|
+
extractTokenProviders,
|
|
76
|
+
MICROSOFT_ENTRA_ID_KEY
|
|
77
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Keycloak status API type definitions (Controller)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Entra identity provider fragment from Keycloak status.
|
|
9
|
+
* @typedef {Object} KeycloakEntraIdentityProvider
|
|
10
|
+
* @property {string} enterpriseAppId - Entra SSO application (client) id
|
|
11
|
+
* @property {string} [tenantId] - Entra tenant id when known
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Keycloak configuration status payload (`data` of GET /api/v1/keycloak/status).
|
|
16
|
+
* @typedef {Object} KeycloakStatus
|
|
17
|
+
* @property {string} [realm]
|
|
18
|
+
* @property {string} [status]
|
|
19
|
+
* @property {string} [controllerClientId]
|
|
20
|
+
* @property {KeycloakEntraIdentityProvider} [entraIdentityProvider]
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
module.exports = {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Token provider API type definitions (Controller admin Trust sources)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Token provider direction
|
|
9
|
+
* @typedef {'inbound'|'outbound'} TokenProviderDirection
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Token provider subject type
|
|
14
|
+
* @typedef {'user'|'service'|'both'} TokenProviderSubjectType
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Allowed operation on a token provider
|
|
19
|
+
* @typedef {'mcp.invoke'|'dataplane.read'|'dataplane.write'} TokenProviderAllowedOperation
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Admin token provider list item (Controller TokenProvider schema)
|
|
24
|
+
* @typedef {Object} TokenProvider
|
|
25
|
+
* @property {string} id - Unique identifier (cuid)
|
|
26
|
+
* @property {string} key - Unique slug (e.g. microsoft-entra-id)
|
|
27
|
+
* @property {string} displayName - Human-readable name
|
|
28
|
+
* @property {string} issuer - JWT issuer URL (iss claim)
|
|
29
|
+
* @property {string} jwksUri - JWKS endpoint for key verification
|
|
30
|
+
* @property {string|null} [audience] - Expected audience claim (may be null)
|
|
31
|
+
* @property {TokenProviderSubjectType} subjectType - Subject type
|
|
32
|
+
* @property {TokenProviderDirection} direction - inbound or outbound
|
|
33
|
+
* @property {string|null} [identityClaim] - Claim used for user identity
|
|
34
|
+
* @property {string|null} [emailClaim] - Claim used for email
|
|
35
|
+
* @property {boolean} [autoProvision] - Whether to auto-create users
|
|
36
|
+
* @property {string|null} [serviceUserKey] - Service user key for service flow
|
|
37
|
+
* @property {TokenProviderAllowedOperation[]} [allowedOperations] - Allowed operations
|
|
38
|
+
* @property {string[]} [allowedScopes] - OAuth scopes (may be empty)
|
|
39
|
+
* @property {boolean} enabled - Whether this provider is enabled
|
|
40
|
+
* @property {string} [createdAt] - Created at
|
|
41
|
+
* @property {string} [updatedAt] - Updated at
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* List admin token providers query options
|
|
46
|
+
* @typedef {Object} ListAdminTokenProvidersOptions
|
|
47
|
+
* @property {TokenProviderDirection} direction - Required by OpenAPI
|
|
48
|
+
* @property {string} [search] - Search across key, displayName, issuer
|
|
49
|
+
* @property {number} [page] - Page number
|
|
50
|
+
* @property {number} [pageSize] - Page size
|
|
51
|
+
* @property {string} [sort] - Sort expression
|
|
52
|
+
* @property {string} [filter] - Filter expression
|
|
53
|
+
* @property {string} [enabled] - 'true' | 'false'
|
|
54
|
+
* @property {TokenProviderSubjectType} [subjectType] - Subject type filter
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Paginated list response for admin token providers
|
|
59
|
+
* @typedef {Object} ListAdminTokenProvidersResponse
|
|
60
|
+
* @property {TokenProvider[]} data - Provider rows
|
|
61
|
+
* @property {Object} meta - Pagination meta
|
|
62
|
+
* @property {Object} [links] - Pagination links
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
module.exports = {};
|
package/lib/app/down.js
CHANGED
|
@@ -111,6 +111,20 @@ async function stopAppContainers(appName, companionKeys, developerId) {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Resolve developer id for downApp (optional override does not change config).
|
|
116
|
+
* @param {Object} options
|
|
117
|
+
* @returns {Promise<string>}
|
|
118
|
+
*/
|
|
119
|
+
async function resolveDownAppDeveloperId(options) {
|
|
120
|
+
const override = options && options.developerId;
|
|
121
|
+
if (override === undefined || override === null || override === '') {
|
|
122
|
+
return config.getDeveloperId();
|
|
123
|
+
}
|
|
124
|
+
const { validateAndNormalizeDeveloperId } = require('../core/config-normalize');
|
|
125
|
+
return validateAndNormalizeDeveloperId(override);
|
|
126
|
+
}
|
|
127
|
+
|
|
114
128
|
/**
|
|
115
129
|
* Stops and removes a specific application's container.
|
|
116
130
|
* If --volumes is passed, attempts to remove the app's named Docker volume.
|
|
@@ -122,6 +136,7 @@ async function stopAppContainers(appName, companionKeys, developerId) {
|
|
|
122
136
|
* @param {string} appName - Application name
|
|
123
137
|
* @param {Object} options - Options
|
|
124
138
|
* @param {boolean} [options.volumes=false] - Remove Docker volume data
|
|
139
|
+
* @param {string|number} [options.developerId] - Target developer for this stop only
|
|
125
140
|
* @returns {Promise<void>} Resolves when operation completes
|
|
126
141
|
* @throws {Error} If validation fails or Docker operations error (other than missing volume)
|
|
127
142
|
*
|
|
@@ -136,7 +151,7 @@ async function downApp(appName, options = {}) {
|
|
|
136
151
|
validateAppName(appName);
|
|
137
152
|
|
|
138
153
|
const volumes = !!options.volumes;
|
|
139
|
-
const developerId = await
|
|
154
|
+
const developerId = await resolveDownAppDeveloperId(options);
|
|
140
155
|
const appConfig = loadBuilderApplicationConfig(appName);
|
|
141
156
|
const companionKeys = getCeleryCompanionAppKeys(appName, appConfig);
|
|
142
157
|
|
|
@@ -10,9 +10,15 @@
|
|
|
10
10
|
|
|
11
11
|
const { getChannelTarget, getDefaultChannelDescription } = require('./channel-targets');
|
|
12
12
|
const { resolveAppDescription } = require('./channel-description');
|
|
13
|
+
const path = require('path');
|
|
13
14
|
const { resolveChannelRuntimeValues } = require('./channel-runtime-resolver');
|
|
14
15
|
const { generateChannelArtifact } = require('./channel-artifact-generator');
|
|
15
16
|
const { fetchRuntimeLayerBToolNames } = require('./runtime-tool-catalog');
|
|
17
|
+
const { loadChannelPackageContext } = require('./channel-discovery');
|
|
18
|
+
const {
|
|
19
|
+
readChannelBranding,
|
|
20
|
+
mergeChannelBranding
|
|
21
|
+
} = require('./channel-branding');
|
|
16
22
|
const {
|
|
17
23
|
getExistingChannelExtension,
|
|
18
24
|
buildChannelExtensionBlock,
|
|
@@ -39,9 +45,14 @@ const {
|
|
|
39
45
|
} = require('./channel-display-name');
|
|
40
46
|
const {
|
|
41
47
|
displayExistingChannelStatus,
|
|
42
|
-
displayChannelInstallResult
|
|
43
|
-
displayChannelError
|
|
48
|
+
displayChannelInstallResult
|
|
44
49
|
} = require('./channel-status-display');
|
|
50
|
+
const { ensureChannelReadyForAdd } = require('./channel-add-ensure');
|
|
51
|
+
const {
|
|
52
|
+
assertMicrosoftCopilotOauthReference,
|
|
53
|
+
assertMicrosoftCopilotOauthReferenceBeforeForce,
|
|
54
|
+
loadMicrosoftCopilotEntraContext
|
|
55
|
+
} = require('./microsoft-copilot-channel-add');
|
|
45
56
|
|
|
46
57
|
/**
|
|
47
58
|
* @param {typeof import('./channel-targets').CHANNEL_TARGETS[string]} target
|
|
@@ -252,6 +263,9 @@ async function completeLiveInstall(params) {
|
|
|
252
263
|
capabilities: target.capabilities,
|
|
253
264
|
artifactPath: artifact.outputPath,
|
|
254
265
|
installGuidePath: artifact.installPath,
|
|
266
|
+
zipPath: artifact.zipPath,
|
|
267
|
+
installReady: artifact.installReady,
|
|
268
|
+
blocker: artifact.blocker,
|
|
255
269
|
validation: toValidationDisplay(validation),
|
|
256
270
|
approvalGuide
|
|
257
271
|
});
|
|
@@ -272,7 +286,10 @@ function finishDryRunInstall(params) {
|
|
|
272
286
|
credentialKey: params.target.credentialKey,
|
|
273
287
|
capabilities: params.target.capabilities,
|
|
274
288
|
artifactPath: params.artifact.outputPath,
|
|
275
|
-
installGuidePath: params.artifact.installPath
|
|
289
|
+
installGuidePath: params.artifact.installPath,
|
|
290
|
+
zipPath: params.artifact.zipPath,
|
|
291
|
+
installReady: params.artifact.installReady,
|
|
292
|
+
blocker: params.artifact.blocker
|
|
276
293
|
});
|
|
277
294
|
return { dryRun: true, channelBlock: params.channelBlock, artifact: params.artifact };
|
|
278
295
|
}
|
|
@@ -281,12 +298,78 @@ function finishDryRunInstall(params) {
|
|
|
281
298
|
* @param {Object} params
|
|
282
299
|
* @returns {Promise<{ artifact: Object, channelBlock: Object, status: string }>}
|
|
283
300
|
*/
|
|
301
|
+
/**
|
|
302
|
+
* @param {Object|null} packageContext
|
|
303
|
+
* @param {Object} options
|
|
304
|
+
* @param {'iconColorPath'|'iconOutlinePath'} key
|
|
305
|
+
* @returns {string|undefined}
|
|
306
|
+
*/
|
|
307
|
+
function brandingIconOrOption(packageContext, options, key) {
|
|
308
|
+
if (typeof options[key] === 'string' && options[key].trim()) {
|
|
309
|
+
return options[key].trim();
|
|
310
|
+
}
|
|
311
|
+
if (packageContext && typeof packageContext[key] === 'string' && packageContext[key].trim()) {
|
|
312
|
+
return packageContext[key].trim();
|
|
313
|
+
}
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async function resolvePackageContextForInstall(params) {
|
|
318
|
+
const { channelTarget, options, name, description, ctx } = params;
|
|
319
|
+
const packagingTargets = new Set(['chatgpt', 'microsoft-copilot', 'slack', 'teams-bot']);
|
|
320
|
+
if (!packagingTargets.has(channelTarget)) {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
324
|
+
const workspaceDir = path.join(projectRoot, 'integration', channelTarget);
|
|
325
|
+
const branding = mergeChannelBranding(readChannelBranding(workspaceDir), {
|
|
326
|
+
displayName: options.overrideDisplayName || undefined,
|
|
327
|
+
description: options.overrideDescription || undefined,
|
|
328
|
+
iconColorPath: options.iconColorPath || undefined,
|
|
329
|
+
iconOutlinePath: options.iconOutlinePath || undefined
|
|
330
|
+
});
|
|
331
|
+
let packageContext;
|
|
332
|
+
try {
|
|
333
|
+
packageContext = await loadChannelPackageContext({
|
|
334
|
+
dataplaneBaseUrl: ctx.dataplaneUrl,
|
|
335
|
+
authConfig: ctx.authConfig,
|
|
336
|
+
channelTarget,
|
|
337
|
+
branding
|
|
338
|
+
});
|
|
339
|
+
} catch {
|
|
340
|
+
packageContext = {
|
|
341
|
+
displayName: name,
|
|
342
|
+
description,
|
|
343
|
+
governedToolNames: [],
|
|
344
|
+
mcpServerUrl: null,
|
|
345
|
+
installReady: false,
|
|
346
|
+
blocker: 'discoveryUnavailable',
|
|
347
|
+
authentication: null,
|
|
348
|
+
authenticationCapabilities: []
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
...packageContext,
|
|
353
|
+
iconColorPath: branding.iconColorPath,
|
|
354
|
+
iconOutlinePath: branding.iconOutlinePath
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
284
358
|
async function buildInstallArtifacts(params) {
|
|
285
359
|
const { channelTarget, options, target, existing, name, baseName, description, ctx } = params;
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
360
|
+
const packageContext = await resolvePackageContextForInstall(params);
|
|
361
|
+
let runtimeToolNames = [];
|
|
362
|
+
if (channelTarget === 'chatgpt' || channelTarget === 'microsoft-copilot') {
|
|
363
|
+
runtimeToolNames =
|
|
364
|
+
packageContext?.governedToolNames?.length > 0
|
|
365
|
+
? packageContext.governedToolNames
|
|
366
|
+
: await fetchRuntimeLayerBToolNames(ctx.dataplaneUrl, ctx.authConfig, channelTarget);
|
|
367
|
+
}
|
|
368
|
+
let entraProvider = null;
|
|
369
|
+
let entraIdentityProvider = null;
|
|
370
|
+
if (channelTarget === 'microsoft-copilot') {
|
|
371
|
+
({ entraProvider, entraIdentityProvider } = await loadMicrosoftCopilotEntraContext(ctx));
|
|
372
|
+
}
|
|
290
373
|
const runtimeValues = resolveChannelRuntimeValues({
|
|
291
374
|
dataplaneBaseUrl: ctx.dataplaneUrl,
|
|
292
375
|
channelTarget,
|
|
@@ -296,7 +379,14 @@ async function buildInstallArtifacts(params) {
|
|
|
296
379
|
appDescription: description,
|
|
297
380
|
icon: options.icon,
|
|
298
381
|
commandPrefix: options.commandPrefix,
|
|
299
|
-
layerBToolNames: runtimeToolNames
|
|
382
|
+
layerBToolNames: runtimeToolNames,
|
|
383
|
+
packageContext,
|
|
384
|
+
entraProvider,
|
|
385
|
+
entraIdentityProvider,
|
|
386
|
+
oauthPluginVaultReferenceId: options.oauthPluginVaultReferenceId,
|
|
387
|
+
scopes: options.scopes,
|
|
388
|
+
iconColorPath: brandingIconOrOption(packageContext, options, 'iconColorPath'),
|
|
389
|
+
iconOutlinePath: brandingIconOrOption(packageContext, options, 'iconOutlinePath')
|
|
300
390
|
});
|
|
301
391
|
const projectRoot = options.projectRoot || process.cwd();
|
|
302
392
|
const artifact = generateArtifactWithAudit(channelTarget, {
|
|
@@ -313,17 +403,39 @@ async function buildInstallArtifacts(params) {
|
|
|
313
403
|
return { artifact, channelBlock, status };
|
|
314
404
|
}
|
|
315
405
|
|
|
406
|
+
/**
|
|
407
|
+
* @param {string} channelTarget
|
|
408
|
+
* @param {Object} options
|
|
409
|
+
* @returns {Object}
|
|
410
|
+
*/
|
|
411
|
+
function assertChannelAddOptions(channelTarget, options) {
|
|
412
|
+
const target = getChannelTarget(channelTarget);
|
|
413
|
+
if (target.experimental) {
|
|
414
|
+
throw new Error(
|
|
415
|
+
`Channel target "${channelTarget}" is experimental. Confirm product requirement before use.`
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
if (!options.name || typeof options.name !== 'string' || !options.name.trim()) {
|
|
419
|
+
throw new Error('--name is required for channel add');
|
|
420
|
+
}
|
|
421
|
+
return target;
|
|
422
|
+
}
|
|
423
|
+
|
|
316
424
|
/**
|
|
317
425
|
* @param {string} channelTarget
|
|
318
426
|
* @param {Object} options
|
|
319
427
|
* @returns {Promise<Object>}
|
|
320
428
|
*/
|
|
321
429
|
async function runAddChannel(channelTarget, options = {}) {
|
|
322
|
-
const
|
|
430
|
+
const target = assertChannelAddOptions(channelTarget, options);
|
|
431
|
+
if (channelTarget === 'microsoft-copilot' && options.force === true) {
|
|
432
|
+
await assertMicrosoftCopilotOauthReferenceBeforeForce(options);
|
|
433
|
+
}
|
|
434
|
+
await ensureChannelReadyForAdd(channelTarget, options);
|
|
435
|
+
const { name, baseName, description, ctx, existing } = await prepareChannelContext(
|
|
323
436
|
channelTarget,
|
|
324
437
|
options
|
|
325
438
|
);
|
|
326
|
-
|
|
327
439
|
const skipped = showExistingChannelStatus({
|
|
328
440
|
existing,
|
|
329
441
|
options,
|
|
@@ -333,7 +445,9 @@ async function runAddChannel(channelTarget, options = {}) {
|
|
|
333
445
|
ctx
|
|
334
446
|
});
|
|
335
447
|
if (skipped) return skipped;
|
|
336
|
-
|
|
448
|
+
if (channelTarget === 'microsoft-copilot') {
|
|
449
|
+
await assertMicrosoftCopilotOauthReference({ options, ctx, name, baseName, description });
|
|
450
|
+
}
|
|
337
451
|
const { artifact, channelBlock, status } = await buildInstallArtifacts({
|
|
338
452
|
channelTarget,
|
|
339
453
|
options,
|
|
@@ -344,11 +458,9 @@ async function runAddChannel(channelTarget, options = {}) {
|
|
|
344
458
|
description,
|
|
345
459
|
ctx
|
|
346
460
|
});
|
|
347
|
-
|
|
348
461
|
if (options.dryRun) {
|
|
349
462
|
return finishDryRunInstall({ channelTarget, name, baseName, target, artifact, channelBlock });
|
|
350
463
|
}
|
|
351
|
-
|
|
352
464
|
return completeLiveInstall({
|
|
353
465
|
ctx,
|
|
354
466
|
channelTarget,
|
|
@@ -367,12 +479,7 @@ async function runAddChannel(channelTarget, options = {}) {
|
|
|
367
479
|
* @param {Object} options
|
|
368
480
|
*/
|
|
369
481
|
async function addChannelCommand(channelTarget, options = {}) {
|
|
370
|
-
|
|
371
|
-
await runAddChannel(channelTarget, options);
|
|
372
|
-
} catch (err) {
|
|
373
|
-
displayChannelError(err.message || String(err));
|
|
374
|
-
throw err;
|
|
375
|
-
}
|
|
482
|
+
await runAddChannel(channelTarget, options);
|
|
376
483
|
}
|
|
377
484
|
|
|
378
485
|
module.exports = {
|
|
@@ -22,7 +22,7 @@ function buildChatGptApprovalGuide(ctx) {
|
|
|
22
22
|
'Enable Developer mode for custom MCP apps (Business: Workspace settings → Apps, or Settings → Apps → Advanced settings; Enterprise/Edu: grant Developer mode under Permissions & roles → Connected data first).',
|
|
23
23
|
'Create a custom MCP app with the Remote MCP URL from INSTALL.md (channel-chatgpt.json is a deployment reference, not a plugin archive).',
|
|
24
24
|
'Select OAuth (authorization-code + PKCE S256). Do not configure a custom API key for a remote custom MCP app.',
|
|
25
|
-
'Scan tools, complete OAuth if prompted, review discovered
|
|
25
|
+
'Scan tools, complete OAuth if prompted, review discovered governed tools, then save the app as a draft.',
|
|
26
26
|
formatCredentialPortalStep(
|
|
27
27
|
ctx,
|
|
28
28
|
`${ctx.credentialKey || 'channel'} credential references (not secrets to paste into ChatGPT)`
|