@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.
Files changed (162) hide show
  1. package/jest.isolated-projects.js +77 -1
  2. package/jest.projects.js +5 -426
  3. package/lib/api/assistants.api.js +67 -0
  4. package/lib/api/datasources-extended.api.js +6 -6
  5. package/lib/api/evidence-authoring-helpers.js +267 -0
  6. package/lib/api/evidence-authoring.api.js +337 -0
  7. package/lib/api/evidence.api.js +57 -1
  8. package/lib/api/index.js +24 -7
  9. package/lib/api/pipeline.api.js +43 -3
  10. package/lib/api/records-bulk.api.js +28 -3
  11. package/lib/api/types/evidence.types.js +156 -8
  12. package/lib/api/work.api.js +283 -0
  13. package/lib/app/run-deploy-job.js +108 -0
  14. package/lib/app/run-helpers.js +10 -3
  15. package/lib/app/run.js +8 -0
  16. package/lib/channels/add-channel.js +54 -23
  17. package/lib/channels/approval-guides/chatgpt.js +8 -7
  18. package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
  19. package/lib/channels/channel-artifact-generator.js +14 -2
  20. package/lib/channels/channel-credential.js +3 -1
  21. package/lib/channels/channel-probe-evidence.js +79 -0
  22. package/lib/channels/channel-probe.js +265 -13
  23. package/lib/channels/channel-runtime-resolver.js +120 -29
  24. package/lib/channels/channel-status-display.js +44 -0
  25. package/lib/channels/channel-targets.js +7 -0
  26. package/lib/channels/chatgpt-runtime-contract.js +29 -0
  27. package/lib/channels/microsoft-copilot-package.js +280 -0
  28. package/lib/channels/runtime-tool-catalog.js +165 -0
  29. package/lib/cli/index.js +2 -0
  30. package/lib/cli/setup-app.js +4 -0
  31. package/lib/cli/setup-external-system-help.js +8 -2
  32. package/lib/cli/setup-external-system.js +12 -0
  33. package/lib/commands/app-test.js +2 -14
  34. package/lib/commands/datasource-load-export-cli.js +6 -1
  35. package/lib/commands/deploy-gate-failure-details.js +19 -17
  36. package/lib/commands/dev-down.js +2 -6
  37. package/lib/commands/evidence-kind.js +65 -15
  38. package/lib/commands/governance-generate-external.js +56 -21
  39. package/lib/commands/lifecycle-external.js +10 -4
  40. package/lib/commands/repair-datasource-apply.js +10 -1
  41. package/lib/commands/repair-datasource-run.js +4 -2
  42. package/lib/commands/repair-datasource-test-payload.js +80 -19
  43. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
  44. package/lib/commands/repair-internal.js +9 -1
  45. package/lib/commands/repair-openapi-sync.js +34 -17
  46. package/lib/commands/role-assistant.js +313 -0
  47. package/lib/commands/upload-data-sync-finalize.js +5 -4
  48. package/lib/commands/upload-sidecar-followups.js +144 -47
  49. package/lib/commands/upload.js +43 -6
  50. package/lib/commands/verify-governance-combined-external.js +73 -9
  51. package/lib/commands/verify-governance-db.js +80 -25
  52. package/lib/commands/verify-governance-external.js +29 -5
  53. package/lib/commands/wizard-core-helpers.js +7 -3
  54. package/lib/commands/wizard-core.js +78 -9
  55. package/lib/datasource/bulk-loader-run-batches.js +4 -2
  56. package/lib/datasource/bulk-loader-service.js +5 -1
  57. package/lib/datasource/datasource-validate-summary.js +2 -2
  58. package/lib/datasource/validate.js +13 -6
  59. package/lib/external-system/deploy-helpers.js +35 -1
  60. package/lib/external-system/deploy.js +23 -10
  61. package/lib/external-system/download-helpers.js +111 -2
  62. package/lib/external-system/download-role-assistant.js +55 -0
  63. package/lib/external-system/download.js +45 -26
  64. package/lib/external-system/test-helpers.js +1 -1
  65. package/lib/external-system/test.js +39 -3
  66. package/lib/generator/builders.js +3 -0
  67. package/lib/generator/wizard.js +51 -4
  68. package/lib/lifecycle/access-control-abac.js +121 -0
  69. package/lib/lifecycle/lifecycle-governance-options.js +4 -1
  70. package/lib/lifecycle/product-model.js +3 -2
  71. package/lib/lifecycle/report-display.js +14 -2
  72. package/lib/programmatic/capability-registry.js +20 -1
  73. package/lib/programmatic/command-options.js +1 -0
  74. package/lib/programmatic/index.js +4 -0
  75. package/lib/programmatic/manifest-patch-section.js +48 -9
  76. package/lib/programmatic/manifest-section-registry-data.js +3 -2
  77. package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
  78. package/lib/programmatic/openapi-component-schemas.js +16 -1
  79. package/lib/programmatic/openapi-description-enricher.js +3 -1
  80. package/lib/programmatic/openapi-descriptions.js +55 -5
  81. package/lib/programmatic/openapi-evidence-schemas.js +161 -0
  82. package/lib/programmatic/openapi-manifest-schemas.js +6 -2
  83. package/lib/programmatic/openapi-operation-bindings.js +99 -0
  84. package/lib/programmatic/openapi-request-schemas.js +36 -1
  85. package/lib/programmatic/openapi-result-schemas.js +1 -1
  86. package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
  87. package/lib/programmatic/openapi-schema-descriptions-core.js +1 -1
  88. package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
  89. package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
  90. package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
  91. package/lib/programmatic/openapi-spec.js +47 -28
  92. package/lib/programmatic/orchestration-openapi-meta.js +5 -0
  93. package/lib/programmatic/request-timeout.js +3 -0
  94. package/lib/programmatic/route-handler-map.js +89 -2
  95. package/lib/programmatic/run-certify.js +15 -2
  96. package/lib/programmatic/run-deploy.js +74 -0
  97. package/lib/programmatic/run-evidence-authoring.js +80 -0
  98. package/lib/programmatic/run-protection.js +124 -0
  99. package/lib/programmatic/run-template-workspace.js +28 -1
  100. package/lib/programmatic/workspace-context.js +6 -0
  101. package/lib/protection/auth-context.js +48 -6
  102. package/lib/protection/paths.js +22 -0
  103. package/lib/protection/run-protection-create-helpers.js +22 -1
  104. package/lib/protection/run-protection-create.js +1 -1
  105. package/lib/protection/sync-after-upload.js +1 -1
  106. package/lib/role-assistant/evidence-sync.js +450 -0
  107. package/lib/role-assistant/github-workflow.js +96 -0
  108. package/lib/role-assistant/knowledge-sync.js +465 -0
  109. package/lib/role-assistant/settings.js +126 -0
  110. package/lib/role-assistant/test-cases-evaluate.js +170 -0
  111. package/lib/role-assistant/test-cases-honesty.js +184 -0
  112. package/lib/role-assistant/test-cases.js +364 -0
  113. package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
  114. package/lib/role-assistant/test-runner-workhub.js +385 -0
  115. package/lib/role-assistant/test-runner.js +500 -0
  116. package/lib/schema/application-schema.json +16 -2
  117. package/lib/schema/deployment-rules.yaml +1 -0
  118. package/lib/schema/external-datasource.schema.json +38 -4
  119. package/lib/schema/external-system.schema.json +4 -4
  120. package/lib/schema/infra.parameter.yaml +20 -0
  121. package/lib/schema/role-assistant-settings.schema.json +108 -0
  122. package/lib/utils/api.js +52 -27
  123. package/lib/utils/app-path-resolve.js +72 -24
  124. package/lib/utils/authentication-shape.js +3 -1
  125. package/lib/utils/deployment-validation.js +28 -3
  126. package/lib/utils/docker-run-ephemeral.js +55 -0
  127. package/lib/utils/enterprise-knowledge-paths.js +29 -12
  128. package/lib/utils/env-copy-keycloak.js +196 -0
  129. package/lib/utils/env-copy.js +34 -1
  130. package/lib/utils/external-system-readiness-compact-display.js +3 -0
  131. package/lib/utils/help-builder.js +1 -0
  132. package/lib/utils/paths.js +67 -23
  133. package/lib/utils/pipeline-upload-auth-retry.js +15 -3
  134. package/lib/utils/role-assistant-paths.js +98 -0
  135. package/lib/utils/schema-loader.js +41 -0
  136. package/lib/utils/schema-resolver.js +36 -4
  137. package/lib/utils/token-manager.js +38 -29
  138. package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
  139. package/lib/validation/external-manifest-validator.js +31 -8
  140. package/lib/validation/validate-external-file.js +8 -1
  141. package/package.json +2 -1
  142. package/scripts/export-builder-api-evidence.js +1 -1
  143. package/templates/applications/dataplane/application.yaml +23 -15
  144. package/templates/applications/dataplane/env.template +1 -1
  145. package/templates/applications/dataplane/rbac.yaml +14 -0
  146. package/templates/applications/miso-controller/application.yaml +1 -1
  147. package/templates/applications/miso-controller/env.template +3 -1
  148. package/templates/channels/chatgpt/install.template.md.hbs +42 -24
  149. package/templates/channels/chatgpt/integration/README.md +10 -7
  150. package/templates/channels/chatgpt/integration/application.json +2 -2
  151. package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
  152. package/templates/channels/chatgpt/integration/env.template +1 -1
  153. package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
  154. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
  155. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
  156. package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
  157. package/templates/channels/microsoft-copilot/integration/README.md +9 -7
  158. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  159. package/templates/channels/microsoft-copilot/integration/env.template +1 -1
  160. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
  161. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
  162. /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 systemFiles = resolveSystemFiles(schemaBasePath, variables.externalIntegration.systems);
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
- variables.externalIntegration.systems,
330
+ systemFileNames,
302
331
  variables.externalIntegration.dataSources || []
