@aifabrix/builder 2.51.1 → 2.52.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/jest.isolated-projects.js +25 -2
  2. package/jest.projects.js +4 -1
  3. package/lib/api/datasources-extended.api.js +8 -8
  4. package/lib/api/keycloak.api.js +92 -0
  5. package/lib/api/token-providers.api.js +77 -0
  6. package/lib/api/types/keycloak.types.js +23 -0
  7. package/lib/api/types/token-providers.types.js +65 -0
  8. package/lib/app/down.js +16 -1
  9. package/lib/app/run-container-start.js +4 -0
  10. package/lib/app/run-deploy-job.js +135 -27
  11. package/lib/app/run-env-compose.js +32 -0
  12. package/lib/app/run-env-recovery.js +35 -0
  13. package/lib/app/run-helpers.js +5 -6
  14. package/lib/app/run.js +11 -3
  15. package/lib/channels/add-channel.js +126 -19
  16. package/lib/channels/approval-guides/chatgpt.js +12 -9
  17. package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
  18. package/lib/channels/channel-add-ensure.js +128 -0
  19. package/lib/channels/channel-artifact-generator.js +11 -1
  20. package/lib/channels/channel-branding.js +94 -0
  21. package/lib/channels/channel-credential.js +8 -0
  22. package/lib/channels/channel-discovery.js +330 -0
  23. package/lib/channels/channel-external-system.js +1 -1
  24. package/lib/channels/channel-runtime-resolver.js +103 -14
  25. package/lib/channels/channel-status-display.js +7 -1
  26. package/lib/channels/channel-targets.js +18 -1
  27. package/lib/channels/chatgpt-runtime-contract.js +1 -1
  28. package/lib/channels/microsoft-copilot-channel-add.js +138 -0
  29. package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
  30. package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
  31. package/lib/channels/microsoft-copilot-icons.js +74 -0
  32. package/lib/channels/microsoft-copilot-limits.js +84 -0
  33. package/lib/channels/microsoft-copilot-package.js +130 -49
  34. package/lib/channels/microsoft-copilot-validate.js +197 -0
  35. package/lib/cli/index.js +2 -0
  36. package/lib/cli/infra-guided-footers.js +1 -1
  37. package/lib/cli/infra-guided-public-url-warn.js +55 -0
  38. package/lib/cli/infra-guided.js +78 -62
  39. package/lib/cli/setup-app.help.js +4 -0
  40. package/lib/cli/setup-app.test-commands.js +59 -28
  41. package/lib/cli/setup-external-system.js +50 -12
  42. package/lib/cli/setup-infra.js +31 -4
  43. package/lib/commands/channel-add-setup.js +35 -10
  44. package/lib/commands/channel.js +27 -12
  45. package/lib/commands/deploy-gate-orchestrator.js +3 -2
  46. package/lib/commands/governance-verify-external.js +2 -5
  47. package/lib/commands/integration-definition.js +158 -0
  48. package/lib/commands/lifecycle-external.js +14 -7
  49. package/lib/commands/repair-auth-apply.js +19 -5
  50. package/lib/commands/repair-datasource-sync-operations.js +4 -1
  51. package/lib/commands/role-assistant-test-output.js +200 -0
  52. package/lib/commands/role-assistant.js +131 -114
  53. package/lib/commands/test-governance-external.js +2 -5
  54. package/lib/commands/up-miso.js +61 -24
  55. package/lib/commands/upload-data-sync-finalize.js +20 -3
  56. package/lib/commands/verify-governance-external.js +2 -5
  57. package/lib/commands/verify-operations-external.js +2 -5
  58. package/lib/commands/verify-operations-step-plan.js +31 -0
  59. package/lib/commands/verify-trust-command-action.js +2 -5
  60. package/lib/datasource/capability/capability-resolve.js +15 -5
  61. package/lib/datasource/capability/copy-operations.js +14 -4
  62. package/lib/datasource/datasource-identifier.js +4 -3
  63. package/lib/datasource/integration-context.js +3 -3
  64. package/lib/datasource/resolve-app.js +90 -8
  65. package/lib/datasource/test-e2e.js +2 -2
  66. package/lib/datasource/unified-validation-run-body.js +15 -1
  67. package/lib/datasource/unified-validation-run-resolve.js +2 -2
  68. package/lib/external-system/deploy-helpers.js +9 -5
  69. package/lib/external-system/deploy.js +2 -1
  70. package/lib/external-system/test-system-level.js +21 -3
  71. package/lib/external-system/test.js +4 -4
  72. package/lib/generator/builders.js +9 -15
  73. package/lib/infrastructure/index.js +19 -4
  74. package/lib/integration-definition/apply.js +478 -0
  75. package/lib/integration-definition/canonical-json.js +51 -0
  76. package/lib/integration-definition/emit-suite-bid.js +94 -0
  77. package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
  78. package/lib/integration-definition/enrich-openapi-operations.js +434 -0
  79. package/lib/integration-definition/from-discovery.js +339 -0
  80. package/lib/integration-definition/hash.js +37 -0
  81. package/lib/integration-definition/index.js +37 -0
  82. package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +205 -0
  83. package/lib/integration-definition/resource-type-catalog.js +59 -0
  84. package/lib/integration-definition/system-kind-enum.js +40 -0
  85. package/lib/integration-definition/validate.js +148 -0
  86. package/lib/programmatic/builder-api-request.js +5 -1
  87. package/lib/programmatic/builder-help-governance.js +3 -2
  88. package/lib/programmatic/capability-registry.js +19 -0
  89. package/lib/programmatic/openapi-component-schemas.js +6 -1
  90. package/lib/programmatic/openapi-descriptions.js +40 -0
  91. package/lib/programmatic/openapi-envelope-examples.js +269 -0
  92. package/lib/programmatic/openapi-operation-bindings.js +45 -0
  93. package/lib/programmatic/openapi-request-schemas.js +14 -0
  94. package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
  95. package/lib/programmatic/openapi-result-schemas.js +46 -99
  96. package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
  97. package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
  98. package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -0
  99. package/lib/programmatic/openapi-schema-helpers.js +9 -9
  100. package/lib/programmatic/openapi-spec.js +11 -1
  101. package/lib/programmatic/orchestration-openapi-meta.js +10 -0
  102. package/lib/programmatic/route-handler-map.js +29 -1
  103. package/lib/programmatic/run-channel-package.js +236 -0
  104. package/lib/programmatic/run-integration-definition.js +69 -0
  105. package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
  106. package/lib/role-assistant/knowledge-sync-logging.js +58 -0
  107. package/lib/role-assistant/knowledge-sync.js +104 -89
  108. package/lib/role-assistant/test-cases-bid.js +155 -0
  109. package/lib/role-assistant/test-cases-evaluate.js +9 -1
  110. package/lib/role-assistant/test-cases-honesty.js +24 -10
  111. package/lib/role-assistant/test-cases.js +28 -0
  112. package/lib/role-assistant/test-runner-bid-apply.js +122 -0
  113. package/lib/role-assistant/test-runner-bid-document.js +278 -0
  114. package/lib/role-assistant/test-runner-workhub-answers.js +289 -46
  115. package/lib/role-assistant/test-runner-workhub.js +121 -25
  116. package/lib/role-assistant/test-suite.js +119 -0
  117. package/lib/schema/application-schema.json +64 -32
  118. package/lib/schema/external-datasource.schema.json +103 -17
  119. package/lib/schema/external-system.schema.json +54 -3
  120. package/lib/schema/infra.parameter.yaml +0 -20
  121. package/lib/schema/integration-definition.schema.json +195 -0
  122. package/lib/utils/authentication-shape.js +6 -0
  123. package/lib/utils/certify-external-workspace.js +40 -0
  124. package/lib/utils/docker-build.js +3 -3
  125. package/lib/utils/docker-run-ephemeral.js +77 -17
  126. package/lib/utils/health-check-public-warn.js +31 -13
  127. package/lib/utils/health-check.js +11 -1
  128. package/lib/utils/help-builder.js +1 -0
  129. package/lib/utils/paths.js +13 -15
  130. package/lib/utils/promote-dev-docker-image.js +97 -18
  131. package/lib/utils/role-assistant-paths.js +76 -14
  132. package/lib/utils/transient-developer-id.js +28 -0
  133. package/lib/utils/upload-sync-options.js +7 -4
  134. package/lib/validation/reserved-system-key-prefixes.js +56 -0
  135. package/lib/validation/validate.js +54 -19
  136. package/package.json +1 -1
  137. package/scripts/pnpm/af.mjs +24 -9
  138. package/scripts/pnpm/help.mjs +1 -1
  139. package/templates/applications/builder-api/application.yaml +3 -3
  140. package/templates/applications/dataplane/application.yaml +5 -5
  141. package/templates/applications/dataplane/env.template +8 -7
  142. package/templates/applications/dataplane/rbac.yaml +10 -10
  143. package/templates/applications/keycloak/application.yaml +2 -2
  144. package/templates/applications/keycloak/env.template +5 -2
  145. package/templates/applications/miso-controller/application.yaml +25 -11
  146. package/templates/applications/miso-controller/env.template +24 -5
  147. package/templates/channels/README.md +11 -1
  148. package/templates/channels/brand/color-192.png +0 -0
  149. package/templates/channels/brand/dark/128.png +0 -0
  150. package/templates/channels/brand/dark/192.png +0 -0
  151. package/templates/channels/brand/dark/256.png +0 -0
  152. package/templates/channels/brand/dark/32.png +0 -0
  153. package/templates/channels/brand/dark/512.png +0 -0
  154. package/templates/channels/brand/dark/64.png +0 -0
  155. package/templates/channels/brand/light/128.png +0 -0
  156. package/templates/channels/brand/light/192.png +0 -0
  157. package/templates/channels/brand/light/256.png +0 -0
  158. package/templates/channels/brand/light/32.png +0 -0
  159. package/templates/channels/brand/light/512.png +0 -0
  160. package/templates/channels/brand/light/64.png +0 -0
  161. package/templates/channels/brand/mono-white/128.png +0 -0
  162. package/templates/channels/brand/mono-white/192.png +0 -0
  163. package/templates/channels/brand/mono-white/256.png +0 -0
  164. package/templates/channels/brand/mono-white/32.png +0 -0
  165. package/templates/channels/brand/mono-white/512.png +0 -0
  166. package/templates/channels/brand/mono-white/64.png +0 -0
  167. package/templates/channels/brand/outline-32.png +0 -0
  168. package/templates/channels/chatgpt/install.template.md.hbs +76 -36
  169. package/templates/channels/chatgpt/integration/README.md +4 -4
  170. package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
  171. package/templates/channels/chatgpt/manifest.template.json.hbs +3 -3
  172. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +10 -16
  173. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
  174. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
  175. package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
  176. package/templates/channels/microsoft-copilot/integration/README.md +2 -3
  177. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
  178. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +7 -7
  179. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -0
  180. package/templates/channels/slack/install.template.md.hbs +6 -1
  181. package/templates/channels/slack/integration/README.md +2 -4
  182. package/templates/channels/teams-bot/install.template.md.hbs +4 -0
  183. package/templates/channels/teams-bot/integration/README.md +3 -5
  184. package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
  185. package/templates/python/docker-compose.hbs +10 -0
  186. package/templates/typescript/docker-compose.hbs +10 -0
  187. /package/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Microsoft Copilot / Teams package string limits.
