@aifabrix/builder 2.50.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 (236) hide show
  1. package/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +374 -0
  2. package/.cursor/rules/assistant-driven-integration-build.mdc +40 -0
  3. package/integration/hubspot-test/create-hubspot.js +1 -16
  4. package/jest.isolated-projects.js +77 -1
  5. package/jest.projects.js +5 -426
  6. package/lib/api/assistants.api.js +67 -0
  7. package/lib/api/datasources-extended.api.js +6 -6
  8. package/lib/api/evidence-authoring-helpers.js +267 -0
  9. package/lib/api/evidence-authoring.api.js +337 -0
  10. package/lib/api/evidence-kinds.api.js +128 -0
  11. package/lib/api/evidence.api.js +57 -1
  12. package/lib/api/index.js +24 -7
  13. package/lib/api/pipeline.api.js +47 -3
  14. package/lib/api/records-bulk.api.js +28 -3
  15. package/lib/api/resource-types.api.js +4 -4
  16. package/lib/api/roles.api.js +121 -0
  17. package/lib/api/types/evidence-kinds.types.js +42 -0
  18. package/lib/api/types/evidence.types.js +156 -8
  19. package/lib/api/types/roles.types.js +36 -0
  20. package/lib/api/work.api.js +283 -0
  21. package/lib/app/certification-verify-rows.js +1 -1
  22. package/lib/app/run-deploy-job.js +108 -0
  23. package/lib/app/run-helpers.js +10 -3
  24. package/lib/app/run.js +8 -0
  25. package/lib/app/show.js +2 -2
  26. package/lib/certification/merge-certification-from-artifact.js +1 -1
  27. package/lib/channels/add-channel.js +54 -23
  28. package/lib/channels/approval-guides/chatgpt.js +8 -7
  29. package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
  30. package/lib/channels/channel-artifact-generator.js +16 -3
  31. package/lib/channels/channel-credential.js +3 -1
  32. package/lib/channels/channel-probe-evidence.js +79 -0
  33. package/lib/channels/channel-probe.js +265 -13
  34. package/lib/channels/channel-runtime-resolver.js +120 -29
  35. package/lib/channels/channel-status-display.js +44 -0
  36. package/lib/channels/channel-targets.js +7 -0
  37. package/lib/channels/chatgpt-runtime-contract.js +29 -0
  38. package/lib/channels/microsoft-copilot-package.js +280 -0
  39. package/lib/channels/runtime-tool-catalog.js +165 -0
  40. package/lib/cli/index.js +6 -0
  41. package/lib/cli/setup-app.js +26 -5
  42. package/lib/cli/setup-dev.js +23 -7
  43. package/lib/cli/setup-external-system-help.js +8 -2
  44. package/lib/cli/setup-external-system.js +12 -0
  45. package/lib/cli/setup-utility-help-after.js +12 -0
  46. package/lib/cli/setup-utility-validate.js +41 -2
  47. package/lib/commands/app-test.js +2 -14
  48. package/lib/commands/datasource-load-export-cli.js +6 -1
  49. package/lib/commands/deploy-gate-failure-details.js +23 -0
  50. package/lib/commands/deploy-gate-orchestrator.js +64 -18
  51. package/lib/commands/dev-cli-handlers.js +6 -3
  52. package/lib/commands/dev-down.js +2 -6
  53. package/lib/commands/dev-init.js +7 -109
  54. package/lib/commands/dev-refresh.js +197 -0
  55. package/lib/commands/evidence-kind.js +478 -0
  56. package/lib/commands/governance-command-action.js +4 -2
  57. package/lib/commands/governance-generate-external.js +56 -21
  58. package/lib/commands/lifecycle-external.js +10 -4
  59. package/lib/commands/repair-datasource-apply.js +10 -1
  60. package/lib/commands/repair-datasource-run.js +4 -2
  61. package/lib/commands/repair-datasource-test-payload.js +80 -19
  62. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
  63. package/lib/commands/repair-internal.js +9 -1
  64. package/lib/commands/repair-openapi-export.js +1 -1
  65. package/lib/commands/repair-openapi-sync.js +43 -18
  66. package/lib/commands/resource-type.js +101 -18
  67. package/lib/commands/role-assistant.js +313 -0
  68. package/lib/commands/role.js +452 -0
  69. package/lib/commands/setup.js +1 -1
  70. package/lib/commands/upload-data-sync-finalize.js +5 -4
  71. package/lib/commands/upload-sidecar-followups.js +144 -47
  72. package/lib/commands/upload.js +43 -6
  73. package/lib/commands/validate-json.js +230 -0
  74. package/lib/commands/verify-governance-combined-external.js +73 -9
  75. package/lib/commands/verify-governance-db.js +80 -25
  76. package/lib/commands/verify-governance-external.js +29 -5
  77. package/lib/commands/verify-operations-step-plan.js +78 -40
  78. package/lib/commands/wizard-core-helpers.js +7 -3
  79. package/lib/commands/wizard-core.js +78 -9
  80. package/lib/core/audit-logger.js +1 -1
  81. package/lib/core/secrets-load.js +1 -3
  82. package/lib/datasource/bulk-loader-run-batches.js +4 -2
  83. package/lib/datasource/bulk-loader-service.js +5 -1
  84. package/lib/datasource/capability/relate-operations.js +1 -1
  85. package/lib/datasource/datasource-validate-summary.js +2 -2
  86. package/lib/datasource/validate.js +13 -6
  87. package/lib/external-system/deploy-helpers.js +35 -1
  88. package/lib/external-system/deploy.js +23 -10
  89. package/lib/external-system/download-helpers.js +111 -2
  90. package/lib/external-system/download-role-assistant.js +55 -0
  91. package/lib/external-system/download.js +45 -26
  92. package/lib/external-system/test-helpers.js +1 -1
  93. package/lib/external-system/test.js +39 -3
  94. package/lib/generator/builders.js +3 -0
  95. package/lib/generator/helpers.js +13 -5
  96. package/lib/generator/split-variables.js +9 -3
  97. package/lib/generator/wizard.js +51 -4
  98. package/lib/infrastructure/helpers-docker-check.js +6 -0
  99. package/lib/infrastructure/helpers.js +1 -1
  100. package/lib/lifecycle/access-control-abac.js +121 -0
  101. package/lib/lifecycle/lifecycle-governance-options.js +4 -1
  102. package/lib/lifecycle/product-model.js +3 -2
  103. package/lib/lifecycle/report-display.js +14 -2
  104. package/lib/programmatic/auth-config.js +3 -2
  105. package/lib/programmatic/bearer-auth.js +1 -1
  106. package/lib/programmatic/capability-registry.js +20 -1
  107. package/lib/programmatic/command-options.js +1 -0
  108. package/lib/programmatic/enterprise-mcp-client.js +18 -2
  109. package/lib/programmatic/index.js +4 -0
  110. package/lib/programmatic/manifest-patch-section.js +48 -9
  111. package/lib/programmatic/manifest-section-registry-data.js +3 -2
  112. package/lib/programmatic/manifest-section-registry.js +18 -1
  113. package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
  114. package/lib/programmatic/openapi-component-schemas.js +16 -1
  115. package/lib/programmatic/openapi-description-enricher.js +3 -1
  116. package/lib/programmatic/openapi-descriptions.js +55 -5
  117. package/lib/programmatic/openapi-evidence-schemas.js +161 -0
  118. package/lib/programmatic/openapi-manifest-schemas.js +6 -2
  119. package/lib/programmatic/openapi-operation-bindings.js +99 -0
  120. package/lib/programmatic/openapi-request-schemas.js +36 -1
  121. package/lib/programmatic/openapi-result-schemas.js +29 -12
  122. package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
  123. package/lib/programmatic/openapi-schema-descriptions-core.js +6 -3
  124. package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
  125. package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
  126. package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
  127. package/lib/programmatic/openapi-schema-helpers.js +16 -1
  128. package/lib/programmatic/openapi-spec.js +48 -29
  129. package/lib/programmatic/orchestration-openapi-meta.js +5 -0
  130. package/lib/programmatic/request-timeout.js +3 -0
  131. package/lib/programmatic/route-handler-map.js +89 -2
  132. package/lib/programmatic/run-certify.js +15 -2
  133. package/lib/programmatic/run-deploy.js +74 -0
  134. package/lib/programmatic/run-evidence-authoring.js +80 -0
  135. package/lib/programmatic/run-protection.js +124 -0
  136. package/lib/programmatic/run-repository-emcp.js +11 -3
  137. package/lib/programmatic/run-template-workspace.js +42 -2
  138. package/lib/programmatic/workspace-context.js +6 -0
  139. package/lib/protection/auth-context.js +48 -6
  140. package/lib/protection/paths.js +22 -0
  141. package/lib/protection/run-protection-create-helpers.js +22 -1
  142. package/lib/protection/run-protection-create.js +1 -1
  143. package/lib/protection/sync-after-upload.js +1 -1
  144. package/lib/resolvers/evidence-kind-file.js +230 -0
  145. package/lib/resolvers/resource-type-file.js +36 -1
  146. package/lib/resolvers/role-file.js +227 -0
  147. package/lib/role-assistant/evidence-sync.js +450 -0
  148. package/lib/role-assistant/github-workflow.js +96 -0
  149. package/lib/role-assistant/knowledge-sync.js +465 -0
  150. package/lib/role-assistant/settings.js +126 -0
  151. package/lib/role-assistant/test-cases-evaluate.js +170 -0
  152. package/lib/role-assistant/test-cases-honesty.js +184 -0
  153. package/lib/role-assistant/test-cases.js +364 -0
  154. package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
  155. package/lib/role-assistant/test-runner-workhub.js +385 -0
  156. package/lib/role-assistant/test-runner.js +500 -0
  157. package/lib/schema/application-schema.json +36 -2
  158. package/lib/schema/deployment-rules.yaml +1 -0
  159. package/lib/schema/external-datasource.schema.json +121 -4
  160. package/lib/schema/external-system.schema.json +20 -6
  161. package/lib/schema/infra.parameter.yaml +45 -1
  162. package/lib/schema/role-assistant-settings.schema.json +108 -0
  163. package/lib/schema/type/credential-field-registry.json +19 -3
  164. package/lib/utils/api.js +52 -21
  165. package/lib/utils/app-path-resolve.js +72 -24
  166. package/lib/utils/app-register-config.js +4 -3
  167. package/lib/utils/authentication-shape.js +3 -1
  168. package/lib/utils/cli-sync-options.js +59 -3
  169. package/lib/utils/cli-utils.js +11 -3
  170. package/lib/utils/deployment-validation.js +28 -3
  171. package/lib/utils/dev-ca-install.js +5 -6
  172. package/lib/utils/dev-config.js +2 -2
  173. package/lib/utils/dev-list-sort.js +145 -0
  174. package/lib/utils/dev-platform-tls.js +0 -63
  175. package/lib/utils/docker-build.js +46 -27
  176. package/lib/utils/docker-daemon-tls-ca.js +1 -1
  177. package/lib/utils/docker-exec.js +91 -6
  178. package/lib/utils/docker-run-ephemeral.js +55 -0
  179. package/lib/utils/enterprise-knowledge-paths.js +29 -12
  180. package/lib/utils/env-copy-keycloak.js +196 -0
  181. package/lib/utils/env-copy.js +35 -4
  182. package/lib/utils/error-formatters/error-parser.js +0 -3
  183. package/lib/utils/external-env-template.js +1 -1
  184. package/lib/utils/external-system-readiness-compact-blocks.js +28 -1
  185. package/lib/utils/external-system-readiness-compact-display.js +3 -0
  186. package/lib/utils/health-check-request.js +2 -3
  187. package/lib/utils/help-builder.js +4 -0
  188. package/lib/utils/image-name.js +22 -4
  189. package/lib/utils/logger.js +37 -4
  190. package/lib/utils/manifest-source-emit.js +1 -1
  191. package/lib/utils/paths.js +98 -26
  192. package/lib/utils/pipeline-upload-auth-retry.js +19 -3
  193. package/lib/utils/postgres-platform-bootstrap.js +24 -17
  194. package/lib/utils/postgres-wipe.js +5 -2
  195. package/lib/utils/remote-dev-auth.js +39 -4
  196. package/lib/utils/resolve-docker-image-ref.js +36 -6
  197. package/lib/utils/role-assistant-paths.js +98 -0
  198. package/lib/utils/schema-loader.js +41 -0
  199. package/lib/utils/schema-resolver.js +36 -4
  200. package/lib/utils/token-manager.js +47 -22
  201. package/lib/utils/upload-sync-options.js +17 -3
  202. package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
  203. package/lib/validation/external-manifest-validator.js +31 -8
  204. package/lib/validation/openapi-contract-surface-validator.js +4 -0
  205. package/lib/validation/validate-external-file.js +8 -1
  206. package/lib/validation/validator.js +6 -5
  207. package/package.json +35 -12
  208. package/scripts/export-builder-api-evidence.js +3 -3
  209. package/scripts/lib/plan-actor.js +2 -2
  210. package/scripts/lib/plan-meta.js +1 -1
  211. package/scripts/lib/plan-parse.js +1 -1
  212. package/scripts/lib/plan-prevalidate-gates.js +12 -1
  213. package/scripts/lib/plan-run-report.js +13 -2
  214. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +10 -10
  215. package/templates/applications/dataplane/application.yaml +47 -17
  216. package/templates/applications/dataplane/env.template +28 -9
  217. package/templates/applications/dataplane/rbac.yaml +78 -2
  218. package/templates/applications/miso-controller/application.yaml +10 -1
  219. package/templates/applications/miso-controller/env.template +13 -8
  220. package/templates/applications/miso-controller/rbac.yaml +72 -0
  221. package/templates/channels/chatgpt/install.template.md.hbs +42 -24
  222. package/templates/channels/chatgpt/integration/README.md +10 -7
  223. package/templates/channels/chatgpt/integration/application.json +2 -2
  224. package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
  225. package/templates/channels/chatgpt/integration/env.template +1 -1
  226. package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
  227. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
  228. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
  229. package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
  230. package/templates/channels/microsoft-copilot/integration/README.md +9 -7
  231. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  232. package/templates/channels/microsoft-copilot/integration/env.template +1 -1
  233. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
  234. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
  235. package/templates/channels/slack/integration/slack-system.json +60 -2
  236. package/templates/python/requirements.txt +1 -1