303
332
  );
304
333
  const datasourceFiles = resolveDatasourceFiles(schemaBasePath, orderedNames);
305
-
306
- return [...systemFiles, ...datasourceFiles];
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 (looksLikeJwt) {
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
- if (manifest.type === 'external' && Array.isArray(manifest.dataSources) && manifest.system?.key) {
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 { loadExternalSystemSchema, loadExternalDataSourceSchema } = require('../utils/schema-loader');
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.0",
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. Include sectionReadToken from GET in PATCH body.',
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 # Explicitly specify Python language
8
- version: 2.0.10
7
+ language: python # Explicitly specify Python language
8
+ version: 2.0.15
9
+
9
10
  # Image Configuration
10
- # Set tag to match your build (e.g. aifabrix build dataplane -t 1.0.0 then tag: 1.0.0)
11
- # Registry is required so the controller can pull the image (avoids "docker: not found" on the controller host).
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 # Main database - configuration only
38
- - name: dataplane-vector # Vector and document store: chunks, embeddings, vector indexes (pgvector)
39
- - name: dataplane-logs # Logs database - execution, audit, ABAC traces
40
- - name: dataplane-records # Records database - external records storage
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: ../.. # Docker build context (relative to builder/dataplane/)
63
- dockerfile: builder/dataplane/Dockerfile # Dockerfile path (relative to project root)
64
- envOutputPath: ../../.env # Copy to repo root for local dev
65
- language: python # Runtime language for template selection (typescript or 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 external system publish (promote scope)"
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.10
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.8'
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
- MORI_BASE_URL=url://mori-controller-public
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 assistant channel install
1
+ # {{appName}} — ChatGPT custom MCP app setup
2
2
 
3
3
  {{appDescription}}
4
4
 
5
5
  Environment: **{{environment}}** · Credential key: `{{credentialKey}}`
6
6
 
7
- ## Connect ChatGPT workspace (admin)
7
+ This output is connection and administration material (`channel-chatgpt.json` + `INSTALL.md`), not a plugin archive.
8
8
 
9
- 1. Sign in to [OpenAI Platform](https://platform.openai.com/) or your **ChatGPT Enterprise** workspace admin console.
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
- ## Platform endpoints
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
- Configure these on the ChatGPT connector / custom GPT. Do not embed secrets in generated files — use AI Fabrix credential storage only.
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
- ## Assistant catalog
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 lists **assistants** available to each user from:
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
- ## After workspace approval
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
- ```bash
39
- aifabrix channel add {{channelTarget}} --name "{{baseName}}"
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
- Optional validation:
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 assistant channel (lab fixture)
1
+ # ChatGPT custom MCP app channel
2
2
 
3
- SSOT for `aifabrix channel scaffold chatgpt` templates (`make sync-json` → builder).
3
+ Generated by `aifabrix channel scaffold chatgpt`.
4
+
5
+ Recommended flow:
4
6
 
5
7
  ```bash
6
- cd aifabrix-dataplane
7
- aifabrix validate chatgpt
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 assistant channel",
5
- "description": "Assistant communication channel — deploy then app add-channel",
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 assistant channel",
4
- "description": "Communication external system for ChatGPT assistant channel install",
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 assistant channel",
12
+ "displayName": "ChatGPT custom MCP app channel",
13
13
  "method": "apikey",
14
14
  "writeOnce": true,
15
15
  "variables": {
@@ -1,4 +1,4 @@
1
- # ChatGPT assistant channel — lab fixture secrets
1
+ # ChatGPT custom MCP app channel credential reference
2
2
  # aifabrix secret set chatgpt/apiKey "<value>" --shared
3
3
 
4
4
  KV_CHATGPT_APIKEY=kv://chatgpt/apiKey