3
+ *
4
+ * @fileoverview Truncate and normalize Copilot package display fields to schema limits
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const TEAMS_NAME_SHORT_MAX = 30;
12
+ const TEAMS_NAME_FULL_MAX = 100;
13
+ const TEAMS_DESCRIPTION_SHORT_MAX = 80;
14
+ const TEAMS_DESCRIPTION_FULL_MAX = 4000;
15
+ const PLUGIN_NAME_FOR_HUMAN_MAX = 20;
16
+ const PLUGIN_DESCRIPTION_FOR_HUMAN_MAX = 100;
17
+
18
+ /**
19
+ * @param {string} text
20
+ * @param {number} max
21
+ * @returns {string}
22
+ */
23
+ function truncateForSchema(text, max) {
24
+ const value = String(text ?? '').trim();
25
+ if (!value) return '';
26
+ if (value.length <= max) return value;
27
+ if (max <= 1) return value.slice(0, max);
28
+ return `${value.slice(0, max - 1)}…`;
29
+ }
30
+
31
+ /**
32
+ * Plugin namespace: lowercase alphanumeric/hyphen only (plugin schema ^[A-Za-z0-9-]+$).
33
+ * @param {string} baseName
34
+ * @returns {string}
35
+ */
36
+ function buildPluginNamespace(baseName) {
37
+ const raw = String(baseName ?? 'aifabrix')
38
+ .trim()
39
+ .toLowerCase()
40
+ .replace(/[^a-z0-9-]+/g, '-')
41
+ .replace(/-+/g, '-')
42
+ .replace(/^-|-$/g, '');
43
+ const ns = raw || 'aifabrix';
44
+ return truncateForSchema(ns, 32).replace(/[^a-z0-9-]/g, '') || 'aifabrix';
45
+ }
46
+
47
+ /**
48
+ * @param {Object} runtimeValues
49
+ * @returns {Object}
50
+ */
51
+ function applyMicrosoftCopilotDisplayLimits(runtimeValues) {
52
+ const appDescription = String(runtimeValues.appDescription || '').trim();
53
+ const baseName = String(runtimeValues.baseName || runtimeValues.appName || 'Assistant').trim();
54
+ const appName = String(runtimeValues.appName || baseName).trim();
55
+ runtimeValues.shortName = truncateForSchema(baseName, TEAMS_NAME_SHORT_MAX);
56
+ runtimeValues.fullName = truncateForSchema(appName, TEAMS_NAME_FULL_MAX);
57
+ runtimeValues.shortDescription = truncateForSchema(
58
+ appDescription || 'Enterprise AI assistant via AI Fabrix Runtime.',
59
+ TEAMS_DESCRIPTION_SHORT_MAX
60
+ );
61
+ runtimeValues.fullDescription = truncateForSchema(
62
+ appDescription || runtimeValues.shortDescription,
63
+ TEAMS_DESCRIPTION_FULL_MAX
64
+ );
65
+ runtimeValues.pluginNameForHuman = truncateForSchema(baseName, PLUGIN_NAME_FOR_HUMAN_MAX);
66
+ runtimeValues.pluginDescriptionForHuman = truncateForSchema(
67
+ appDescription || runtimeValues.shortDescription,
68
+ PLUGIN_DESCRIPTION_FOR_HUMAN_MAX
69
+ );
70
+ runtimeValues.pluginNamespace = buildPluginNamespace(baseName);
71
+ return runtimeValues;
72
+ }
73
+
74
+ module.exports = {
75
+ TEAMS_NAME_SHORT_MAX,
76
+ TEAMS_NAME_FULL_MAX,
77
+ TEAMS_DESCRIPTION_SHORT_MAX,
78
+ TEAMS_DESCRIPTION_FULL_MAX,
79
+ PLUGIN_NAME_FOR_HUMAN_MAX,
80
+ PLUGIN_DESCRIPTION_FOR_HUMAN_MAX,
81
+ truncateForSchema,
82
+ buildPluginNamespace,
83
+ applyMicrosoftCopilotDisplayLimits
84
+ };
@@ -9,12 +9,16 @@
9
9
  'use strict';