package/lib/app/show.js CHANGED
@@ -347,7 +347,7 @@ async function getShowAuthToken(controllerUrl, config) {
347
347
  }
348
348
 
349
349
  const formatted = formatAuthenticationError({
350
- controllerUrl: controllerUrl || undefined,
350
+ controllerUrl: undefined,
351
351
  message: 'No valid authentication found'
352
352
  });
353
353
  logger.error(formatted);
@@ -477,7 +477,7 @@ function resolveExternalSystemMeta(res, config, system, application, appKey) {
477
477
 
478
478
  function buildBaseExternalResult(configData, appKey, openapiFiles, openapiEndpoints, res) {
479
479
  const system = configData.system || configData;
480
- const dataSources = configData.dataSources || configData.dataSources || [];
480
+ const dataSources = configData.dataSources || [];
481
481
  const application = configData.application || configData.app || {};
482
482
  const config = res && res.config && typeof res.config === 'object' ? res.config : {};
483
483
  const meta = resolveExternalSystemMeta(res, config, system, application, appKey);
@@ -13,7 +13,7 @@
13
13
  function certificateIdToString(id) {
14
14
  if (id === undefined || id === null) return '';
15
15
  if (typeof id === 'string') return id.trim();
16
- if (typeof id === 'object' && id !== null && typeof id.id === 'string') return String(id.id).trim();
16
+ if (typeof id === 'object' && typeof id.id === 'string') return String(id.id).trim();
17
17
  return String(id).trim();
18
18
  }
19
19
 
@@ -12,6 +12,7 @@ const { getChannelTarget, getDefaultChannelDescription } = require('./channel-ta
12
12
  const { resolveAppDescription } = require('./channel-description');
13
13
  const { resolveChannelRuntimeValues } = require('./channel-runtime-resolver');
14
14
  const { generateChannelArtifact } = require('./channel-artifact-generator');
15
+ const { fetchRuntimeLayerBToolNames } = require('./runtime-tool-catalog');
15
16
  const {
16
17
  getExistingChannelExtension,
17
18
  buildChannelExtensionBlock,
@@ -66,7 +67,9 @@ function toValidationDisplay(validation) {
66
67
  callbackReachable: validation.callbackReachable,
67
68
  credentialValid: validation.credentialValid,
68
69
  assistantCatalogReachable: validation.assistantCatalogReachable,
69
- inboundVerification: Object.values(validation.inboundVerification).every(Boolean)
70
+ inboundVerification: Object.values(validation.inboundVerification).every(Boolean),
71
+ probeTiers: validation.probeTiers || null,
72
+ runtimeChecks: validation.runtimeChecks || null
70
73
  };
71
74
  }
72
75
 
@@ -156,7 +159,13 @@ async function patchConfigWithAudit(ctx, channelTarget, channelBlock, status, ar
156
159
  async function maybeRunProbe(ctx, channelBlock, channelTarget, probe) {
157
160
  if (!probe) return null;
158
161
  const validation = await runChannelProbe(
159
- { dataplaneUrl: ctx.dataplaneUrl, authConfig: ctx.authConfig },
162
+ {
163
+ dataplaneUrl: ctx.dataplaneUrl,
164
+ authConfig: ctx.authConfig,
165
+ environment: ctx.environment,
166
+ controllerUrl: ctx.controllerUrl,
167
+ channelTarget
168
+ },
160
169
  channelBlock
161
170
  );
162
171
  if (validation.passed) {
@@ -268,6 +277,42 @@ function finishDryRunInstall(params) {
268
277
  return { dryRun: true, channelBlock: params.channelBlock, artifact: params.artifact };
269
278
  }
270
279
 
280
+ /**
281
+ * @param {Object} params
282
+ * @returns {Promise<{ artifact: Object, channelBlock: Object, status: string }>}
283
+ */
284
+ async function buildInstallArtifacts(params) {
285
+ const { channelTarget, options, target, existing, name, baseName, description, ctx } = params;
286
+ const runtimeToolNames =
287
+ channelTarget === 'chatgpt' || channelTarget === 'microsoft-copilot'
288
+ ? await fetchRuntimeLayerBToolNames(ctx.dataplaneUrl, ctx.authConfig, channelTarget)
289
+ : [];
290
+ const runtimeValues = resolveChannelRuntimeValues({
291
+ dataplaneBaseUrl: ctx.dataplaneUrl,
292
+ channelTarget,
293
+ appName: name,
294
+ baseName,
295
+ environment: ctx.environment,
296
+ appDescription: description,
297
+ icon: options.icon,
298
+ commandPrefix: options.commandPrefix,
299
+ layerBToolNames: runtimeToolNames
300
+ });
301
+ const projectRoot = options.projectRoot || process.cwd();
302
+ const artifact = generateArtifactWithAudit(channelTarget, {
303
+ projectRoot,
304
+ channelTarget,
305
+ runtimeValues,
306
+ dryRun: options.dryRun === true
307
+ });
308
+ const status = resolveInstallStatus(target, existing, options.dryRun === true);
309
+ const channelBlock = buildChannelExtensionBlock(
310
+ { name, baseName, description, artifactHash: artifact.artifactHash, status },
311
+ target
312
+ );
313
+ return { artifact, channelBlock, status };
314
+ }
315
+
271
316
  /**
272
317
  * @param {string} channelTarget
273
318
  * @param {Object} options
@@ -289,31 +334,17 @@ async function runAddChannel(channelTarget, options = {}) {
289
334
  });
290
335
  if (skipped) return skipped;
291
336
 
292
- const runtimeValues = resolveChannelRuntimeValues({
293
- dataplaneBaseUrl: ctx.dataplaneUrl,
337
+ const { artifact, channelBlock, status } = await buildInstallArtifacts({
294
338
  channelTarget,
295
- appName: name,
339
+ options,
340
+ target,
341
+ existing,
342
+ name,
296
343
  baseName,
297
- environment: ctx.environment,
298
- appDescription: description,
299
- icon: options.icon,
300
- commandPrefix: options.commandPrefix
301
- });
302
-
303
- const projectRoot = options.projectRoot || process.cwd();
304
- const artifact = generateArtifactWithAudit(channelTarget, {
305
- projectRoot,
306
- channelTarget,
307
- runtimeValues,
308
- dryRun: options.dryRun === true
344
+ description,
345
+ ctx
309
346
  });
310
347
 
311
- const status = resolveInstallStatus(target, existing, options.dryRun === true);
312
- const channelBlock = buildChannelExtensionBlock(
313
- { name, baseName, description, artifactHash: artifact.artifactHash, status },
314
- target
315
- );
316
-
317
348
  if (options.dryRun) {
318
349
  return finishDryRunInstall({ channelTarget, name, baseName, target, artifact, channelBlock });
319
350
  }
@@ -19,13 +19,14 @@ function buildChatGptApprovalGuide(ctx) {
19
19
  portalUrl: 'https://platform.openai.com/',
20
20
  steps: [
21
21
  'Sign in to OpenAI Platform (https://platform.openai.com/) or your ChatGPT Enterprise workspace admin console.',
22
- 'Create a custom GPT, workspace connector, or MCP integration using the generated manifest (path shown below).',
23
- 'Configure the OpenAPI specification URL and MCP endpoint from INSTALL.md (dataplane ChatGPT routes).',
24
- 'Complete OAuth or API key setup as required by your ChatGPT deployment model.',
25
- formatCredentialPortalStep(ctx, 'the API key or OAuth client credentials'),
26
- 'Publish the connector to your workspace and complete admin approval if your org requires it.',
27
- `After ChatGPT lists the connector as active, re-run: ${ctx.resumeCommand}`,
28
- 'Optional: add --probe when dataplane ChatGPT runtime routes are live on your environment.'
22
+ 'Enable Developer mode / custom MCP app creation for the workspace (if policy requires admin allow-listing).',
23
+ 'Create a custom MCP app using the generated channel-chatgpt.json reference artifact (path shown below).',
24
+ 'Configure the Runtime MCP endpoint from INSTALL.md and verify Runtime Layer B tool discovery.',
25
+ 'Complete workspace authentication setup for the custom MCP app.',
26
+ formatCredentialPortalStep(ctx, 'workspace credential references used by the ChatGPT channel'),
27
+ 'Publish the app to your workspace and configure end-user access controls.',
28
+ `After ChatGPT lists the app as active, re-run: ${ctx.resumeCommand}`,
29
+ 'Optional: use --probe for AI Fabrix-side runtime checks; OpenAI-side publication remains a manual admin responsibility.'
29
30
  ]
30
31
  };
31
32
  }
@@ -19,14 +19,13 @@ function buildMicrosoftCopilotApprovalGuide(ctx) {
19
19
  portalUrl: 'https://admin.cloud.microsoft.com/',
20
20
  steps: [
21
21
  'Sign in to Microsoft 365 admin (https://admin.cloud.microsoft.com/) or Copilot Studio as a tenant admin.',
22
- 'Create or open a Copilot extensibility project (declarative agent or API plugin).',
23
- 'Import or configure using the generated manifest artifact (path shown below).',
24
- 'Point the OpenAPI / action endpoints to the URLs in INSTALL.md (dataplane Copilot routes).',
25
- 'Register OAuth client credentials for the API provider connection if the platform prompts for them.',
26
- formatCredentialPortalStep(ctx, 'the OAuth client ID and client secret'),
27
- 'Submit for M365 admin approval and publish to your organization when review completes.',
28
- `After Copilot shows the extension as available, re-run: ${ctx.resumeCommand}`,
29
- 'Optional: add --probe when dataplane Copilot runtime routes are live on your environment.'
22
+ 'Upload or sideload the generated appPackage.<env>.zip package artifact.',
23
+ 'Review declarative agent + Remote MCP plugin details and approve tenant distribution.',
24
+ 'Configure Entra/OAuth consent and user assignment for target groups.',
25
+ formatCredentialPortalStep(ctx, 'Copilot channel credential references (no secrets in package files)'),
26
+ 'Verify Runtime MCP endpoint and Layer B tool visibility using INSTALL.md guidance.',
27
+ `After Copilot shows the package as available, re-run: ${ctx.resumeCommand}`,
28
+ 'Optional: use --probe for AI Fabrix-side checks; tenant upload/approval remains Microsoft-admin responsibility.'
30
29
  ]
31
30
  };
32
31
  }
@@ -14,8 +14,10 @@ const path = require('path');
14
14
  const Handlebars = require('handlebars');
15
15
  const { getChannelTarget } = require('./channel-targets');
16
16
  const { BUILDER_ROOT } = require('./channel-integration-template');
17
+ const { generateMicrosoftCopilotPackage } = require('./microsoft-copilot-package');
17
18
 
18
19
  const SECRET_PATTERNS = [/xoxb-/i, /xoxp-/i, /client_secret/i, /signing_secret/i, /Bearer\s+/i];
20
+ const PLACEHOLDER_DOMAIN_RE = /\byour-domain\.com\b/i;
19
21
 
20
22
  /**
21
23
  * @param {string} content
@@ -27,7 +29,7 @@ function assertNoSecretsInArtifact(content) {
27
29
  throw new Error('Generated artifact must not contain secret-like values');
28
30
  }
29
31
  }
30
- if (content.includes('your-domain.com')) {
32
+ if (PLACEHOLDER_DOMAIN_RE.test(content)) {
31
33
  throw new Error('Generated artifact still contains placeholder domain your-domain.com');
32
34
  }
33
35
  }
@@ -114,11 +116,22 @@ function renderInstallGuideIfPresent(templatesDir, runtimeValues, outputDir, dry
114
116
  */
115
117
  function generateChannelArtifact(params) {
116
118
  const templatesDir = getChannelTemplatesDir(params.channelTarget);
119
+ const outputPath = getArtifactOutputPath(params.projectRoot, params.channelTarget);
120
+
121
+ if (params.channelTarget === 'microsoft-copilot') {
122
+ return generateMicrosoftCopilotPackage({
123
+ runtimeValues: params.runtimeValues,
124
+ templatesDir,
125
+ outputPath,
126
+ dryRun: params.dryRun === true,
127
+ assertNoSecretsInArtifact,
128
+ renderTemplateFile
129
+ });
130
+ }
131
+
117
132
  const manifestTemplate = path.join(templatesDir, 'manifest.template.json.hbs');
118
133
  const content = renderTemplateFile(manifestTemplate, params.runtimeValues);
119
134
  assertNoSecretsInArtifact(content);
120
-
121
- const outputPath = getArtifactOutputPath(params.projectRoot, params.channelTarget);
122
135
  const artifactHash = crypto.createHash('sha256').update(content, 'utf8').digest('hex');
123
136
  const outputDir = path.dirname(outputPath);
124
137
 
@@ -48,8 +48,10 @@ const CREDENTIAL_SPECS = {
48
48
  method: 'oauth2',
49
49
  credentialConfig: {
50
50
  type: 'oauth2',
51
- grantType: 'client_credentials',
51
+ grantType: 'authorization_code',
52
+ authorizationUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
52
53
  tokenUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
54
+ scope: 'openid profile offline_access',
53
55
  clientId: 'pending-channel-install',
54
56
  clientSecret: 'pending-channel-install'
55
57
  }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Probe BTA Evidence authoring reachability for channel installs (plan 163 T7).
3
+ *
4
+ * @fileoverview Evidence metadata/download live checks without mutating activate/upload
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const {
12
+ getEvidenceMetadata,
13
+ downloadEvidence
14
+ } = require('../api/evidence-authoring.api');
15
+
16
+ const EVIDENCE_AUTHORING_CAPABILITY_KEYS = Object.freeze([
17
+ 'evidence.metadata',
18
+ 'evidence.download',
19
+ 'evidence.upload',
20
+ 'evidence.activate'
21
+ ]);
22
+
23
+ /**
24
+ * Prefer Business Transformation when present; otherwise first catalog role key.
25
+ *
26
+ * @param {Object[]} catalogItems
27
+ * @returns {string}
28
+ */
29
+ function pickEvidenceRoleKey(catalogItems) {
30
+ const keys = (Array.isArray(catalogItems) ? catalogItems : [])
31
+ .map((item) => String(item?.key || item?.roleKey || item?.roleRef || '').trim())
32
+ .filter(Boolean);
33
+ const preferred = keys.find((key) => key.includes('business-transformation'));
34
+ return preferred || keys[0] || '';
35
+ }
36
+
37
+ /**
38
+ * Live metadata + download prove Evidence path; upload/activate remain contract keys.
39
+ *
40
+ * @async
41
+ * @param {Object} ctx
42
+ * @param {Object[]} catalogItems
43
+ * @returns {Promise<{ reachable: boolean, roleKey: string, capabilityKeys: string[] }>}
44
+ */
45
+ async function probeEvidenceAuthoringReachable(ctx, catalogItems) {
46
+ const roleKey = pickEvidenceRoleKey(catalogItems);
47
+ const capabilityKeys = [...EVIDENCE_AUTHORING_CAPABILITY_KEYS];
48
+ if (!roleKey || !ctx?.dataplaneUrl || !ctx?.authConfig || !ctx?.controllerUrl) {
49
+ return { reachable: false, roleKey, capabilityKeys };
50
+ }
51
+ try {
52
+ const options = { controllerUrl: ctx.controllerUrl };
53
+ const metadata = await getEvidenceMetadata(
54
+ ctx.dataplaneUrl,
55
+ ctx.authConfig,
56
+ roleKey,
57
+ options
58
+ );
59
+ const downloaded = await downloadEvidence(
60
+ ctx.dataplaneUrl,
61
+ ctx.authConfig,
62
+ { roleKey, pageSize: 1 },
63
+ options
64
+ );
65
+ const reachable =
66
+ Boolean(metadata?.roleKey) &&
67
+ Array.isArray(metadata?.evidenceKinds) &&
68
+ Array.isArray(downloaded?.data);
69
+ return { reachable, roleKey, capabilityKeys };
70
+ } catch {
71
+ return { reachable: false, roleKey, capabilityKeys };
72
+ }
73
+ }
74
+
75
+ module.exports = {
76
+ EVIDENCE_AUTHORING_CAPABILITY_KEYS,
77
+ pickEvidenceRoleKey,
78
+ probeEvidenceAuthoringReachable
79
+ };
@@ -8,8 +8,15 @@
8
8
 
9
9
  'use strict';
10
10
 
11
- const { ApiClient } = require('../api/index');
11
+ const { ApiClient, createDataplaneApiClient } = require('../api/index');
12
12
  const { fetchDataplaneGeneralHealth } = require('../api/dataplane-health.api');
13
+ const { resolveChannelRuntimeValues } = require('./channel-runtime-resolver');
14
+ const {
15
+ fetchRuntimeLayerBToolNames,
16
+ isRuntimeMcpGatewayReachable
17
+ } = require('./runtime-tool-catalog');
18
+ const { probeEvidenceAuthoringReachable } = require('./channel-probe-evidence');
19
+ const { RUNTIME_ORCHESTRATION_SYSTEM_KEY } = require('./chatgpt-runtime-contract');
13
20
 
14
21
  /**
15
22
  * @async
@@ -35,12 +42,48 @@ async function isUrlReachable(url) {
35
42
  * @returns {Promise<boolean>}
36
43
  */
37
44
  async function isAssistantCatalogReachable(dataplaneUrl, authConfig) {
38
- const client = require('../api/index').createDataplaneApiClient(dataplaneUrl, authConfig);
45
+ const summary = await fetchAssistantCatalogSummary(dataplaneUrl, authConfig);
46
+ return summary.reachable;
47
+ }
48
+
49
+ /**
50
+ * @param {Object} response
51
+ * @returns {Object[]}
52
+ */
53
+ function extractAssistantCatalogItems(response) {
54
+ const body = response?.data ?? response;
55
+ const data = body?.data ?? body;
56
+ if (Array.isArray(data)) {
57
+ return data;
58
+ }
59
+ if (Array.isArray(data?.items)) {
60
+ return data.items;
61
+ }
62
+ if (Array.isArray(data?.workers)) {
63
+ return data.workers;
64
+ }
65
+ if (Array.isArray(data?.assistants)) {
66
+ return data.assistants;
67
+ }
68
+ return [];
69
+ }
70
+
71
+ /**
72
+ * @async
73
+ * @param {string} dataplaneUrl
74
+ * @param {Object} authConfig
75
+ * @returns {Promise<{ reachable: boolean, items: Object[] }>}
76
+ */
77
+ async function fetchAssistantCatalogSummary(dataplaneUrl, authConfig) {
78
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
39
79
  try {
40
80
  const res = await client.get('/api/v1/workers/available', { timeoutMs: 10000 });
41
- return res && res.success !== false;
81
+ return {
82
+ reachable: Boolean(res && res.success !== false),
83
+ items: extractAssistantCatalogItems(res)
84
+ };
42
85
  } catch {
43
- return false;
86
+ return { reachable: false, items: [] };
44
87
  }
45
88
  }
46
89
 
@@ -59,31 +102,240 @@ function evaluateInboundVerification(channelExt) {
59
102
  }
60
103
 
61
104
  /**
62
- * @async
63
105
  * @param {Object} ctx
64
106
  * @param {Object|null} channelExt
65
- * @returns {Promise<Object>}
107
+ * @param {boolean} catalogOk
108
+ * @param {string[]} layerBToolNames
109
+ * @param {Object[]} [catalogItems]
110
+ * @returns {Promise<{ probeTiers: Object, runtimeChecks: Object, evidenceCapabilityKeys?: string[] }>}
66
111
  */
67
- async function runChannelProbe(ctx, channelExt) {
68
- const health = await fetchDataplaneGeneralHealth(ctx.dataplaneUrl, { timeoutMs: 8000 });
69
- const catalogOk = await isAssistantCatalogReachable(ctx.dataplaneUrl, ctx.authConfig);
70
- const callbackOk = health?.status === 'healthy' || health?.status === 'ok';
112
+ /**
113
+ * @param {Object|null} channelExt
114
+ * @param {Object} runtime
115
+ * @param {Object} runtimeChecks
116
+ * @param {Object} evidence
117
+ * @returns {Object}
118
+ */
119
+ function buildChatGptProbePayload(channelExt, runtime, runtimeChecks, evidence) {
120
+ return {
121
+ probeTiers: {
122
+ locallyValidated: Boolean(
123
+ channelExt?.credentialKey &&
124
+ channelExt?.artifactHash &&
125
+ runtime.mcpUrl &&
126
+ !runtime.mcpUrl.includes('/api/v1/chatgpt/')
127
+ ),
128
+ runtimeValidated: Object.values(runtimeChecks).every(Boolean),
129
+ requiresChatGptWorkspaceAdmin: true
130
+ },
131
+ runtimeChecks,
132
+ evidenceCapabilityKeys: evidence.capabilityKeys,
133
+ evidenceRoleKey: evidence.roleKey
134
+ };
135
+ }
71
136
 
72
- const inbound = evaluateInboundVerification(channelExt);
137
+ /**
138
+ * @param {Object} ctx
139
+ * @param {Object|null} channelExt
140
+ * @param {boolean} catalogOk
141
+ * @param {string[]} layerBToolNames
142
+ * @param {Object[]} [catalogItems]
143
+ * @returns {Promise<{ probeTiers: Object, runtimeChecks: Object, evidenceCapabilityKeys?: string[] }>}
144
+ */
145
+ async function buildChatGptProbeDetails(
146
+ ctx,
147
+ channelExt,
148
+ catalogOk,
149
+ layerBToolNames,
150
+ catalogItems = []
151
+ ) {
152
+ const runtime = resolveChannelRuntimeValues({
153
+ dataplaneBaseUrl: ctx.dataplaneUrl,
154
+ channelTarget: 'chatgpt',
155
+ appName: channelExt?.name || 'chatgpt',
156
+ baseName: channelExt?.baseName || channelExt?.name || 'chatgpt',
157
+ appDescription: channelExt?.description || 'ChatGPT custom MCP app',
158
+ layerBToolNames
159
+ });
160
+ const mcpHandshakeReachable = await isRuntimeMcpGatewayReachable(
161
+ ctx.dataplaneUrl,
162
+ ctx.authConfig,
163
+ RUNTIME_ORCHESTRATION_SYSTEM_KEY
164
+ );
165
+ const evidence = await probeEvidenceAuthoringReachable(ctx, catalogItems);
166
+ const runtimeChecks = {
167
+ mcpHandshakeReachable,
168
+ layerBToolNamesDiscovered: Array.isArray(layerBToolNames) && layerBToolNames.length > 0,
169
+ roleAssistantResolutionReachable: catalogOk,
170
+ tenantEnvironmentBoundaryConfirmed: Boolean(ctx.environment || channelExt?.name),
171
+ evidenceAuthoringReachable: evidence.reachable
172
+ };
173
+ return buildChatGptProbePayload(channelExt, runtime, runtimeChecks, evidence);
174
+ }
73
175
 
176
+ /**
177
+ * @param {Object} ctx
178
+ * @param {Object|null} channelExt
179
+ * @param {boolean} catalogOk
180
+ * @param {string[]} layerBToolNames
181
+ * @returns {Promise<{ probeTiers: Object, runtimeChecks: Object }>}
182
+ */
183
+ async function buildMicrosoftCopilotProbeDetails(ctx, channelExt, catalogOk, layerBToolNames) {
184
+ const runtime = resolveChannelRuntimeValues({
185
+ dataplaneBaseUrl: ctx.dataplaneUrl,
186
+ channelTarget: 'microsoft-copilot',
187
+ appName: channelExt?.name || 'microsoft-copilot',
188
+ baseName: channelExt?.baseName || channelExt?.name || 'microsoft-copilot',
189
+ appDescription: channelExt?.description || 'Microsoft Copilot package channel',
190
+ layerBToolNames
191
+ });
192
+ const mcpHandshakeReachable = await isRuntimeMcpGatewayReachable(
193
+ ctx.dataplaneUrl,
194
+ ctx.authConfig,
195
+ RUNTIME_ORCHESTRATION_SYSTEM_KEY
196
+ );
197
+ const runtimeChecks = {
198
+ mcpHandshakeReachable,
199
+ layerBToolNamesDiscovered: Array.isArray(layerBToolNames) && layerBToolNames.length > 0,
200
+ authMetadataConfigured: Boolean(runtime.runtimeAuthMode && channelExt?.credentialKey),
201
+ roleAssistantResolutionReachable: catalogOk,
202
+ tenantEnvironmentBoundaryConfirmed: Boolean(ctx.environment || channelExt?.name)
203
+ };
204
+ return {
205
+ probeTiers: {
206
+ builderValidated: Boolean(
207
+ channelExt?.credentialKey &&
208
+ channelExt?.artifactHash &&
209
+ runtime.mcpUrl &&
210
+ !runtime.mcpUrl.includes('/api/v1/copilot/')
211
+ ),
212
+ runtimeValidated: Object.values(runtimeChecks).every(Boolean),
213
+ requiresMicrosoftAdmin: true
214
+ },
215
+ runtimeChecks
216
+ };
217
+ }
218
+
219
+ /**
220
+ * @param {Object} health
221
+ * @param {boolean} callbackOk
222
+ * @param {boolean} catalogOk
223
+ * @param {Object|null} channelExt
224
+ * @param {Object} inbound
225
+ * @returns {Object}
226
+ */
227
+ function buildBaseProbeResult(health, callbackOk, catalogOk, channelExt, inbound) {
74
228
  return {
75
229
  dataplaneHealth: health?.status || 'unknown',
76
230
  callbackReachable: callbackOk,
77
231
  assistantCatalogReachable: catalogOk,
78
232
  credentialValid: Boolean(channelExt?.credentialKey),
79
- inboundVerification: inbound,
80
- passed: callbackOk && catalogOk && Boolean(channelExt?.credentialKey)
233
+ inboundVerification: inbound
81
234
  };
82
235
  }
83
236
 
237
+ /**
238
+ * @param {Object} base
239
+ * @param {Object} probe
240
+ * @param {Object|null} channelExt
241
+ * @returns {Object}
242
+ */
243
+ function buildCopilotProbeResult(base, probe, channelExt) {
244
+ return {
245
+ ...base,
246
+ ...probe,
247
+ expectedLayerBToolNames: [...(probe.discoveredLayerBToolNames || [])],
248
+ passed:
249
+ probe.probeTiers.builderValidated &&
250
+ probe.probeTiers.runtimeValidated &&
251
+ Boolean(channelExt?.credentialKey)
252
+ };
253
+ }
254
+
255
+ /**
256
+ * @param {Object} base
257
+ * @param {Object} probe
258
+ * @param {Object|null} channelExt
259
+ * @returns {Object}
260
+ */
261
+ function buildChatGptProbeResult(base, probe, channelExt) {
262
+ return {
263
+ ...base,
264
+ ...probe,
265
+ expectedLayerBToolNames: [...(probe.discoveredLayerBToolNames || [])],
266
+ passed:
267
+ probe.probeTiers.locallyValidated &&
268
+ probe.probeTiers.runtimeValidated &&
269
+ Boolean(channelExt?.credentialKey)
270
+ };
271
+ }
272
+
273
+ /**
274
+ * @async
275
+ * @param {Object} ctx
276
+ * @param {Object|null} channelExt
277
+ * @returns {Promise<Object>}
278
+ */
279
+ async function runChannelProbe(ctx, channelExt) {
280
+ const health = await fetchDataplaneGeneralHealth(ctx.dataplaneUrl, { timeoutMs: 8000 });
281
+ const catalog = await fetchAssistantCatalogSummary(ctx.dataplaneUrl, ctx.authConfig);
282
+ const catalogOk = catalog.reachable;
283
+ const callbackOk = health?.status === 'healthy' || health?.status === 'ok';
284
+ const inbound = evaluateInboundVerification(channelExt);
285
+ const base = buildBaseProbeResult(health, callbackOk, catalogOk, channelExt, inbound);
286
+
287
+ if (channelExt?.type !== 'chatgpt' && channelExt?.type !== 'microsoftCopilot') {
288
+ return {
289
+ ...base,
290
+ passed: callbackOk && catalogOk && Boolean(channelExt?.credentialKey)
291
+ };
292
+ }
293
+
294
+ const channelTarget = channelExt?.type === 'chatgpt' ? 'chatgpt' : 'microsoft-copilot';
295
+ const layerBToolNames = await fetchRuntimeLayerBToolNames(
296
+ ctx.dataplaneUrl,
297
+ ctx.authConfig,
298
+ channelTarget
299
+ );
300
+
301
+ if (channelExt?.type === 'microsoftCopilot') {
302
+ const copilotProbe = await buildMicrosoftCopilotProbeDetails(
303
+ ctx,
304
+ channelExt,
305
+ catalogOk,
306
+ layerBToolNames
307
+ );
308
+ return buildCopilotProbeResult(
309
+ base,
310
+ { ...copilotProbe, discoveredLayerBToolNames: layerBToolNames },
311
+ channelExt
312
+ );
313
+ }
314
+
315
+ const chatgptProbe = await buildChatGptProbeDetails(
316
+ ctx,
317
+ channelExt,
318
+ catalogOk,
319
+ layerBToolNames,
320
+ catalog.items
321
+ );
322
+ return buildChatGptProbeResult(
323
+ base,
324
+ { ...chatgptProbe, discoveredLayerBToolNames: layerBToolNames },
325
+ channelExt
326
+ );
327
+ }
328
+
84
329
  module.exports = {
85
330
  isUrlReachable,
86
331
  isAssistantCatalogReachable,
332
+ extractAssistantCatalogItems,
333
+ fetchAssistantCatalogSummary,
87
334
  evaluateInboundVerification,
335
+ buildChatGptProbeDetails,
336
+ buildMicrosoftCopilotProbeDetails,
337
+ buildBaseProbeResult,
338
+ buildCopilotProbeResult,
339
+ buildChatGptProbeResult,
88
340
  runChannelProbe
89
341
  };