@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
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Optional build.deployJob — one-shot before long-running aifabrix run start.
3
+ *
4
+ * @fileoverview Run deployJob once per aifabrix run
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ const config = require('../core/config');
10
+ const logger = require('../utils/logger');
11
+ const {
12
+ infoLine,
13
+ formatSuccessLine,
14
+ formatBlockingError
15
+ } = require('../utils/cli-test-layout-chalk');
16
+ const { runDockerRunEphemeral } = require('../utils/docker-run-ephemeral');
17
+ const { getInfraBridgeNetworkName } = require('../infrastructure/orphan-infra-docker-teardown');
18
+
19
+ /**
20
+ * Resolve trimmed deployJob from app config, or null if unset.
21
+ * @param {Object} appConfig - Application configuration
22
+ * @returns {string|null}
23
+ */
24
+ function resolveDeployJobCommand(appConfig) {
25
+ const raw = appConfig?.build?.deployJob;
26
+ if (typeof raw !== 'string') {
27
+ return null;
28
+ }
29
+ const trimmed = raw.trim();
30
+ return trimmed ? trimmed : null;
31
+ }
32
+
33
+ /**
34
+ * Developer infra bridge so ephemeral jobs resolve compose DNS (e.g. postgres).
35
+ * @returns {Promise<string>}
36
+ */
37
+ async function resolveDeployJobNetwork() {
38
+ const developerId = await config.getDeveloperId();
39
+ return getInfraBridgeNetworkName(developerId);
40
+ }
41
+
42
+ /**
43
+ * If build.deployJob is set, run it once in an ephemeral container (same image + env).
44
+ * Fail-closed on non-zero exit. No-op when unset.
45
+ *
46
+ * When `devMountPath` is set (aifabrix run --reload), bind-mount that path at `/app`
47
+ * so migrate/deploy sees the same tree as the long-running reload container.
48
+ *
49
+ * @async
50
+ * @param {Object} params
51
+ * @param {Object} params.appConfig - Application configuration
52
+ * @param {string} params.fullImage - Image:tag used for the run
53
+ * @param {string|null} [params.runEnvPath] - Ephemeral .env.run path for --env-file
54
+ * @param {string|null} [params.devMountPath] - Host path mounted at /app for --reload
55
+ * @param {boolean} [params.debug]
56
+ * @returns {Promise<void>}
57
+ * @throws {Error} If deployJob exits non-zero
58
+ */
59
+ async function runDeployJobIfConfigured({
60
+ appConfig,
61
+ fullImage,
62
+ runEnvPath = null,
63
+ devMountPath = null,
64
+ debug = false
65
+ }) {
66
+ const cmd = resolveDeployJobCommand(appConfig);
67
+ if (!cmd) {
68
+ return;
69
+ }
70
+ if (!fullImage || typeof fullImage !== 'string') {
71
+ throw new Error('build.deployJob requires a resolved container image');
72
+ }
73
+ const network = await resolveDeployJobNetwork();
74
+ const volumeMounts = _deployJobVolumeMounts(devMountPath);
75
+ logger.log(infoLine(`Running build.deployJob once before start (${fullImage})…`));
76
+ if (debug) {
77
+ logger.log(infoLine(`deployJob: ${cmd}`));
78
+ logger.log(infoLine(`deployJob network: ${network}`));
79
+ if (volumeMounts.length) {
80
+ logger.log(infoLine(`deployJob mount: ${volumeMounts[0].host} -> /app`));
81
+ }
82
+ }
83
+ const code = await runDockerRunEphemeral(fullImage, cmd, runEnvPath, {
84
+ network,
85
+ volumeMounts
86
+ });
87
+ if (code !== 0) {
88
+ logger.log(formatBlockingError(`build.deployJob failed with exit code ${code}`));
89
+ throw new Error(`build.deployJob failed with exit code ${code}`);
90
+ }
91
+ logger.log(formatSuccessLine('build.deployJob completed'));
92
+ }
93
+
94
+ /**
95
+ * @param {string|null|undefined} devMountPath
96
+ * @returns {Array<{ host: string, container: string }>}
97
+ */
98
+ function _deployJobVolumeMounts(devMountPath) {
99
+ if (typeof devMountPath !== 'string' || !devMountPath.trim()) {
100
+ return [];
101
+ }
102
+ return [{ host: devMountPath.trim(), container: '/app' }];
103
+ }
104
+
105
+ module.exports = {
106
+ resolveDeployJobCommand,
107
+ runDeployJobIfConfigured
108
+ };
@@ -34,7 +34,12 @@ const runEnvCompose = require('./run-env-compose');
34
34
  const { getAppInfraRequirements } = require('./run-infra-requirements');