10
10
 
11
11
  const crypto = require('crypto');
12
- const fs = require('fs');
12
+ const fs = require('../internal/fs-real-sync');
13
13
  const path = require('path');
14
14
  const AdmZip = require('adm-zip');
15
-
16
- const PLACEHOLDER_ICON_BASE64 =
17
- 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO2s9WQAAAAASUVORK5CYII=';
15
+ const { getMicrosoftCopilotIcons } = require('./microsoft-copilot-icons');
16
+ const { assertValidMicrosoftCopilotPackage } = require('./microsoft-copilot-validate');
17
+ const { applyMicrosoftCopilotDisplayLimits } = require('./microsoft-copilot-limits');
18
+ const {
19
+ assertCopilotPackageOauthPluginVaultReference
20
+ } = require('./microsoft-copilot-entra-sso');
21
+ const { renderCopilotChannelTemplate } = require('./microsoft-copilot-entra-sso-form');
18
22
 
19
23
  /**
20
24
  * @param {string} outputPath
@@ -37,16 +41,20 @@ function buildCopilotOutputPaths(outputPath, runtimeValues) {
37
41
  zipFileName,
38
42
  zipPath: path.join(buildDir, zipFileName),
39
43
  validationPath: path.join(installDir, 'validation.json'),
40
- installPath: path.join(installDir, 'README.md')
44
+ installPath: path.join(rootDir, 'INSTALL.md'),
45
+ installReadmePath: path.join(installDir, 'README.md')
41
46
  };
42
47
  }
43
48
 
44
49
  /**
50
+ * @param {Object} [runtimeValues]
45
51
  * @returns {{ color: Buffer, outline: Buffer }}
46
52
  */
