@aifabrix/builder 2.51.0 → 2.51.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 +77 -1
- package/jest.projects.js +5 -426
- package/lib/api/assistants.api.js +67 -0
- package/lib/api/datasources-extended.api.js +6 -6
- package/lib/api/evidence-authoring-helpers.js +267 -0
- package/lib/api/evidence-authoring.api.js +337 -0
- package/lib/api/evidence.api.js +57 -1
- package/lib/api/index.js +24 -7
- package/lib/api/pipeline.api.js +43 -3
- package/lib/api/records-bulk.api.js +28 -3
- package/lib/api/types/evidence.types.js +156 -8
- package/lib/api/work.api.js +283 -0
- package/lib/app/run-deploy-job.js +108 -0
- package/lib/app/run-helpers.js +10 -3
- package/lib/app/run.js +8 -0
- package/lib/channels/add-channel.js +54 -23
- package/lib/channels/approval-guides/chatgpt.js +8 -7
- package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
- package/lib/channels/channel-artifact-generator.js +14 -2
- package/lib/channels/channel-credential.js +3 -1
- package/lib/channels/channel-probe-evidence.js +79 -0
- package/lib/channels/channel-probe.js +265 -13
- package/lib/channels/channel-runtime-resolver.js +120 -29
- package/lib/channels/channel-status-display.js +44 -0
- package/lib/channels/channel-targets.js +7 -0
- package/lib/channels/chatgpt-runtime-contract.js +29 -0
- package/lib/channels/microsoft-copilot-package.js +280 -0
- package/lib/channels/runtime-tool-catalog.js +165 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/setup-app.js +4 -0
- package/lib/cli/setup-external-system-help.js +8 -2
- package/lib/cli/setup-external-system.js +12 -0
- package/lib/commands/app-test.js +2 -14
- package/lib/commands/datasource-load-export-cli.js +6 -1
- package/lib/commands/deploy-gate-failure-details.js +19 -17
- package/lib/commands/dev-down.js +2 -6
- package/lib/commands/evidence-kind.js +65 -15
- package/lib/commands/governance-generate-external.js +56 -21
- package/lib/commands/lifecycle-external.js +10 -4
- package/lib/commands/repair-datasource-apply.js +10 -1
- package/lib/commands/repair-datasource-run.js +4 -2
- package/lib/commands/repair-datasource-test-payload.js +80 -19
- package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
- package/lib/commands/repair-internal.js +9 -1
- package/lib/commands/repair-openapi-sync.js +34 -17
- package/lib/commands/role-assistant.js +313 -0
- package/lib/commands/upload-data-sync-finalize.js +5 -4
- package/lib/commands/upload-sidecar-followups.js +144 -47
- package/lib/commands/upload.js +43 -6
- package/lib/commands/verify-governance-combined-external.js +73 -9
- package/lib/commands/verify-governance-db.js +80 -25
- package/lib/commands/verify-governance-external.js +29 -5
- package/lib/commands/wizard-core-helpers.js +7 -3
- package/lib/commands/wizard-core.js +78 -9
- package/lib/datasource/bulk-loader-run-batches.js +4 -2
- package/lib/datasource/bulk-loader-service.js +5 -1
- package/lib/datasource/datasource-validate-summary.js +2 -2
- package/lib/datasource/validate.js +13 -6
- package/lib/external-system/deploy-helpers.js +35 -1
- package/lib/external-system/deploy.js +23 -10
- package/lib/external-system/download-helpers.js +111 -2
- package/lib/external-system/download-role-assistant.js +55 -0
- package/lib/external-system/download.js +45 -26
- package/lib/external-system/test-helpers.js +1 -1
- package/lib/external-system/test.js +39 -3
- package/lib/generator/builders.js +3 -0
- package/lib/generator/wizard.js +51 -4
- package/lib/lifecycle/access-control-abac.js +121 -0
- package/lib/lifecycle/lifecycle-governance-options.js +4 -1
- package/lib/lifecycle/product-model.js +3 -2
- package/lib/lifecycle/report-display.js +14 -2
- package/lib/programmatic/capability-registry.js +20 -1
- package/lib/programmatic/command-options.js +1 -0
- package/lib/programmatic/index.js +4 -0
- package/lib/programmatic/manifest-patch-section.js +48 -9
- package/lib/programmatic/manifest-section-registry-data.js +3 -2
- package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
- package/lib/programmatic/openapi-component-schemas.js +16 -1
- package/lib/programmatic/openapi-description-enricher.js +3 -1
- package/lib/programmatic/openapi-descriptions.js +55 -5
- package/lib/programmatic/openapi-evidence-schemas.js +161 -0
- package/lib/programmatic/openapi-manifest-schemas.js +6 -2
- package/lib/programmatic/openapi-operation-bindings.js +99 -0
- package/lib/programmatic/openapi-request-schemas.js +36 -1
- package/lib/programmatic/openapi-result-schemas.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
- package/lib/programmatic/openapi-schema-descriptions-core.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
- package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
- package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
- package/lib/programmatic/openapi-spec.js +47 -28
- package/lib/programmatic/orchestration-openapi-meta.js +5 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +89 -2
- package/lib/programmatic/run-certify.js +15 -2
- package/lib/programmatic/run-deploy.js +74 -0
- package/lib/programmatic/run-evidence-authoring.js +80 -0
- package/lib/programmatic/run-protection.js +124 -0
- package/lib/programmatic/run-template-workspace.js +28 -1
- package/lib/programmatic/workspace-context.js +6 -0
- package/lib/protection/auth-context.js +48 -6
- package/lib/protection/paths.js +22 -0
- package/lib/protection/run-protection-create-helpers.js +22 -1
- package/lib/protection/run-protection-create.js +1 -1
- package/lib/protection/sync-after-upload.js +1 -1
- package/lib/role-assistant/evidence-sync.js +450 -0
- package/lib/role-assistant/github-workflow.js +96 -0
- package/lib/role-assistant/knowledge-sync.js +465 -0
- package/lib/role-assistant/settings.js +126 -0
- package/lib/role-assistant/test-cases-evaluate.js +170 -0
- package/lib/role-assistant/test-cases-honesty.js +184 -0
- package/lib/role-assistant/test-cases.js +364 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
- package/lib/role-assistant/test-runner-workhub.js +385 -0
- package/lib/role-assistant/test-runner.js +500 -0
- package/lib/schema/application-schema.json +16 -2
- package/lib/schema/deployment-rules.yaml +1 -0
- package/lib/schema/external-datasource.schema.json +38 -4
- package/lib/schema/external-system.schema.json +4 -4
- package/lib/schema/infra.parameter.yaml +20 -0
- package/lib/schema/role-assistant-settings.schema.json +108 -0
- package/lib/utils/api.js +52 -27
- package/lib/utils/app-path-resolve.js +72 -24
- package/lib/utils/authentication-shape.js +3 -1
- package/lib/utils/deployment-validation.js +28 -3
- package/lib/utils/docker-run-ephemeral.js +55 -0
- package/lib/utils/enterprise-knowledge-paths.js +29 -12
- package/lib/utils/env-copy-keycloak.js +196 -0
- package/lib/utils/env-copy.js +34 -1
- package/lib/utils/external-system-readiness-compact-display.js +3 -0
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/paths.js +67 -23
- package/lib/utils/pipeline-upload-auth-retry.js +15 -3
- package/lib/utils/role-assistant-paths.js +98 -0
- package/lib/utils/schema-loader.js +41 -0
- package/lib/utils/schema-resolver.js +36 -4
- package/lib/utils/token-manager.js +38 -29
- package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
- package/lib/validation/external-manifest-validator.js +31 -8
- package/lib/validation/validate-external-file.js +8 -1
- package/package.json +2 -1
- package/scripts/export-builder-api-evidence.js +1 -1
- package/templates/applications/dataplane/application.yaml +23 -15
- package/templates/applications/dataplane/env.template +1 -1
- package/templates/applications/dataplane/rbac.yaml +14 -0
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +3 -1
- package/templates/channels/chatgpt/install.template.md.hbs +42 -24
- package/templates/channels/chatgpt/integration/README.md +10 -7
- package/templates/channels/chatgpt/integration/application.json +2 -2
- package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
- package/templates/channels/chatgpt/integration/env.template +1 -1
- package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
- package/templates/channels/microsoft-copilot/integration/README.md +9 -7
- package/templates/channels/microsoft-copilot/integration/application.json +2 -2
- package/templates/channels/microsoft-copilot/integration/env.template +1 -1
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
- /package/.cursor/plans/{157.1-security-quality-violations-by-type.csv → Done/157.1-security-quality-violations-by-type.csv} +0 -0
|
@@ -175,6 +175,18 @@ function resolveDatasourceFiles(schemaBasePath, datasourceFiles) {
|
|
|
175
175
|
return datasourceFiles.map(fileName => resolveSingleExternalFile(schemaBasePath, fileName, 'datasource'));
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
function resolveRoleAssistantSettingsFile(appPath) {
|
|
179
|
+
const yamlPath = path.join(appPath, 'role-assistant.yaml');
|
|
180
|
+
if (fs.existsSync(yamlPath)) {
|
|
181
|
+
return resolveSingleExternalFile(appPath, 'role-assistant.yaml', 'role-assistant-settings');
|
|
182
|
+
}
|
|
183
|
+
const ymlPath = path.join(appPath, 'role-assistant.yml');
|
|
184
|
+
if (fs.existsSync(ymlPath)) {
|
|
185
|
+
return resolveSingleExternalFile(appPath, 'role-assistant.yml', 'role-assistant-settings');
|
|
186
|
+
}
|
|
187
|
+
return resolveSingleExternalFile(appPath, 'role-assistant.yaml', 'role-assistant-settings');
|
|
188
|
+
}
|
|
189
|
+
|
|
178
190
|
/**
|
|
179
191
|
* @param {string} schemaBasePath
|
|
180
192
|
* @param {string[]} systemFileNames
|
|
@@ -282,6 +294,21 @@ async function loadAndValidateVariables(appPath) {
|
|
|
282
294
|
}
|
|
283
295
|
}
|
|
284
296
|
|
|
297
|
+
function readSystemKindFromFirstSystemFile(schemaBasePath, systemFileNames) {
|
|
298
|
+
if (!systemFileNames.length) {
|
|
299
|
+
return '';
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
const firstSystemPath = path.join(schemaBasePath, systemFileNames[0]);
|
|
303
|
+
const parsedSystem = loadConfigFile(firstSystemPath);
|
|
304
|
+
return parsedSystem && typeof parsedSystem.systemKind === 'string'
|
|
305
|
+
? parsedSystem.systemKind
|
|
306
|
+
: '';
|
|
307
|
+
} catch {
|
|
308
|
+
return '';
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
285
312
|
async function resolveExternalFiles(appName, options = {}) {
|
|
286
313
|
if (!appName || typeof appName !== 'string') {
|
|
287
314
|
throw new Error('App name is required and must be a string');
|
|
@@ -295,15 +322,20 @@ async function resolveExternalFiles(appName, options = {}) {
|
|
|
295
322
|
}
|
|
296
323
|
|
|
297
324
|
const schemaBasePath = await resolveSchemaBasePath(appName, options);
|
|
298
|
-
const
|
|
325
|
+
const systemFileNames = variables.externalIntegration.systems || [];
|
|
326
|
+
const systemFiles = resolveSystemFiles(schemaBasePath, systemFileNames);
|
|
327
|
+
const systemKind = readSystemKindFromFirstSystemFile(schemaBasePath, systemFileNames);
|
|
299
328
|
const orderedNames = orderDatasourceFileNamesBySystemKeys(
|
|
300
329
|
schemaBasePath,
|
|
301
|
-
|
|
330
|
+
systemFileNames,
|
|
302
331
|
variables.externalIntegration.dataSources || []
|
|
303
332
|
);
|
|
304
333
|
const datasourceFiles = resolveDatasourceFiles(schemaBasePath, orderedNames);
|
|
305
|
-
|
|
306
|
-
|
|
334
|
+
const extraFiles = [];
|
|
335
|
+
if (String(systemKind).trim() === 'digitalWorker') {
|
|
336
|
+
extraFiles.push(resolveRoleAssistantSettingsFile(appPath));
|
|
337
|
+
}
|
|
338
|
+
return [...systemFiles, ...datasourceFiles, ...extraFiles];
|
|
307
339
|
}
|
|
308
340
|
|
|
309
341
|
module.exports = {
|
|
@@ -408,53 +408,62 @@ function applyLabDataplaneApiKeyBearer(authConfig) {
|
|
|
408
408
|
return authConfig;
|
|
409
409
|
}
|
|
410
410
|
|
|
411
|
+
function looksLikeJwtBearer(token) {
|
|
412
|
+
const existing = token ? String(token) : '';
|
|
413
|
+
return existing.split('.').length === 3 && existing.length > 80;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function preferClientCredentialsDeploymentAuth() {
|
|
417
|
+
return String(process.env.AIFABRIX_DEPLOYMENT_AUTH || '').toLowerCase() === 'client-credentials';
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* @param {Object} authConfig
|
|
422
|
+
* @returns {boolean} true when a substitution was applied
|
|
423
|
+
*/
|
|
424
|
+
function trySubstituteEnvApiKeyBearer(authConfig) {
|
|
425
|
+
if (!process.env.API_KEY) {
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
const apiKey = String(process.env.API_KEY);
|
|
429
|
+
if (authConfig.type === 'bearer' && authConfig.token !== apiKey) {
|
|
430
|
+
authConfig.token = apiKey;
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
if (authConfig.type === 'client-token') {
|
|
434
|
+
authConfig.type = 'bearer';
|
|
435
|
+
authConfig.token = apiKey;
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
if (!authConfig.token) {
|
|
439
|
+
authConfig.type = 'bearer';
|
|
440
|
+
authConfig.token = apiKey;
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
|
|
411
446
|
function requireBearerForDataplanePipeline(authConfig) {
|
|
412
447
|
if (!authConfig || typeof authConfig !== 'object') {
|
|
413
448
|
return;
|
|
414
449
|
}
|
|
415
|
-
|
|
416
450
|
if (authConfig.type === 'client-token' && authConfig.token) {
|
|
417
451
|
return;
|
|
418
452
|
}
|
|
419
|
-
|
|
420
453
|
// Token-forward (builder-api / bearerForward): never substitute lab API_KEY for the caller JWT.
|
|
421
454
|
if (authConfig.preserveBearer && authConfig.token) {
|
|
422
455
|
return;
|
|
423
456
|
}
|
|
424
|
-
|
|
425
|
-
const preferAppClientToken =
|
|
426
|
-
String(process.env.AIFABRIX_DEPLOYMENT_AUTH || '').toLowerCase() === 'client-credentials';
|
|
427
|
-
|
|
428
|
-
const existing = authConfig.token ? String(authConfig.token) : '';
|
|
429
|
-
const looksLikeJwt = existing.split('.').length === 3 && existing.length > 80;
|
|
430
457
|
// Enterprise path: keep a real OAuth JWT; do not overwrite with process.env.API_KEY.
|
|
431
|
-
if (
|
|
458
|
+
if (looksLikeJwtBearer(authConfig.token)) {
|
|
432
459
|
return;
|
|
433
460
|
}
|
|
434
|
-
|
|
435
|
-
if (!preferAppClientToken && process.env.API_KEY) {
|
|
436
|
-
const apiKey = String(process.env.API_KEY);
|
|
437
|
-
if (authConfig.type === 'bearer' && authConfig.token !== apiKey) {
|
|
438
|
-
authConfig.token = apiKey;
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
if (authConfig.type === 'client-token') {
|
|
442
|
-
authConfig.type = 'bearer';
|
|
443
|
-
authConfig.token = apiKey;
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
if (authConfig.token) {
|
|
461
|
+
if (!preferClientCredentialsDeploymentAuth() && trySubstituteEnvApiKeyBearer(authConfig)) {
|
|
449
462
|
return;
|
|
450
463
|
}
|
|
451
|
-
|
|
452
|
-
if (!preferAppClientToken && process.env.API_KEY) {
|
|
453
|
-
authConfig.type = 'bearer';
|
|
454
|
-
authConfig.token = String(process.env.API_KEY);
|
|
464
|
+
if (authConfig.token) {
|
|
455
465
|
return;
|
|
456
466
|
}
|
|
457
|
-
|
|
458
467
|
if (authConfig.clientId || authConfig.clientSecret) {
|
|
459
468
|
throw new Error(
|
|
460
469
|
'Dataplane pipeline endpoints require OAuth2 (Bearer token) or x-client-token. ' +
|
|
@@ -25,6 +25,42 @@ function tryReadApplicationYamlAt(cfgPath) {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* @param {unknown} value
|
|
30
|
+
* @returns {boolean}
|
|
31
|
+
*/
|
|
32
|
+
function isPositivePortValue(value) {
|
|
33
|
+
if (typeof value === 'number' && value > 0) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
return typeof value === 'string' && /^\d+$/.test(String(value).trim());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* True when a manifest can authoritatively supply listen/port for url:// expansion.
|
|
41
|
+
* External-system `application.json` (upload payload) has no HTTP listen port — using it
|
|
42
|
+
* for `url://builder-api-internal` falls through to {@link DECLARATIVE_URL_INFRA_DEFAULTS}
|
|
43
|
+
* (3000) and breaks Docker CIP against the real builder-api container (3005).
|
|
44
|
+
*
|
|
45
|
+
* @param {object|null|undefined} doc
|
|
46
|
+
* @returns {boolean}
|
|
47
|
+
*/
|
|
48
|
+
function isDeclarativeUrlPortAuthorityDoc(doc) {
|
|
49
|
+
if (!doc || typeof doc !== 'object') {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if ((doc.app && doc.app.type) === 'external') {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (doc.externalIntegration && typeof doc.externalIntegration === 'object') {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return (
|
|
59
|
+
isPositivePortValue(doc.port) ||
|
|
60
|
+
isPositivePortValue(doc.build && doc.build.containerPort)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
28
64
|
/**
|
|
29
65
|
* Candidate paths for cross-app url:// resolution (low → high priority).
|
|
30
66
|
* Platform system apps prefer materialized `~/.aifabrix-work|home/builder` over CLI package `builder/` stubs.
|
|
@@ -83,7 +119,7 @@ function loadApplicationYamlDocForUrlResolve(appKey, ctx, pathsUtil) {
|
|
|
83
119
|
if (appKey === current && ctx.variablesPath) {
|
|
84
120
|
const vp = path.resolve(String(ctx.variablesPath));
|
|
85
121
|
const fromVars = tryReadApplicationYamlAt(vp);
|
|
86
|
-
if (fromVars) {
|
|
122
|
+
if (isDeclarativeUrlPortAuthorityDoc(fromVars)) {
|
|
87
123
|
return fromVars;
|
|
88
124
|
}
|
|
89
125
|
}
|
|
@@ -105,5 +141,6 @@ function loadApplicationYamlDocForUrlResolve(appKey, ctx, pathsUtil) {
|
|
|
105
141
|
|
|
106
142
|
module.exports = {
|
|
107
143
|
collectApplicationYamlPathsForUrlResolve,
|
|
144
|
+
isDeclarativeUrlPortAuthorityDoc,
|
|
108
145
|
loadApplicationYamlDocForUrlResolve
|
|
109
146
|
};
|
|
@@ -217,6 +217,36 @@ function validateDatasourceSystemKeyAlignment(manifest, errors) {
|
|
|
217
217
|
});
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Vendor OpenAPI contract surface is for Connected Systems. Role Assistants
|
|
222
|
+
* (digitalWorker) promote settings + Evidence — skip blocking OpenAPI vendor checks.
|
|
223
|
+
* @param {Object} manifest
|
|
224
|
+
* @param {Object} options
|
|
225
|
+
* @param {string[]} errors
|
|
226
|
+
* @param {string[]} warnings
|
|
227
|
+
*/
|
|
228
|
+
function applyExternalContractSurfaceChecks(manifest, options, errors, warnings) {
|
|
229
|
+
if (manifest.type !== 'external' || !Array.isArray(manifest.dataSources) || !manifest.system?.key) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const appPath = options.appPath;
|
|
233
|
+
if (typeof appPath !== 'string' || !appPath) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const systemKind =
|
|
237
|
+
typeof manifest.system.systemKind === 'string' ? manifest.system.systemKind.trim() : '';
|
|
238
|
+
if (systemKind === 'digitalWorker') {
|
|
239
|
+
warnings.push(
|
|
240
|
+
'Role Assistant (digitalWorker): vendor OpenAPI contract checks skipped ' +
|
|
241
|
+
'(settings + evidence path; run repair --api only when binding vendor OpenAPI).'
|
|
242
|
+
);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const contract = validateAppContractSurface(appPath, manifest.system.key, manifest.dataSources);
|
|
246
|
+
errors.push(...contract.errors);
|
|
247
|
+
warnings.push(...contract.warnings);
|
|
248
|
+
}
|
|
249
|
+
|
|
220
250
|
/**
|
|
221
251
|
* Validates controller deployment manifest for external systems
|
|
222
252
|
* Validates manifest structure and inline system/dataSources against their schemas
|
|
@@ -251,14 +281,7 @@ async function validateControllerManifest(manifest, options = {}) {
|
|
|
251
281
|
validateDatasources(manifest, ajv, externalDatasourceSchema, errors, warnings);
|
|
252
282
|
validateDatasourceSystemKeyAlignment(manifest, errors);
|
|
253
283
|
errors.push(...validateManifestRuntimeConfiguration(manifest));
|
|
254
|
-
|
|
255
|
-
const appPath = options.appPath;
|
|
256
|
-
if (typeof appPath === 'string' && appPath) {
|
|
257
|
-
const contract = validateAppContractSurface(appPath, manifest.system.key, manifest.dataSources);
|
|
258
|
-
errors.push(...contract.errors);
|
|
259
|
-
warnings.push(...contract.warnings);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
284
|
+
applyExternalContractSurfaceChecks(manifest, options, errors, warnings);
|
|
262
285
|
validateConditionalRequirements(manifest, errors, warnings);
|
|
263
286
|
validateRequiredFields(manifest, errors);
|
|
264
287
|
|
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const yaml = require('js-yaml');
|
|
13
|
-
const {
|
|
13
|
+
const {
|
|
14
|
+
loadExternalSystemSchema,
|
|
15
|
+
loadExternalDataSourceSchema,
|
|
16
|
+
loadRoleAssistantSettingsSchema
|
|
17
|
+
} = require('../utils/schema-loader');
|
|
14
18
|
const { formatValidationErrors } = require('../utils/error-formatter');
|
|
15
19
|
const { validateFieldReferences } = require('../datasource/field-reference-validator');
|
|
16
20
|
const { validateAbac } = require('../datasource/abac-validator');
|
|
@@ -53,6 +57,9 @@ function getValidatorForType(normalizedType) {
|
|
|
53
57
|
if (normalizedType === 'datasource') {
|
|
54
58
|
return loadExternalDataSourceSchema();
|
|
55
59
|
}
|
|
60
|
+
if (normalizedType === 'role-assistant-settings') {
|
|
61
|
+
return loadRoleAssistantSettingsSchema();
|
|
62
|
+
}
|
|
56
63
|
throw new Error(`Unknown file type: ${normalizedType}`);
|
|
57
64
|
}
|
|
58
65
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aifabrix/builder",
|
|
3
|
-
"version": "2.51.
|
|
3
|
+
"version": "2.51.1",
|
|
4
4
|
"description": "AI Fabrix Builder — CLI and developer scripts (pnpm + af)",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -114,6 +114,7 @@
|
|
|
114
114
|
},
|
|
115
115
|
"dependencies": {
|
|
116
116
|
"@aifabrix/miso-client": "^4.19.0",
|
|
117
|
+
"adm-zip": "^0.6.0",
|
|
117
118
|
"ajv": "^8.20.0",
|
|
118
119
|
"ajv-formats": "^3.0.1",
|
|
119
120
|
"axios": "^1.18.1",
|
|
@@ -238,7 +238,7 @@ function rowsFromManifestGuides() {
|
|
|
238
238
|
title: 'Section quality and missing inputs',
|
|
239
239
|
when: 'GET manifest section returns quality.missingInputs',
|
|
240
240
|
expectedBehavior:
|
|
241
|
-
'Collect required missing inputs from the user before PATCH. Do not guess values.
|
|
241
|
+
'Collect required missing inputs from the user before PATCH. Do not guess values. sectionReadToken from GET is optional on PATCH (server re-reads when omitted; stale token returns 409). Prefer GET when you need quality or missingInputs.',
|
|
242
242
|
order: 17
|
|
243
243
|
})
|
|
244
244
|
];
|
|
@@ -4,11 +4,12 @@ app:
|
|
|
4
4
|
displayName: "AI Fabrix Dataplane"
|
|
5
5
|
description: "AI Fabrix Dataplane is a secure, in-tenant integration and automation layer that supplies governed, normalized, and explainable enterprise data to AI agents. Using CIP as a declarative standard, it enforces RBAC and ABAC, executes integrations, and exposes trusted data via MCP and OpenAPI."
|
|
6
6
|
type: webapp
|
|
7
|
-
language: python
|
|
8
|
-
version: 2.0.
|
|
7
|
+
language: python # Explicitly specify Python language
|
|
8
|
+
version: 2.0.15
|
|
9
|
+
|
|
9
10
|
# Image Configuration
|
|
10
|
-
# Set tag to match your build (
|
|
11
|
-
# Registry is required so the controller can pull the image (avoids
|
|
11
|
+
# Set tag to match your build (example: aifabrix build dataplane -t 1.0.0).
|
|
12
|
+
# Registry is required so the controller can pull the image (avoids docker-not-found on the controller host).
|
|
12
13
|
image:
|
|
13
14
|
name: aifabrix/dataplane
|
|
14
15
|
tag: latest
|
|
@@ -34,10 +35,10 @@ frontDoorRouting:
|
|
|
34
35
|
requires:
|
|
35
36
|
database: true
|
|
36
37
|
databases:
|
|
37
|
-
- name: dataplane
|
|
38
|
-
- name: dataplane-vector
|
|
39
|
-
- name: dataplane-logs
|
|
40
|
-
- name: dataplane-records
|
|
38
|
+
- name: dataplane # Main database - configuration only
|
|
39
|
+
- name: dataplane-vector # Vector and document store: chunks, embeddings, vector indexes (pgvector)
|
|
40
|
+
- name: dataplane-logs # Logs database - execution, audit, ABAC traces
|
|
41
|
+
- name: dataplane-records # Records database - external records storage
|
|
41
42
|
redis: true
|
|
42
43
|
storage: true
|
|
43
44
|
|
|
@@ -59,10 +60,10 @@ authentication:
|
|
|
59
60
|
# Build Configuration
|
|
60
61
|
# Dataplane builds from published image; context is project root (like miso-controller)
|
|
61
62
|
build:
|
|
62
|
-
context: ../..
|
|
63
|
-
dockerfile: builder/dataplane/Dockerfile
|
|
64
|
-
envOutputPath: ../../.env
|
|
65
|
-
language: python
|
|
63
|
+
context: ../.. # Docker build context (relative to builder/dataplane/)
|
|
64
|
+
dockerfile: builder/dataplane/Dockerfile # Dockerfile path (relative to project root)
|
|
65
|
+
envOutputPath: ../../.env # Copy to repo root for local dev
|
|
66
|
+
language: python # Runtime language for template selection (typescript or python)
|
|
66
67
|
# Dev reload: polling + watch /app/app only (avoids EMFILE on full repo bind-mount). See run_local.py, make af-reload.
|
|
67
68
|
reloadStart: WATCHFILES_FORCE_POLLING=1 uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-3001} --reload --reload-dir /app/app
|
|
68
69
|
# Pulled-image local compose (no bind-mount): drives `command` in generated docker-compose. Override for non-standard images (e.g. stub ACR tags).
|
|
@@ -72,6 +73,10 @@ build:
|
|
|
72
73
|
worker: true
|
|
73
74
|
imageRunWorker: exec python -m app.core.celery_worker_main
|
|
74
75
|
imageRunBeat: exec python -m app.core.celery_beat_main
|
|
76
|
+
# Optional one-shot before ANY long-running role (api/worker/beat). Run exactly once per
|
|
77
|
+
# aifabrix run / Deployment — never once-per-container. Schema: application-schema build.deployJob.
|
|
78
|
+
# Local: aifabrix run; remote: miso Deployment via miso-azure (not Docker on controller).
|
|
79
|
+
deployJob: python -m app.db.migrate upgrade
|
|
75
80
|
|
|
76
81
|
# =============================================================================
|
|
77
82
|
# Portal Input Configuration (Deployment Wizard)
|
|
@@ -81,7 +86,7 @@ build:
|
|
|
81
86
|
#
|
|
82
87
|
# NOT included (auto-configured):
|
|
83
88
|
# - Database URLs, Redis → auto-generated during Azure install
|
|
84
|
-
# - MISO_*, KEYCLOAK_* → auto-generated during Azure install
|
|
89
|
+
# - MISO_*, KEYCLOAK_* → auto-generated during Azure install
|
|
85
90
|
# - AI params / provider secrets → LLM external systems + secret store
|
|
86
91
|
# (never product OPENAI_* / AZURE_OPENAI_*). ≥2 providers →
|
|
87
92
|
# ENTERPRISE_LLM_DEFAULT_SELECTOR (portal LLM category).
|
|
@@ -135,8 +140,9 @@ configuration:
|
|
|
135
140
|
category: Trust
|
|
136
141
|
portalInput:
|
|
137
142
|
field: select
|
|
138
|
-
label: "Enforce trust gate on datasource publish"
|
|
143
|
+
label: "Enforce trust gate on datasource publish (empty=ENVIRONMENT default)"
|
|
139
144
|
options:
|
|
145
|
+
- ""
|
|
140
146
|
- "false"
|
|
141
147
|
- "true"
|
|
142
148
|
|
|
@@ -144,8 +150,9 @@ configuration:
|
|
|
144
150
|
category: Trust
|
|
145
151
|
portalInput:
|
|
146
152
|
field: select
|
|
147
|
-
label: "Enforce trust gate on
|
|
153
|
+
label: "Enforce trust gate on Deploy / system publish (empty=ENVIRONMENT default)"
|
|
148
154
|
options:
|
|
155
|
+
- ""
|
|
149
156
|
- "false"
|
|
150
157
|
- "true"
|
|
151
158
|
|
|
@@ -225,3 +232,4 @@ configuration:
|
|
|
225
232
|
field: text
|
|
226
233
|
label: "Default LLM provider (system key when two or more providers)"
|
|
227
234
|
placeholder: "openai"
|
|
235
|
+
|
|
@@ -44,7 +44,7 @@ API_KEY_BYPASS_ROLES=full-access
|
|
|
44
44
|
|
|
45
45
|
# API Configuration
|
|
46
46
|
API_V1_STR=/api/v1
|
|
47
|
-
VERSION=2.0.
|
|
47
|
+
VERSION=2.0.15
|
|
48
48
|
# Minimum supported aifabrix-builder CLI semver (advertised on /health and /api/v1/health; empty = no gate)
|
|
49
49
|
# MIN_BUILDER_CLI_VERSION=2.45.0
|
|
50
50
|
MIN_BUILDER_CLI_VERSION=2.45.5
|
|
@@ -143,6 +143,20 @@ permissions:
|
|
|
143
143
|
- name: "integration-template:delete"
|
|
144
144
|
roles: ["aifabrix-platform-admin", "aifabrix-developer"]
|
|
145
145
|
description: "Delete integration templates"
|
|
146
|
+
|
|
147
|
+
# Resource type catalog (public read; Miso owns writes via controller sync)
|
|
148
|
+
- name: "resource-type:read"
|
|
149
|
+
roles:
|
|
150
|
+
[
|
|
151
|
+
"aifabrix-platform-admin",
|
|
152
|
+
"aifabrix-security-admin",
|
|
153
|
+
"aifabrix-deployment-admin",
|
|
154
|
+
"aifabrix-compliance-admin",
|
|
155
|
+
"aifabrix-developer",
|
|
156
|
+
"aifabrix-observer",
|
|
157
|
+
"aifabrix-ai-trust-operator",
|
|
158
|
+
]
|
|
159
|
+
description: "Read the global Resource Type catalog (list/get)"
|
|
146
160
|
|
|
147
161
|
# Generic dataplane operations
|
|
148
162
|
- name: "dataplane:read"
|
|
@@ -4,7 +4,7 @@ app:
|
|
|
4
4
|
displayName: 'Miso Controller'
|
|
5
5
|
description: 'Miso is the AI Fabrix in-tenant controller and portal layer for securely operating enterprise AI apps inside a customer’s Azure tenant. It provides Entra ID SSO, RBAC, audit logs, environment/app configuration via schemas, and safe secret handling via Key Vault references—ensuring governance, traceability, and predictable UX across portal, SDK, and CLI.'
|
|
6
6
|
type: webapp
|
|
7
|
-
version: '2.0.
|
|
7
|
+
version: '2.0.10'
|
|
8
8
|
|
|
9
9
|
# Image Configuration
|
|
10
10
|
# Set tag to match your build (e.g. aifabrix build miso-controller -t 1.0.0 then tag: 1.0.0)
|
|
@@ -288,7 +288,9 @@ ENTRA_GRAPH_DELEGATED_REDIRECT_URI=https://aifabrix-pro-mori-web-we.azurewebsite
|
|
|
288
288
|
# MORI SERVICE CONFIGURATION
|
|
289
289
|
# =============================================================================
|
|
290
290
|
|
|
291
|
-
|
|
291
|
+
# Shared pro Mori (Entra handoff + Graph callback). Prefer this over url://mori-controller-public
|
|
292
|
+
# unless a local Mori app is registered and running for the developer.
|
|
293
|
+
MORI_BASE_URL=https://aifabrix-pro-mori-web-we.azurewebsites.net
|
|
292
294
|
MORI_AUTH_METHOD=kv://mori-controller-auth-methodKeyVault
|
|
293
295
|
MORI_CALLBACK_API_KEY=kv://mori-callback-api-keyKeyVault
|
|
294
296
|
MORI_API_KEY=kv://mori-controller-api-keyKeyVault
|
|
@@ -1,46 +1,64 @@
|
|
|
1
|
-
# {{appName}} — ChatGPT
|
|
1
|
+
# {{appName}} — ChatGPT custom MCP app setup
|
|
2
2
|
|
|
3
3
|
{{appDescription}}
|
|
4
4
|
|
|
5
5
|
Environment: **{{environment}}** · Credential key: `{{credentialKey}}`
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This output is connection and administration material (`channel-chatgpt.json` + `INSTALL.md`), not a plugin archive.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
2. Create a custom GPT, workspace connector, or MCP integration using the generated manifest from `dist/channels/chatgpt/channel-chatgpt.json`.
|
|
11
|
-
3. Configure the **OpenAPI specification URL** and **MCP endpoint** from **Platform endpoints** below.
|
|
12
|
-
4. Complete OAuth or API key setup as required by your ChatGPT deployment model.
|
|
13
|
-
5. Save the **API key** or **OAuth client credentials** in AI Fabrix: [Authentication settings]({{credentialPortalUrl}})
|
|
14
|
-
6. Publish the connector to your workspace and complete admin approval if your organization requires it.
|
|
9
|
+
## Workspace admin setup (manual)
|
|
15
10
|
|
|
16
|
-
|
|
11
|
+
1. Sign in to your ChatGPT workspace as an admin and enable **Developer mode / custom MCP apps** if required by workspace policy.
|
|
12
|
+
2. Create a custom MCP app and use the generated artifact from `dist/channels/chatgpt/channel-chatgpt.json` as the connection reference.
|
|
13
|
+
3. Configure the remote MCP URL and transport from **Runtime connection** below.
|
|
14
|
+
4. Complete authentication setup for your workspace policy. Store credential references in AI Fabrix: [Authentication settings]({{credentialPortalUrl}})
|
|
15
|
+
5. Refresh tools in ChatGPT after configuration changes so ChatGPT reads the latest Runtime tool list.
|
|
16
|
+
6. Publish the app to the workspace and configure workspace access controls (RBAC or equivalent).
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
## Runtime connection
|
|
19
|
+
|
|
20
|
+
Configure these values in the ChatGPT custom MCP app. Do not place secrets in generated files.
|
|
19
21
|
|
|
20
|
-
{{#if openapiUrl}}
|
|
21
|
-
- **OpenAPI spec:** {{openapiUrl}}
|
|
22
|
-
{{/if}}
|
|
23
22
|
{{#if mcpUrl}}
|
|
24
|
-
- **MCP:** {{mcpUrl}}
|
|
25
|
-
{{/if}}
|
|
26
|
-
{{#if actionsBaseUrl}}
|
|
27
|
-
- **Actions base:** {{actionsBaseUrl}}
|
|
23
|
+
- **Remote MCP URL:** {{mcpUrl}}
|
|
28
24
|
{{/if}}
|
|
25
|
+
- **Runtime system key:** {{runtimeSystemKey}}
|
|
26
|
+
- **Transport:** {{runtimeTransport}}
|
|
27
|
+
- **Auth mode:** {{runtimeAuthMode}}
|
|
28
|
+
- **Expected Layer B tools:**
|
|
29
|
+
{{#each layerBToolNames}}
|
|
30
|
+
- `{{this}}`
|
|
31
|
+
{{/each}}
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
AI Fabrix `--probe` validates Runtime through the first-party MCP gateway
|
|
34
|
+
(`/api/v1/mcp/servers/tools` and MCP docs for `{{runtimeSystemKey}}`), including
|
|
35
|
+
Layer B discovery and BTA Evidence authoring reachability. ChatGPT workspace apps still
|
|
36
|
+
require a working streamable-http Remote MCP URL and admin publication.
|
|
31
37
|
|
|
32
|
-
Runtime
|
|
38
|
+
## Runtime and Role Assistant boundary checks
|
|
39
|
+
|
|
40
|
+
- Runtime should resolve assistants dynamically per user from:
|
|
33
41
|
|
|
34
42
|
{{assistantCatalogUrl}}
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
- The ChatGPT top-level tool surface should stay at Runtime Layer B tools.
|
|
45
|
+
- Evidence authoring remains an internal Business Transformation Assistant capability path, not top-level ChatGPT tools.
|
|
37
46
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
## BTA Evidence-authoring proof scenario
|
|
48
|
+
|
|
49
|
+
Use this scenario to verify ChatGPT → Runtime → BTA path without exposing Evidence commands as top-level ChatGPT tools:
|
|
50
|
+
|
|
51
|
+
1. Ask ChatGPT to improve an existing Role Assistant from process documentation.
|
|
52
|
+
2. Confirm ChatGPT starts or continues Business Transformation Assistant work through Runtime.
|
|
53
|
+
3. Confirm BTA performs evidence metadata/download/upload reasoning and presents certified proposals.
|
|
54
|
+
4. Approve selected proposals and confirm activation remains an explicit human action.
|
|
41
55
|
|
|
42
|
-
|
|
56
|
+
## AI Fabrix validation commands
|
|
57
|
+
|
|
58
|
+
After workspace publication and authentication setup:
|
|
43
59
|
|
|
44
60
|
```bash
|
|
45
61
|
aifabrix channel add {{channelTarget}} --name "{{baseName}}" --probe
|
|
46
62
|
```
|
|
63
|
+
|
|
64
|
+
Use `--force` only to regenerate artifacts and patch channel metadata. It does not rotate credentials.
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
# ChatGPT
|
|
1
|
+
# ChatGPT custom MCP app channel
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Generated by `aifabrix channel scaffold chatgpt`.
|
|
4
|
+
|
|
5
|
+
Recommended flow:
|
|
4
6
|
|
|
5
7
|
```bash
|
|
6
|
-
|
|
7
|
-
aifabrix
|
|
8
|
-
aifabrix upload chatgpt --probe
|
|
9
|
-
aifabrix deploy chatgpt
|
|
10
|
-
aifabrix app add-channel chatgpt --name "Your Assistant"
|
|
8
|
+
aifabrix upload chatgpt --force
|
|
9
|
+
aifabrix channel add chatgpt --name "Your Assistant" --probe
|
|
11
10
|
```
|
|
11
|
+
|
|
12
|
+
The `channel add` step generates `dist/channels/chatgpt/channel-chatgpt.json` and
|
|
13
|
+
`INSTALL.md` for ChatGPT workspace admins to connect a custom MCP app to the
|
|
14
|
+
runtime-orchestration server.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"app": {
|
|
3
3
|
"key": "chatgpt",
|
|
4
|
-
"displayName": "ChatGPT
|
|
5
|
-
"description": "Assistant communication channel — deploy then
|
|
4
|
+
"displayName": "ChatGPT custom MCP app channel",
|
|
5
|
+
"description": "Assistant communication channel — deploy then channel add",
|
|
6
6
|
"type": "external"
|
|
7
7
|
},
|
|
8
8
|
"externalIntegration": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"key": "chatgpt",
|
|
3
|
-
"displayName": "ChatGPT
|
|
4
|
-
"description": "Communication external system for ChatGPT
|
|
3
|
+
"displayName": "ChatGPT custom MCP app channel",
|
|
4
|
+
"description": "Communication external system for ChatGPT custom MCP app onboarding",
|
|
5
5
|
"type": "custom",
|
|
6
6
|
"systemKind": "communication",
|
|
7
7
|
"enabled": true,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"authType": "credential",
|
|
10
10
|
"credential": {
|
|
11
11
|
"credentialKey": "chatgpt-channel",
|
|
12
|
-
"displayName": "ChatGPT
|
|
12
|
+
"displayName": "ChatGPT custom MCP app channel",
|
|
13
13
|
"method": "apikey",
|
|
14
14
|
"writeOnce": true,
|
|
15
15
|
"variables": {
|