35
35
  const { resolveRunImage } = require('./run-resolve-image');
36
36
  const { startContainer } = require('./run-container-start');
37
- const { resolveEnvOutputPath, writeEnvOutputForReload, writeEnvOutputForLocal } = require('../utils/env-copy');
37
+ const {
38
+ resolveEnvOutputPath,
39
+ resolveEnvLocalOutputPath,
40
+ writeEnvOutputForReload,
41
+ writeEnvOutputForLocal
42
+ } = require('../utils/env-copy');
38
43
  const { resolveVersionForApp } = require('../utils/image-version');
39
44
  const healthCheckUtil = require('../utils/health-check');
40
45
  const { computeTraefikPublicAppUrl } = require('../utils/health-check-url');
@@ -303,8 +308,8 @@ async function generateComposeFile(appName, appConfig, composeOptions, devDir, l
303
308
 
304
309
  /**
305
310
  * Writes `build.envOutputPath` after each `aifabrix run`: **local**-flavored env for the host/IDE
306
- * when not using `--reload`; with **`--reload`**, merges container `.env.run` into the existing file
307
- * (preserves resolve comments) without appending run-only keys missing from the template.
311
+ * when not using `--reload`; with **`--reload`**, merges container `.env.run` into `.env` and also
312
+ * writes a sibling **`.env.local`** with localhost / published-port URLs for host CLI and manual tests.
308
313
  *
309
314
  * @async
310
315
  * @param {string} appName - Application name
@@ -326,6 +331,8 @@ async function writeEnvOutputIfConfigured(appName, appConfig, runEnvPath, option
326
331
  }
327
332
  if (options && options.reload === true) {
328
333
  await writeEnvOutputForReload(outputPath, runEnvPath, appName);
334
+ const localOutputPath = resolveEnvLocalOutputPath(outputPath);
335
+ await writeEnvOutputForLocal(appName, localOutputPath);
329
336
  } else {
330
337
  await writeEnvOutputForLocal(appName, outputPath);
331
338
  }
package/lib/app/run.js CHANGED
@@ -404,6 +404,14 @@ async function runApp(appName, options = {}) {
404
404
  logger.log(chalk.gray(`[DEBUG] Compose file generated: ${prepared.tempComposePath}`));
405
405
  }
406
406
  logReloadDevSummary(Boolean(prepared.mergedRunOptions.reload), prepared.mergedRunOptions.reloadSyncSummary);
407
+ const { runDeployJobIfConfigured } = require('./run-deploy-job');
408
+ await runDeployJobIfConfigured({
409
+ appConfig: prepared.appConfig,
410
+ fullImage: prepared.mergedRunOptions.image,
411
+ runEnvPath: prepared.runEnvPath,
412
+ devMountPath: prepared.devMountPath || null,
413
+ debug
414
+ });
407
415
  await startAppContainer(appName, prepared.tempComposePath, prepared.hostPort, prepared.appConfig, {
408
416
  debug,
409
417
  runEnvPath: prepared.runEnvPath,
@@ -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,6 +14,7 @@ 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];
19
20
  const PLACEHOLDER_DOMAIN_RE = /\byour-domain\.com\b/i;
@@ -115,11 +116,22 @@ function renderInstallGuideIfPresent(templatesDir, runtimeValues, outputDir, dry
115
116
  */
116
117
  function generateChannelArtifact(params) {
117
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
+
118
132
  const manifestTemplate = path.join(templatesDir, 'manifest.template.json.hbs');
119
133
  const content = renderTemplateFile(manifestTemplate, params.runtimeValues);
120
134
  assertNoSecretsInArtifact(content);
121
-
122
- const outputPath = getArtifactOutputPath(params.projectRoot, params.channelTarget);
123
135
  const artifactHash = crypto.createHash('sha256').update(content, 'utf8').digest('hex');
124
136
  const outputDir = path.dirname(outputPath);
125
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
+ };