47
- function getPlaceholderIcons() {
48
- const icon = Buffer.from(PLACEHOLDER_ICON_BASE64, 'base64');
49
- return { color: icon, outline: icon };
53
+ function resolvePackageIcons(runtimeValues) {
54
+ return getMicrosoftCopilotIcons({
55
+ colorPath: runtimeValues?.iconColorPath,
56
+ outlinePath: runtimeValues?.iconOutlinePath
57
+ });
50
58
  }
51
59
 
52
60
  /**
@@ -67,7 +75,8 @@ function renderCopilotTemplates(runtimeValues, renderTemplateFile, templatesDir)
67
75
  runtimeValues
68
76
  )
69
77
  };
70
- const installText = renderTemplateFile(path.join(templatesDir, 'install.template.md.hbs'), runtimeValues);
78
+ // INSTALL prose + Entra SSO form table live in templates (partial entraSsoRegistration).
79
+ const installText = renderCopilotChannelTemplate('install.template.md.hbs', runtimeValues);
71
80
  return { sourceFiles, installText };
72
81
  }
73
82
 
@@ -112,6 +121,9 @@ function buildCopilotValidation(runtimeValues, paths, sourceFileNames) {
112
121
  runtimeMcpUrl: runtimeValues.mcpUrl,
113
122
  runtimeTransport: runtimeValues.runtimeTransport,
114
123
  expectedLayerBToolNames: runtimeValues.layerBToolNames || [],
124
+ governedToolNames: runtimeValues.layerBToolNames || [],
125
+ installReady: runtimeValues.installReady === true,
126
+ blocker: runtimeValues.discoveryBlocker || null,
115
127
  boundaries: {
116
128
  onePackagePerEnvironment: true,
117
129
  topLevelVendorToolsExposed: false,
@@ -142,6 +154,65 @@ function computeCopilotArtifactHash(parts) {
142
154
  return hash.digest('hex');
143
155
  }
144
156
 
157
+ /**
158
+ * @param {Object} runtimeValues
159
+ * @returns {Object}
160
+ */
161
+ function buildCopilotSummaryRuntime(runtimeValues) {
162
+ return {
163
+ runtimeSystemKey: runtimeValues.runtimeSystemKey,
164
+ remoteMcpUrl: runtimeValues.mcpUrl,
165
+ transport: runtimeValues.runtimeTransport,
166
+ assistantCatalogUrl: runtimeValues.assistantCatalogUrl,
167
+ roleAssistantResolution: 'dynamic-runtime',
168
+ layerBToolNames: runtimeValues.layerBToolNames || [],
169
+ governedToolNames: runtimeValues.layerBToolNames || []
170
+ };
171
+ }
172
+
173
+ /**
174
+ * @param {Object} runtimeValues
175
+ * @returns {Object}
176
+ */
177
+ function buildCopilotSummaryAuthentication(runtimeValues) {
178
+ const discovery = runtimeValues.discoveryAuthentication || null;
179
+ const serialized = discovery ? JSON.stringify(discovery) : '';
180
+ if (/authorizationServerMetadataUrl":"[^"]+"/i.test(serialized)) {
181
+ throw new Error('Keycloak authorizationServerMetadata must not appear in Copilot package summary');
182
+ }
183
+ return {
184
+ mode: runtimeValues.runtimeAuthMode,
185
+ pluginAuthType: 'OAuthPluginVault',
186
+ oauthPluginVaultReferenceId: runtimeValues.oauthPluginVaultReferenceId || null,
187
+ recommendedCapability: discovery?.recommendedCapability || null,
188
+ entraIssuer: runtimeValues.entraIssuer || discovery?.issuer || null,
189
+ entraEnterpriseAppId: runtimeValues.entraEnterpriseAppId || null,
190
+ entraTenantId: runtimeValues.entraTenantId || null,
191
+ entraSsoClientId: runtimeValues.entraSsoClientId || null,
192
+ scopes: runtimeValues.scopes || [],
193
+ discovery,
194
+ note:
195
+ 'Microsoft Entra SSO via OAuthPluginVault (Teams auth-config reference_id); ' +
196
+ 'miso-controller performs exchange — package carries public specs only; no Fabrix OAuth secrets'
197
+ };
198
+ }
199
+
200
+ /**
201
+ * @param {Object} runtimeValues
202
+ * @param {Object} paths
203
+ * @returns {Object}
204
+ */
205
+ function buildCopilotSummaryPackage(runtimeValues, paths) {
206
+ return {
207
+ appId: runtimeValues.packageAppId,
208
+ version: runtimeValues.packageVersion,
209
+ sourceFiles: ['manifest.json', 'declarativeAgent.json', 'ai-plugin.json', 'color.png', 'outline.png'],
210
+ zipFileName: paths.zipFileName,
211
+ installGuide: 'INSTALL.md',
212
+ validationFile: path.relative(paths.rootDir, paths.validationPath)
213
+ };
214
+ }
215
+
145
216
  /**
146
217
  * @param {Object} runtimeValues
147
218
  * @param {Object} paths
@@ -156,27 +227,16 @@ function buildCopilotSummary(runtimeValues, paths, validation, artifactHash) {
156
227
  baseName: runtimeValues.baseName,
157
228
  description: runtimeValues.appDescription,
158
229
  environment: runtimeValues.environment,
159
- runtime: {
160
- runtimeSystemKey: runtimeValues.runtimeSystemKey,
161
- remoteMcpUrl: runtimeValues.mcpUrl,
162
- transport: runtimeValues.runtimeTransport,
163
- assistantCatalogUrl: runtimeValues.assistantCatalogUrl,
164
- roleAssistantResolution: 'dynamic-runtime',
165
- layerBToolNames: runtimeValues.layerBToolNames || []
166
- },
167
- authentication: {
168
- mode: runtimeValues.runtimeAuthMode,
169
- credentialKey: runtimeValues.credentialKey,
170
- credentialPortalUrl: runtimeValues.credentialPortalUrl,
171
- scopes: runtimeValues.scopes || []
172
- },
173
- package: {
174
- appId: runtimeValues.packageAppId,
175
- version: runtimeValues.packageVersion,
176
- sourceFiles: ['manifest.json', 'declarativeAgent.json', 'ai-plugin.json', 'color.png', 'outline.png'],
177
- zipFileName: paths.zipFileName,
178
- installGuide: path.relative(paths.rootDir, paths.installPath),
179
- validationFile: path.relative(paths.rootDir, paths.validationPath)
230
+ runtime: buildCopilotSummaryRuntime(runtimeValues),
231
+ authentication: buildCopilotSummaryAuthentication(runtimeValues),
232
+ package: buildCopilotSummaryPackage(runtimeValues, paths),
233
+ installReady: runtimeValues.installReady === true,
234
+ blocker: runtimeValues.discoveryBlocker || null,
235
+ links: {
236
+ readme: paths.installPath,
237
+ zip: paths.zipPath,
238
+ filesRoot: paths.sourceDir,
239
+ validation: paths.validationPath
180
240
  },
181
241
  probeTiers: {
182
242
  builderValidated: true,
@@ -211,24 +271,21 @@ function writeCopilotArtifacts(params) {
211
271
  fs.writeFileSync(path.join(paths.sourceDir, 'outline.png'), icons.outline);
212
272
  fs.writeFileSync(paths.zipPath, zipBuffer);
213
273
  fs.writeFileSync(paths.installPath, installText, 'utf8');
274
+ if (paths.installReadmePath) {
275
+ fs.writeFileSync(paths.installReadmePath, installText, 'utf8');
276
+ }
214
277
  fs.writeFileSync(paths.validationPath, validationText, 'utf8');
215
278
  fs.writeFileSync(paths.summaryPath, summaryText, 'utf8');
216
279
  }
217
280
 
218
281
  /**
219
282
  * @param {Object} params
220
- * @param {Object} params.runtimeValues
221
- * @param {string} params.templatesDir
222
- * @param {string} params.outputPath
223
- * @param {boolean} [params.dryRun]
224
- * @param {(content: string) => void} params.assertNoSecretsInArtifact
225
- * @param {(templatePath: string, context: Object) => string} params.renderTemplateFile
226
- * @returns {{ outputPath: string, artifactHash: string, content: string, installPath: string }}
283
+ * @param {Object} runtimeValues
284
+ * @param {Object} paths
285
+ * @param {{ color: Buffer, outline: Buffer }} icons
286
+ * @returns {{ summary: Object, summaryText: string, zipBuffer: Buffer, sourceFiles: Object, installText: string, validationText: string }}
227
287
  */
228
- function generateMicrosoftCopilotPackage(params) {
229
- const runtimeValues = params.runtimeValues || {};
230
- const paths = buildCopilotOutputPaths(params.outputPath, runtimeValues);
231
- const icons = getPlaceholderIcons();
288
+ function buildCopilotPackagePayload(params, runtimeValues, paths, icons) {
232
289
  const { sourceFiles, installText } = renderCopilotTemplates(
233
290
  runtimeValues,
234
291
  params.renderTemplateFile,
@@ -236,6 +293,7 @@ function generateMicrosoftCopilotPackage(params) {
236
293
  );
237
294
  assertNoSecretsInSourceFiles(sourceFiles, params.assertNoSecretsInArtifact);
238
295
  params.assertNoSecretsInArtifact(installText);
296
+ assertValidMicrosoftCopilotPackage(sourceFiles, icons);
239
297
  const zipBuffer = buildCopilotZipBuffer(sourceFiles, icons);
240
298
  const validation = buildCopilotValidation(runtimeValues, paths, Object.keys(sourceFiles));
241
299
  const validationText = JSON.stringify(validation, null, 2);
@@ -250,22 +308,45 @@ function generateMicrosoftCopilotPackage(params) {
250
308
  const summary = buildCopilotSummary(runtimeValues, paths, validation, artifactHash);
251
309
  const summaryText = JSON.stringify(summary, null, 2);
252
310
  params.assertNoSecretsInArtifact(summaryText);
311
+ return { summary, summaryText, zipBuffer, sourceFiles, installText, validationText };
312
+ }
313
+
314
+ /**
315
+ * @param {Object} params
316
+ * @param {Object} params.runtimeValues
317
+ * @param {string} params.templatesDir
318
+ * @param {string} params.outputPath
319
+ * @param {boolean} [params.dryRun]
320
+ * @param {(content: string) => void} params.assertNoSecretsInArtifact
321
+ * @param {(templatePath: string, context: Object) => string} params.renderTemplateFile
322
+ * @returns {{ outputPath: string, artifactHash: string, content: string, installPath: string }}
323
+ */
324
+ function generateMicrosoftCopilotPackage(params) {
325
+ const runtimeValues = applyMicrosoftCopilotDisplayLimits({ ...(params.runtimeValues || {}) });
326
+ assertCopilotPackageOauthPluginVaultReference(runtimeValues);
327
+ const paths = buildCopilotOutputPaths(params.outputPath, runtimeValues);
328
+ const icons = resolvePackageIcons(runtimeValues);
329
+ const payload = buildCopilotPackagePayload(params, runtimeValues, paths, icons);
253
330
  if (!params.dryRun) {
254
331
  writeCopilotArtifacts({
255
332
  paths,
256
- sourceFiles,
333
+ sourceFiles: payload.sourceFiles,
257
334
  icons,
258
- zipBuffer,
259
- installText,
260
- validationText,
261
- summaryText
335
+ zipBuffer: payload.zipBuffer,
336
+ installText: payload.installText,
337
+ validationText: payload.validationText,
338
+ summaryText: payload.summaryText
262
339
  });
263
340
  }
264
341
  return {
265
342
  outputPath: paths.summaryPath,
266
- artifactHash,
267
- content: summaryText,
268
- installPath: paths.installPath
343
+ artifactHash: payload.summary.artifact.hash,
344
+ content: payload.summaryText,
345
+ installPath: paths.installPath,
346
+ zipPath: paths.zipPath,
347
+ links: payload.summary.links,
348
+ installReady: payload.summary.installReady === true,
349
+ blocker: payload.summary.blocker || null
269
350
  };
270
351
  }
271
352
 
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Validate Microsoft Copilot package source files before ZIP.
3
+ *
4
+ * @fileoverview Schema-oriented checks for Teams + declarative agent + AI plugin manifests
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const {
12
+ TEAMS_NAME_SHORT_MAX,
13
+ TEAMS_NAME_FULL_MAX,
14
+ TEAMS_DESCRIPTION_SHORT_MAX,
15
+ TEAMS_DESCRIPTION_FULL_MAX,
16
+ PLUGIN_NAME_FOR_HUMAN_MAX,
17
+ PLUGIN_DESCRIPTION_FOR_HUMAN_MAX
18
+ } = require('./microsoft-copilot-limits');
19
+ const { readPngDimensions } = require('./microsoft-copilot-icons');
20
+
21
+ const ALLOWED_AUTH_TYPES = new Set(['None', 'OAuthPluginVault', 'ApiKeyPluginVault']);
22
+
23
+ /**
24
+ * @param {unknown} value
25
+ * @param {string} label
26
+ * @param {number} max
27
+ * @param {string[]} errors
28
+ */
29
+ function assertMaxLength(value, label, max, errors) {
30
+ if (typeof value !== 'string') {
31
+ errors.push(`${label} must be a string`);
32
+ return;
33
+ }
34
+ if (value.length > max) {
35
+ errors.push(`${label} exceeds ${max} characters (got ${value.length})`);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * @param {Record<string, string>} sourceFiles
41
+ * @returns {{ manifest: Object|null, agent: Object|null, plugin: Object|null, errors: string[] }}
42
+ */
43
+ function parsePackageJsonFiles(sourceFiles) {
44
+ const errors = [];
45
+ const parsed = { manifest: null, agent: null, plugin: null };
46
+ try {
47
+ parsed.manifest = JSON.parse(sourceFiles['manifest.json'] || '');
48
+ } catch {
49
+ errors.push('manifest.json is not valid JSON');
50
+ }
51
+ try {
52
+ parsed.agent = JSON.parse(sourceFiles['declarativeAgent.json'] || '');
53
+ } catch {
54
+ errors.push('declarativeAgent.json is not valid JSON');
55
+ }
56
+ try {
57
+ parsed.plugin = JSON.parse(sourceFiles['ai-plugin.json'] || '');
58
+ } catch {
59
+ errors.push('ai-plugin.json is not valid JSON');
60
+ }
61
+ return { ...parsed, errors };
62
+ }
63
+
64
+ /**
65
+ * @param {Object|null} manifest
66
+ * @param {string[]} errors
67
+ */
68
+ function validateTeamsManifest(manifest, errors) {
69
+ if (!manifest) return;
70
+ assertMaxLength(manifest?.name?.short, 'manifest.name.short', TEAMS_NAME_SHORT_MAX, errors);
71
+ assertMaxLength(manifest?.name?.full, 'manifest.name.full', TEAMS_NAME_FULL_MAX, errors);
72
+ assertMaxLength(
73
+ manifest?.description?.short,
74
+ 'manifest.description.short',
75
+ TEAMS_DESCRIPTION_SHORT_MAX,
76
+ errors
77
+ );
78
+ assertMaxLength(
79
+ manifest?.description?.full,
80
+ 'manifest.description.full',
81
+ TEAMS_DESCRIPTION_FULL_MAX,
82
+ errors
83
+ );
84
+ if (!manifest.copilotAgents?.declarativeAgents?.length) {
85
+ errors.push('manifest.copilotAgents.declarativeAgents is required');
86
+ }
87
+ }
88
+
89
+ /**
90
+ * @param {Object|null} agent
91
+ * @param {string[]} errors
92
+ */
93
+ function validateDeclarativeAgent(agent, errors) {
94
+ if (!agent) return;
95
+ if (agent.version !== 'v1.4') {
96
+ errors.push('declarativeAgent.version must be "v1.4"');
97
+ }
98
+ if (!agent.id || !agent.name || !agent.description || !agent.instructions) {
99
+ errors.push('declarativeAgent requires id, name, description, and instructions');
100
+ }
101
+ }
102
+
103
+ /**
104
+ * @param {Object|null} plugin
105
+ * @param {string[]} errors
106
+ */
107
+ function validateAiPlugin(plugin, errors) {
108
+ if (!plugin) return;
109
+ if (plugin.schema_version !== 'v2.4') {
110
+ errors.push('ai-plugin.schema_version must be "v2.4"');
111
+ }
112
+ assertMaxLength(plugin.name_for_human, 'ai-plugin.name_for_human', PLUGIN_NAME_FOR_HUMAN_MAX, errors);
113
+ assertMaxLength(
114
+ plugin.description_for_human,
115
+ 'ai-plugin.description_for_human',
116
+ PLUGIN_DESCRIPTION_FOR_HUMAN_MAX,
117
+ errors
118
+ );
119
+ if (!plugin.namespace || !/^[A-Za-z0-9-]+$/.test(plugin.namespace)) {
120
+ errors.push('ai-plugin.namespace is required (alphanumeric and hyphen only)');
121
+ }
122
+ validateAiPluginRuntime(plugin, errors);
123
+ }
124
+
125
+ /**
126
+ * @param {Object} plugin
127
+ * @param {string[]} errors
128
+ */
129
+ function validateAiPluginRuntime(plugin, errors) {
130
+ const runtime = Array.isArray(plugin.runtimes) ? plugin.runtimes[0] : null;
131
+ if (!runtime || runtime.type !== 'RemoteMCPServer') {
132
+ errors.push('ai-plugin.runtimes[0].type must be RemoteMCPServer');
133
+ return;
134
+ }
135
+ if (!ALLOWED_AUTH_TYPES.has(runtime.auth?.type)) {
136
+ errors.push(
137
+ `ai-plugin.runtimes[0].auth.type must be one of ${[...ALLOWED_AUTH_TYPES].join(', ')}`
138
+ );
139
+ }
140
+ if (runtime.auth?.type === 'OAuthPluginVault') {
141
+ const ref = runtime.auth.reference_id;
142
+ if (typeof ref !== 'string' || !ref.trim()) {
143
+ errors.push(
144
+ 'OAuthPluginVault.reference_id is required — pass --oauth-plugin-vault-reference-id ' +
145
+ 'with the Teams Developer Portal Entra SSO auth-config id'
146
+ );
147
+ }
148
+ }
149
+ if (runtime.auth?.type === 'None') {
150
+ errors.push(
151
+ 'ai-plugin.runtimes[0].auth.type None is not allowed; use OAuthPluginVault (Microsoft token / Entra)'
152
+ );
153
+ }
154
+ const desc = runtime.spec?.mcp_tool_description;
155
+ if (desc && (desc.mode || desc.layerBToolNames)) {
156
+ errors.push(
157
+ 'ai-plugin mcp_tool_description must not use mode/layerBToolNames; omit for dynamic discovery or use tools/file'
158
+ );
159
+ }
160
+ }
161
+
162
+ /**
163
+ * @param {{ color: Buffer, outline: Buffer }} icons
164
+ * @param {string[]} errors
165
+ */
166
+ function validatePackageIcons(icons, errors) {
167
+ const colorDims = readPngDimensions(icons.color);
168
+ const outlineDims = readPngDimensions(icons.outline);
169
+ if (!colorDims || colorDims.width !== 192 || colorDims.height !== 192) {
170
+ errors.push('color.png must be a 192x192 PNG');
171
+ }
172
+ if (!outlineDims || outlineDims.width !== 32 || outlineDims.height !== 32) {
173
+ errors.push('outline.png must be a 32x32 PNG');
174
+ }
175
+ }
176
+
177
+ /**
178
+ * @param {Record<string, string>} sourceFiles
179
+ * @param {{ color: Buffer, outline: Buffer }} icons
180
+ * @throws {Error}
181
+ */
182
+ function assertValidMicrosoftCopilotPackage(sourceFiles, icons) {
183
+ const parsed = parsePackageJsonFiles(sourceFiles);
184
+ const errors = [...parsed.errors];
185
+ validateTeamsManifest(parsed.manifest, errors);
186
+ validateDeclarativeAgent(parsed.agent, errors);
187
+ validateAiPlugin(parsed.plugin, errors);
188
+ validatePackageIcons(icons, errors);
189
+ if (errors.length) {
190
+ throw new Error(`Microsoft Copilot package validation failed:\n- ${errors.join('\n- ')}`);
191
+ }
192
+ }
193
+
194
+ module.exports = {
195
+ assertValidMicrosoftCopilotPackage,
196
+ ALLOWED_AUTH_TYPES
197
+ };
package/lib/cli/index.js CHANGED
@@ -67,6 +67,8 @@ function setupCoreCliCommands(program) {
67
67
  setupChannelCommands(program);
68
68
  setupDatasourceCommands(program);
69
69
  setupManifestCommands(program);
70
+ const { setupIntegrationDefinitionCommands } = require('../commands/integration-definition');
71
+ setupIntegrationDefinitionCommands(program);
70
72
  }
71
73
 
72
74
  /**
@@ -95,7 +95,7 @@ function logPlatformSetupDurationHint(scope = 'platform') {
95
95
  const line =
96
96
  scope === 'dataplane'
97
97
  ? 'Installation may take a few minutes. Authenticating and starting Dataplane — please wait.'
98
- : 'Installation may take a few minutes. Keycloak, Miso Controller, and Dataplane are starting — please wait.';
98
+ : 'Installation may take a few minutes. Keycloak, Miso Controller, Dataplane, and Builder API are starting — please wait.';
99
99
  logger.log(chalk.yellow(`⚠ ${line}`));
100
100
  }
101
101
 
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @fileoverview Consolidate deferred public-URL health warnings for guided install UX.
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const logger = require('../utils/logger');
8
+ const { formatWarningLine } = require('../utils/cli-test-layout-chalk');
9
+
10
+ /**
11
+ * @param {string} message
12
+ * @returns {{ kind: 'dns'|'localhost', url: string }|null}
13
+ */
14
+ function extractPublicUrlFromWarning(message) {
15
+ const text = String(message || '');
16
+ const dnsMatch = text.match(/Public URL was not verified \(DNS\):\s*(\S+)/);
17
+ if (dnsMatch) {
18
+ return { kind: 'dns', url: dnsMatch[1].replace(/\.$/, '') };
19
+ }
20
+ const match = text.match(/Public URL was not verified:\s*(\S+)/);
21
+ if (match) {
22
+ return { kind: 'localhost', url: match[1].replace(/\.$/, '') };
23
+ }
24
+ return null;
25
+ }
26
+
27
+ /**
28
+ * Print one consolidated public-URL warning (avoid N near-duplicate lines).
29
+ * @param {string[]} messages
30
+ */
31
+ function flushDeferredPublicUrlWarnings(messages) {
32
+ const list = (messages || []).filter(Boolean);
33
+ if (list.length === 0) {
34
+ return;
35
+ }
36
+ const parsed = list.map(extractPublicUrlFromWarning).filter(Boolean);
37
+ if (parsed.length === 0) {
38
+ for (const message of list) {
39
+ logger.log(formatWarningLine(message));
40
+ }
41
+ return;
42
+ }
43
+ const urls = [...new Set(parsed.map((row) => row.url))];
44
+ const hasDns = parsed.some((row) => row.kind === 'dns');
45
+ const urlPart = urls.length === 1 ? urls[0] : urls.join(', ');
46
+ const detail = hasDns
47
+ ? 'Hostname DNS and/or Traefik routing/TLS could not be verified; health succeeded via localhost only.'
48
+ : 'Health checks succeeded via localhost only. Validate Traefik routing, TLS, and DNS.';
49
+ logger.log(formatWarningLine(`Public URL was not verified: ${urlPart}. ${detail}`));
50
+ }
51
+
52
+ module.exports = {
53
+ extractPublicUrlFromWarning,
54
+ flushDeferredPublicUrlWarnings
55
+ };