@aifabrix/builder 2.52.1 → 2.53.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 (225) hide show
  1. package/lib/app/deploy-config.js +3 -2
  2. package/lib/channels/add-channel.js +11 -41
  3. package/lib/channels/approval-guides/index.js +3 -0
  4. package/lib/channels/approval-guides/microsoft-copilot.js +26 -17
  5. package/lib/channels/channel-add-existing.js +72 -0
  6. package/lib/channels/channel-runtime-resolver.js +5 -1
  7. package/lib/channels/channel-status-display.js +30 -3
  8. package/lib/channels/microsoft-copilot-entra-sso-form.js +73 -2
  9. package/lib/channels/microsoft-copilot-entra-sso.js +26 -5
  10. package/lib/channels/microsoft-copilot-limits.js +22 -3
  11. package/lib/channels/microsoft-copilot-package.js +59 -14
  12. package/lib/channels/microsoft-copilot-validate.js +40 -10
  13. package/lib/cli/attach-default-help-examples.js +102 -0
  14. package/lib/cli/index.js +2 -0
  15. package/lib/cli/setup-credential-deployment.js +7 -5
  16. package/lib/cli/setup-utility-validate.js +1 -0
  17. package/lib/commands/credential-push.js +37 -5
  18. package/lib/commands/package-score.js +72 -0
  19. package/lib/commands/role-assistant.js +46 -8
  20. package/lib/deployment/deployer-poll-run.js +2 -2
  21. package/lib/deployment/deployer.js +1 -1
  22. package/lib/deployment/environment.js +2 -2
  23. package/lib/external-system/deploy.js +11 -21
  24. package/lib/integration-definition/apply.js +12 -14
  25. package/lib/integration-definition/enrich-openapi-operations.js +9 -10
  26. package/lib/lab/package-parity-score.js +417 -0
  27. package/lib/programmatic/capability-registry.js +1 -0
  28. package/lib/programmatic/help-content/channel-onboarding.md +35 -0
  29. package/lib/programmatic/help-content/cip-overview.md +27 -0
  30. package/lib/programmatic/help-content/connected-system-ui.md +70 -0
  31. package/lib/programmatic/help-content/dimensions-guide.md +23 -0
  32. package/lib/programmatic/help-content/enterprise-sync-fabrix.md +123 -0
  33. package/lib/programmatic/help-content/overview.md +25 -0
  34. package/lib/programmatic/help-content/subscription-guide.md +27 -0
  35. package/lib/programmatic/help-content/workflow.md +50 -0
  36. package/lib/programmatic/openapi-component-schemas.js +1 -0
  37. package/lib/programmatic/openapi-descriptions.js +6 -0
  38. package/lib/programmatic/openapi-envelope-examples.js +11 -0
  39. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  40. package/lib/programmatic/openapi-request-schemas.js +39 -13
  41. package/lib/programmatic/openapi-result-schemas.js +30 -0
  42. package/lib/programmatic/openapi-schema-descriptions-core.js +18 -0
  43. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  44. package/lib/programmatic/persist-supplied-openapi.js +180 -0
  45. package/lib/programmatic/route-handler-map.js +6 -0
  46. package/lib/programmatic/run-integration-definition.js +13 -5
  47. package/lib/role-assistant/evidence-sync.js +55 -7
  48. package/lib/role-assistant/test-runner-attachments.js +118 -0
  49. package/lib/role-assistant/test-runner-bid-apply.js +100 -21
  50. package/lib/role-assistant/test-runner-bid-document.js +1 -1
  51. package/lib/role-assistant/test-runner-start-payload.js +96 -0
  52. package/lib/role-assistant/test-runner-workhub-answers.js +11 -7
  53. package/lib/role-assistant/test-runner-workhub-helpers.js +100 -0
  54. package/lib/role-assistant/test-runner-workhub-soft.js +86 -0
  55. package/lib/role-assistant/test-runner-workhub.js +68 -105
  56. package/lib/role-assistant/test-runner.js +5 -63
  57. package/lib/role-assistant/test-suite.js +33 -40
  58. package/lib/schema/external-datasource.schema.json +13 -4
  59. package/lib/utils/compose-generator.js +10 -1
  60. package/lib/utils/compose-traefik-extra-routes.js +107 -0
  61. package/lib/utils/docker-build.js +2 -2
  62. package/lib/utils/help-builder.js +2 -1
  63. package/lib/utils/promote-dev-docker-image.js +15 -10
  64. package/package.json +14 -16
  65. package/templates/README.md +12 -1
  66. package/templates/applications/dataplane/application.yaml +8 -1
  67. package/templates/applications/dataplane/env.template +1 -1
  68. package/templates/applications/miso-controller/application.yaml +2 -1
  69. package/templates/applications/miso-controller/rbac.yaml +1 -0
  70. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +6 -2
  71. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +8 -5
  72. package/templates/channels/microsoft-copilot/fi-fi.template.json.hbs +7 -0
  73. package/templates/channels/microsoft-copilot/install.template.md.hbs +74 -22
  74. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +29 -14
  75. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +6 -2
  76. package/templates/python/docker-compose.hbs +36 -0
  77. package/templates/typescript/docker-compose.hbs +36 -0
  78. package/.cursor/plans/Archive/157.1-security-quality-violations-by-type.csv +0 -374
  79. package/.cursor/process/README.md +0 -9
  80. package/.cursor/rules/anchor-docs.mdc +0 -15
  81. package/.cursor/rules/assistant-driven-integration-build.mdc +0 -40
  82. package/.cursor/rules/cli-layout.mdc +0 -81
  83. package/.cursor/rules/cli-product-neutral.mdc +0 -43
  84. package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +0 -49
  85. package/.cursor/rules/docs-rules.mdc +0 -32
  86. package/.cursor/rules/plan-rules/application-lifecycle.mdc +0 -30
  87. package/.cursor/rules/plan-rules/plan-frontmatter.mdc +0 -35
  88. package/.cursor/rules/plan-rules/plan-lifecycle.mdc +0 -31
  89. package/.cursor/rules/plan-rules/plan-validation-gate.mdc +0 -34
  90. package/.cursor/rules/project-rules.mdc +0 -913
  91. package/.cursor/skills/README.md +0 -15
  92. package/.dockerignore +0 -52
  93. package/.eslintignore +0 -22
  94. package/.markdownlint.json +0 -14
  95. package/anchor-docs/README.md +0 -10
  96. package/anchor-docs/_TEMPLATE +0 -24
  97. package/babel.config.js +0 -6
  98. package/bin/builder-api-cleanup.js +0 -26
  99. package/bin/builder-api-nest.js +0 -10
  100. package/bin/builder-api-spike.js +0 -19
  101. package/enterprise-knowledge/README.md +0 -50
  102. package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +0 -22
  103. package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +0 -15
  104. package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +0 -69
  105. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +0 -52
  106. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +0 -29
  107. package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +0 -2
  108. package/integration/README.md +0 -81
  109. package/integration/github/README.md +0 -53
  110. package/integration/github/application.json +0 -21
  111. package/integration/github/deploy.js +0 -72
  112. package/integration/github/env.template +0 -11
  113. package/integration/github/github-datasource-issue-comments.json +0 -239
  114. package/integration/github/github-datasource-issues.json +0 -399
  115. package/integration/github/github-datasource-repository.json +0 -354
  116. package/integration/github/github-deploy.json +0 -1202
  117. package/integration/github/github-system.json +0 -82
  118. package/integration/hubspot-test/README.md +0 -159
  119. package/integration/hubspot-test/application.json +0 -54
  120. package/integration/hubspot-test/companies.json +0 -2048
  121. package/integration/hubspot-test/create-hubspot.js +0 -523
  122. package/integration/hubspot-test/env.template +0 -4
  123. package/integration/hubspot-test/hubspot-test-datasource-company.json +0 -138
  124. package/integration/hubspot-test/hubspot-test-datasource-contact.json +0 -146
  125. package/integration/hubspot-test/hubspot-test-datasource-deal.json +0 -146
  126. package/integration/hubspot-test/hubspot-test-datasource-users.json +0 -76
  127. package/integration/hubspot-test/hubspot-test-deploy.json +0 -2160
  128. package/integration/hubspot-test/hubspot-test-system.json +0 -74
  129. package/integration/hubspot-test/rbac.json +0 -166
  130. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +0 -20
  131. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +0 -9
  132. package/integration/hubspot-test/test-artifacts/wizard-invalid-add-datasource.yaml +0 -5
  133. package/integration/hubspot-test/test-artifacts/wizard-invalid-app-name.yaml +0 -5
  134. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-create.yaml +0 -7
  135. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-select.yaml +0 -7
  136. package/integration/hubspot-test/test-artifacts/wizard-invalid-known-platform.yaml +0 -4
  137. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-app.yaml +0 -4
  138. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  139. package/integration/hubspot-test/test-artifacts/wizard-invalid-mode.yaml +0 -5
  140. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-file.yaml +0 -5
  141. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-url.yaml +0 -4
  142. package/integration/hubspot-test/test-artifacts/wizard-invalid-source.yaml +0 -4
  143. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-array-test.yaml +0 -5
  144. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  145. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  146. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-test.yaml +0 -5
  147. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-test.yaml +0 -5
  148. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +0 -5
  149. package/integration/hubspot-test/test-dataplane-down-helpers.js +0 -249
  150. package/integration/hubspot-test/test-dataplane-down-tests.js +0 -386
  151. package/integration/hubspot-test/test-dataplane-down.js +0 -209
  152. package/integration/hubspot-test/test.js +0 -1588
  153. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +0 -16
  154. package/integration/hubspot-test/wizard-hubspot-platform.yaml +0 -8
  155. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +0 -23
  156. package/integration/roundtrip-test-local/README.md +0 -143
  157. package/integration/roundtrip-test-local/application.yaml +0 -13
  158. package/integration/roundtrip-test-local/env.template +0 -15
  159. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +0 -14
  160. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +0 -61
  161. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +0 -25
  162. package/integration/roundtrip-test-local2/README.md +0 -143
  163. package/integration/roundtrip-test-local2/application.yaml +0 -13
  164. package/integration/roundtrip-test-local2/env.template +0 -15
  165. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +0 -31
  166. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +0 -86
  167. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +0 -25
  168. package/integration/test/wizard.yaml +0 -8
  169. package/jest.config.coverage.js +0 -37
  170. package/jest.config.default.js +0 -11
  171. package/jest.config.integration.fixtures.js +0 -22
  172. package/jest.config.integration.js +0 -33
  173. package/jest.config.isolated.js +0 -11
  174. package/jest.config.manual.js +0 -30
  175. package/jest.global-live-fabrix-hooks.js +0 -36
  176. package/jest.isolated-projects.js +0 -283
  177. package/jest.projects.js +0 -79
  178. package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +0 -205
  179. package/packages/builder-api/README.md +0 -120
  180. package/packages/builder-api/package.json +0 -30
  181. package/packages/builder-api/src/app.module.ts +0 -7
  182. package/packages/builder-api/src/auth/auth.module.ts +0 -5
  183. package/packages/builder-api/src/external-system/external-system.module.ts +0 -5
  184. package/packages/builder-api/src/main.ts +0 -16
  185. package/packages/builder-api/tsconfig.json +0 -15
  186. package/scripts/check-builder-api-architecture-grep.js +0 -79
  187. package/scripts/check-datasource-test-run-schema-sync.js +0 -34
  188. package/scripts/ci-fix.sh +0 -19
  189. package/scripts/ci-simulate.sh +0 -19
  190. package/scripts/diagnose-cli.js +0 -150
  191. package/scripts/docker_image_content_gate.js +0 -47
  192. package/scripts/docker_image_content_gate.sh +0 -5
  193. package/scripts/export-builder-api-evidence.js +0 -262
  194. package/scripts/install-local.js +0 -490
  195. package/scripts/lib/builder-api-docker-staging-copy.js +0 -60
  196. package/scripts/lib/builder-api-docker-staging-core.js +0 -183
  197. package/scripts/lib/docker-image-content-gate-core.js +0 -166
  198. package/scripts/lib/plan-actor.js +0 -109
  199. package/scripts/lib/plan-config.js +0 -20
  200. package/scripts/lib/plan-gates.js +0 -154
  201. package/scripts/lib/plan-meta.js +0 -104
  202. package/scripts/lib/plan-parse.js +0 -162
  203. package/scripts/lib/plan-prevalidate-gates.js +0 -337
  204. package/scripts/lib/plan-run-core.js +0 -125
  205. package/scripts/lib/plan-run-report.js +0 -119
  206. package/scripts/plan-approve.js +0 -24
  207. package/scripts/plan-archive.js +0 -49
  208. package/scripts/plan-run.js +0 -13
  209. package/scripts/plan-set-actor.js +0 -21
  210. package/scripts/plan-validate.js +0 -80
  211. package/scripts/pnpm/af.mjs +0 -125
  212. package/scripts/pnpm/check-quiet.mjs +0 -32
  213. package/scripts/pnpm/dev-install.mjs +0 -29
  214. package/scripts/pnpm/dev-reload.mjs +0 -18
  215. package/scripts/pnpm/help.mjs +0 -55
  216. package/scripts/pnpm/lib/dev-reload-core.mjs +0 -189
  217. package/scripts/pnpm/lib/dev-reload-log-capture.mjs +0 -67
  218. package/scripts/pnpm/lib/dev-reload-log-utils.mjs +0 -32
  219. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +0 -124
  220. package/scripts/pnpm/lib/resolve-app-url.mjs +0 -183
  221. package/scripts/pnpm/lib/spinner.mjs +0 -42
  222. package/scripts/pnpm-global-remove.js +0 -48
  223. package/scripts/stage_builder_api_for_docker.js +0 -65
  224. package/scripts/sync-builder-api-template.js +0 -44
  225. package/scripts/test-dataplane-bootstrap.js +0 -331
@@ -18,7 +18,10 @@ const { applyMicrosoftCopilotDisplayLimits } = require('./microsoft-copilot-limi
18
18
  const {
19
19
  assertCopilotPackageOauthPluginVaultReference
20
20
  } = require('./microsoft-copilot-entra-sso');
21
- const { renderCopilotChannelTemplate } = require('./microsoft-copilot-entra-sso-form');
21
+ const {
22
+ renderCopilotChannelTemplate,
23
+ applyWebApplicationInfoFields
24
+ } = require('./microsoft-copilot-entra-sso-form');
22
25
 
23
26
  /**
24
27
  * @param {string} outputPath
@@ -73,7 +76,8 @@ function renderCopilotTemplates(runtimeValues, renderTemplateFile, templatesDir)
73
76
  'ai-plugin.json': renderTemplateFile(
74
77
  path.join(templatesDir, 'ai-plugin.template.json.hbs'),
75
78
  runtimeValues
76
- )
79
+ ),
80
+ 'fi-fi.json': renderTemplateFile(path.join(templatesDir, 'fi-fi.template.json.hbs'), runtimeValues)
77
81
  };
78
82
  // INSTALL prose + Entra SSO form table live in templates (partial entraSsoRegistration).
79
83
  const installText = renderCopilotChannelTemplate('install.template.md.hbs', runtimeValues);
@@ -100,6 +104,7 @@ function buildCopilotZipBuffer(sourceFiles, icons) {
100
104
  zip.addFile('manifest.json', Buffer.from(sourceFiles['manifest.json'], 'utf8'));
101
105
  zip.addFile('declarativeAgent.json', Buffer.from(sourceFiles['declarativeAgent.json'], 'utf8'));
102
106
  zip.addFile('ai-plugin.json', Buffer.from(sourceFiles['ai-plugin.json'], 'utf8'));
107
+ zip.addFile('fi-fi.json', Buffer.from(sourceFiles['fi-fi.json'], 'utf8'));
103
108
  zip.addFile('color.png', icons.color);
104
109
  zip.addFile('outline.png', icons.outline);
105
110
  return zip.toBuffer();
@@ -130,8 +135,9 @@ function buildCopilotValidation(runtimeValues, paths, sourceFileNames) {
130
135
  roleAssistantResolution: 'dynamic-runtime'
131
136
  },
132
137
  requiresMicrosoftAdmin: [
133
- 'upload-package',
134
- 'tenant-approval',
138
+ 'developer-portal-import',
139
+ 'publish-to-org',
140
+ 'teams-admin-approval',
135
141
  'assignment-and-consent',
136
142
  'first-copilot-invoke'
137
143
  ]
@@ -206,7 +212,14 @@ function buildCopilotSummaryPackage(runtimeValues, paths) {
206
212
  return {
207
213
  appId: runtimeValues.packageAppId,
208
214
  version: runtimeValues.packageVersion,
209
- sourceFiles: ['manifest.json', 'declarativeAgent.json', 'ai-plugin.json', 'color.png', 'outline.png'],
215
+ sourceFiles: [
216
+ 'manifest.json',
217
+ 'declarativeAgent.json',
218
+ 'ai-plugin.json',
219
+ 'fi-fi.json',
220
+ 'color.png',
221
+ 'outline.png'
222
+ ],
210
223
  zipFileName: paths.zipFileName,
211
224
  installGuide: 'INSTALL.md',
212
225
  validationFile: path.relative(paths.rootDir, paths.validationPath)
@@ -232,6 +245,9 @@ function buildCopilotSummary(runtimeValues, paths, validation, artifactHash) {
232
245
  package: buildCopilotSummaryPackage(runtimeValues, paths),
233
246
  installReady: runtimeValues.installReady === true,
234
247
  blocker: runtimeValues.discoveryBlocker || null,
248
+ packageReady: runtimeValues.packageReady !== false && runtimeValues.installReady === true,
249
+ packageBlocker: runtimeValues.packageBlocker || runtimeValues.discoveryBlocker || null,
250
+ microsoftPublicationPending: runtimeValues.microsoftPublicationPending !== false,
235
251
  links: {
236
252
  readme: paths.installPath,
237
253
  zip: paths.zipPath,
@@ -267,6 +283,7 @@ function writeCopilotArtifacts(params) {
267
283
  'utf8'
268
284
  );
269
285
  fs.writeFileSync(path.join(paths.sourceDir, 'ai-plugin.json'), sourceFiles['ai-plugin.json'], 'utf8');
286
+ fs.writeFileSync(path.join(paths.sourceDir, 'fi-fi.json'), sourceFiles['fi-fi.json'], 'utf8');
270
287
  fs.writeFileSync(path.join(paths.sourceDir, 'color.png'), icons.color);
271
288
  fs.writeFileSync(path.join(paths.sourceDir, 'outline.png'), icons.outline);
272
289
  fs.writeFileSync(paths.zipPath, zipBuffer);
@@ -301,6 +318,7 @@ function buildCopilotPackagePayload(params, runtimeValues, paths, icons) {
301
318
  sourceFiles['manifest.json'],
302
319
  sourceFiles['declarativeAgent.json'],
303
320
  sourceFiles['ai-plugin.json'],
321
+ sourceFiles['fi-fi.json'],
304
322
  installText,
305
323
  validationText,
306
324
  zipBuffer
@@ -312,19 +330,43 @@ function buildCopilotPackagePayload(params, runtimeValues, paths, icons) {
312
330
  }
313
331
 
314
332
  /**
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 }}
333
+ * Read prior webApplicationInfo from an existing source manifest (preserve on regenerate).
334
+ * @param {string} sourceDir
335
+ * @returns {{ id: string, resource: string }|null}
323
336
  */
337
+ function readPriorWebApplicationInfo(sourceDir) {
338
+ const manifestPath = path.join(sourceDir, 'manifest.json');
339
+ if (!fs.existsSync(manifestPath)) {
340
+ return null;
341
+ }
342
+ try {
343
+ const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
344
+ const info = parsed && parsed.webApplicationInfo;
345
+ if (
346
+ info &&
347
+ typeof info.id === 'string' &&
348
+ info.id.trim() &&
349
+ typeof info.resource === 'string' &&
350
+ info.resource.trim()
351
+ ) {
352
+ return { id: info.id.trim(), resource: info.resource.trim() };
353
+ }
354
+ } catch {
355
+ return null;
356
+ }
357
+ return null;
358
+ }
359
+
324
360
  function generateMicrosoftCopilotPackage(params) {
325
361
  const runtimeValues = applyMicrosoftCopilotDisplayLimits({ ...(params.runtimeValues || {}) });
326
362
  assertCopilotPackageOauthPluginVaultReference(runtimeValues);
327
363
  const paths = buildCopilotOutputPaths(params.outputPath, runtimeValues);
364
+ const prior = readPriorWebApplicationInfo(paths.sourceDir);
365
+ applyWebApplicationInfoFields(runtimeValues, {
366
+ webApplicationInfoId: runtimeValues.webApplicationInfoId,
367
+ webApplicationInfoResource: runtimeValues.webApplicationInfoResource,
368
+ priorWebApplicationInfo: prior
369
+ });
328
370
  const icons = resolvePackageIcons(runtimeValues);
329
371
  const payload = buildCopilotPackagePayload(params, runtimeValues, paths, icons);
330
372
  if (!params.dryRun) {
@@ -346,7 +388,10 @@ function generateMicrosoftCopilotPackage(params) {
346
388
  zipPath: paths.zipPath,
347
389
  links: payload.summary.links,
348
390
  installReady: payload.summary.installReady === true,
349
- blocker: payload.summary.blocker || null
391
+ blocker: payload.summary.blocker || null,
392
+ packageReady: payload.summary.packageReady === true,
393
+ packageBlocker: payload.summary.packageBlocker || null,
394
+ microsoftPublicationPending: payload.summary.microsoftPublicationPending !== false
350
395
  };
351
396
  }
352
397
 
@@ -123,21 +123,17 @@ function validateAiPlugin(plugin, errors) {
123
123
  }
124
124
 
125
125
  /**
126
- * @param {Object} plugin
126
+ * @param {Object} runtime
127
127
  * @param {string[]} errors
128
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)) {
129
+ function validateAiPluginAuth(runtime, errors) {
130
+ const authType = runtime.auth?.type;
131
+ if (!ALLOWED_AUTH_TYPES.has(authType)) {
136
132
  errors.push(
137
133
  `ai-plugin.runtimes[0].auth.type must be one of ${[...ALLOWED_AUTH_TYPES].join(', ')}`
138
134
  );
139
135
  }
140
- if (runtime.auth?.type === 'OAuthPluginVault') {
136
+ if (authType === 'OAuthPluginVault') {
141
137
  const ref = runtime.auth.reference_id;
142
138
  if (typeof ref !== 'string' || !ref.trim()) {
143
139
  errors.push(
@@ -146,17 +142,51 @@ function validateAiPluginRuntime(plugin, errors) {
146
142
  );
147
143
  }
148
144
  }
149
- if (runtime.auth?.type === 'None') {
145
+ if (authType === 'None') {
150
146
  errors.push(
151
147
  'ai-plugin.runtimes[0].auth.type None is not allowed; use OAuthPluginVault (Microsoft token / Entra)'
152
148
  );
153
149
  }
150
+ }
151
+
152
+ /**
153
+ * @param {Object} plugin
154
+ * @param {Object} runtime
155
+ * @param {string[]} errors
156
+ */
157
+ function validateAiPluginDiscovery(plugin, runtime, errors) {
154
158
  const desc = runtime.spec?.mcp_tool_description;
155
159
  if (desc && (desc.mode || desc.layerBToolNames)) {
156
160
  errors.push(
157
161
  'ai-plugin mcp_tool_description must not use mode/layerBToolNames; omit for dynamic discovery or use tools/file'
158
162
  );
159
163
  }
164
+ if (!Array.isArray(plugin.functions) || plugin.functions.length !== 0) {
165
+ errors.push(
166
+ 'ai-plugin.functions must be [] for Microsoft dynamic MCP tool discovery ' +
167
+ '(see run_for_functions ["*"])'
168
+ );
169
+ }
170
+ const runFor = runtime.run_for_functions;
171
+ if (!Array.isArray(runFor) || runFor.length !== 1 || runFor[0] !== '*') {
172
+ errors.push(
173
+ 'ai-plugin.runtimes[0].run_for_functions must be ["*"] for dynamic MCP tool discovery'
174
+ );
175
+ }
176
+ }
177
+
178
+ /**
179
+ * @param {Object} plugin
180
+ * @param {string[]} errors
181
+ */
182
+ function validateAiPluginRuntime(plugin, errors) {
183
+ const runtime = Array.isArray(plugin.runtimes) ? plugin.runtimes[0] : null;
184
+ if (!runtime || runtime.type !== 'RemoteMCPServer') {
185
+ errors.push('ai-plugin.runtimes[0].type must be RemoteMCPServer');
186
+ return;
187
+ }
188
+ validateAiPluginAuth(runtime, errors);
189
+ validateAiPluginDiscovery(plugin, runtime, errors);
160
190
  }
161
191
 
162
192
  /**
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Attach minimal product-neutral Examples: blocks to commands that lack them.
3
+ * Runs after setupCommands so intentional addHelpText Examples are preserved.
4
+ *
5
+ * @fileoverview Default CLI help Examples for check:cli-help gate
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ const EXAMPLE_HEADER_RE = /Examples:/m;
11
+ const EXAMPLE_LINE_RE = /\$ aifabrix |\$ af /;
12
+
13
+ /**
14
+ * @param {import('commander').Command} cmd
15
+ * @returns {string}
16
+ */
17
+ function captureHelpOutput(cmd) {
18
+ if (!cmd || typeof cmd.outputHelp !== 'function') {
19
+ return '';
20
+ }
21
+ let buf = '';
22
+ cmd.outputHelp({
23
+ write: (chunk) => {
24
+ buf += String(chunk);
25
+ }
26
+ });
27
+ return buf;
28
+ }
29
+
30
+ /**
31
+ * @param {string} text
32
+ * @returns {boolean}
33
+ */
34
+ function hasValidExamples(text) {
35
+ return EXAMPLE_HEADER_RE.test(text) && EXAMPLE_LINE_RE.test(text);
36
+ }
37
+
38
+ /**
39
+ * @param {string} pathStr
40
+ * @returns {string}
41
+ */
42
+ function defaultExamplesBlock(pathStr) {
43
+ return `
44
+ Examples:
45
+ $ aifabrix ${pathStr}
46
+ $ aifabrix ${pathStr} -h
47
+ `;
48
+ }
49
+
50
+ /**
51
+ * @param {import('commander').Command} cmd
52
+ * @param {string[]} parts
53
+ * @param {Array<{ cmd: import('commander').Command, path: string }>} out
54
+ */
55
+ function collectCommands(cmd, parts, out) {
56
+ // Tests sometimes pass mock nodes without Commander methods — skip those.
57
+ if (!cmd || typeof cmd.name !== 'function') {
58
+ return;
59
+ }
60
+ const name = cmd.name();
61
+ if (name === 'help') {
62
+ return;
63
+ }
64
+ const next = name && name !== 'aifabrix' ? parts.concat(name) : parts;
65
+ const subs = Array.isArray(cmd.commands) ? cmd.commands : [];
66
+ if (next.length > 0) {
67
+ out.push({ cmd, path: next.join(' ') });
68
+ }
69
+ for (const child of subs) {
70
+ collectCommands(child, next, out);
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Ensure every leaf and parent has Examples with $ aifabrix / $ af.
76
+ * @param {import('commander').Command} program
77
+ * @returns {number} count of commands that received a default Examples block
78
+ */
79
+ function attachDefaultHelpExamples(program) {
80
+ const entries = [];
81
+ collectCommands(program, [], entries);
82
+ let attached = 0;
83
+ for (const { cmd, path: pathStr } of entries) {
84
+ if (typeof cmd.addHelpText !== 'function') {
85
+ continue;
86
+ }
87
+ const text = captureHelpOutput(cmd);
88
+ if (hasValidExamples(text)) {
89
+ continue;
90
+ }
91
+ cmd.addHelpText('after', defaultExamplesBlock(pathStr));
92
+ attached += 1;
93
+ }
94
+ return attached;
95
+ }
96
+
97
+ module.exports = {
98
+ attachDefaultHelpExamples,
99
+ hasValidExamples,
100
+ captureHelpOutput,
101
+ defaultExamplesBlock
102
+ };
package/lib/cli/index.js CHANGED
@@ -100,6 +100,8 @@ function setupCommands(program) {
100
100
  setupCoreCliCommands(program);
101
101
  setupExtendedCliCommands(program);
102
102
  setupDevAndPlatformCommands(program);
103
+ const { attachDefaultHelpExamples } = require('./attach-default-help-examples');
104
+ attachDefaultHelpExamples(program);
103
105
  }
104
106
 
105
107
  module.exports = {
@@ -18,8 +18,10 @@ const { runDeploymentList } = require('../commands/deployment-list');
18
18
  const CREDENTIAL_HELP_AFTER = `
19
19
  Subcommands:
20
20
  env <systemKey> Prompt for KV_* values → integration/<key>/.env
21
- push <systemKey> Upload secrets from .env to Dataplane
21
+ push <systemKey> Upload secrets from an env file to Dataplane (--file recommended)
22
22
  list List credentials (pagination: --page-size)
23
+
24
+ Secrets: pass paths only (e.g. --file ./path/to/.env). Never put secret values on the command line.
23
25
  `;
24
26
 
25
27
  const DEPLOYMENT_HELP_AFTER = `
@@ -42,10 +44,11 @@ function setupCredentialEnvAndPush(credential) {
42
44
  });
43
45
  credential
44
46
  .command('push <systemKey>')
45
- .description('Push KV_* secrets from .env to Dataplane')
46
- .action(async(systemKey, _options) => {
47
+ .description('Push KV_* secrets from an env file to Dataplane (file path only — no secret values on argv)')
48
+ .option('--file <path>', 'Env file with KV_* values (must exist; never put secret values on argv)')
49
+ .action(async(systemKey, options) => {
47
50
  try {
48
- await runCredentialPush(systemKey);
51
+ await runCredentialPush(systemKey, { file: options.file });
49
52
  } catch (error) {
50
53
  logger.error(chalk.red(`Error: ${error.message}`));
51
54
  handleCommandError(error, 'credential push');
@@ -53,7 +56,6 @@ function setupCredentialEnvAndPush(credential) {
53
56
  }
54
57
  });
55
58
  }
56
-
57
59
  /**
58
60
  * Sets up credential and deployment list commands
59
61
  * @param {Command} program - Commander program instance
@@ -228,6 +228,7 @@ function setupValidateDiffCommands(program) {
228
228
  setupValidateCommand(program);
229
229
  setupValidateJsonCommand(program);
230
230
  setupDiffCommand(program);
231
+ require('../commands/package-score').setupPackageScoreCommand(program);
231
232
  }
232
233
 
233
234
  module.exports = {
@@ -51,8 +51,10 @@ async function buildPayload(systemKey) {
51
51
  * Runs credential push: push KV_* from .env to dataplane.
52
52
  * @async
53
53
  * @param {string} systemKey - External system key
54
+ * @param {Object} [options]
55
+ * @param {string} [options.file] - Explicit env file path (required to exist when set)
54
56
  * @returns {Promise<{ pushed: number }>} Count of secrets pushed
55
- * @throws {Error} If auth or push fails
57
+ * @throws {Error} If auth or push fails, or --file path is missing
56
58
  */
57
59
  function logPushResult(pushResult) {
58
60
  if (pushResult.pushed > 0) {
@@ -66,10 +68,35 @@ function logPushResult(pushResult) {
66
68
  if (pushResult.warning) logger.log(chalk.yellow(`Warning: ${pushResult.warning}`));
67
69
  }
68
70
 
69
- async function runCredentialPush(systemKey) {
71
+ /**
72
+ * Resolve env file path for credential push.
73
+ * @param {string} systemKey
74
+ * @param {Object} [options]
75
+ * @returns {string}
76
+ */
77
+ function resolveCredentialEnvFilePath(systemKey, options = {}) {
78
+ const fileOpt =
79
+ options.file !== undefined && options.file !== null ? String(options.file).trim() : '';
80
+ if (fileOpt) {
81
+ const resolved = path.resolve(fileOpt);
82
+ const fsReal = require('../internal/fs-real-sync');
83
+ let isFile = false;
84
+ try {
85
+ isFile = fsReal.existsSync(resolved) && fsReal.statSync(resolved).isFile();
86
+ } catch (_err) {
87
+ isFile = false;
88
+ }
89
+ if (!isFile) {
90
+ throw new Error(`Credential env file not found: ${resolved}`);
91
+ }
92
+ return resolved;
93
+ }
94
+ return path.join(getIntegrationPath(systemKey), '.env');
95
+ }
96
+
97
+ async function runCredentialPush(systemKey, options = {}) {
70
98
  validateSystemKeyFormat(systemKey);
71
- const appPath = getIntegrationPath(systemKey);
72
- const envFilePath = path.join(appPath, '.env');
99
+ const envFilePath = resolveCredentialEnvFilePath(systemKey, options);
73
100
 
74
101
  const { resolveEnvironment } = require('../core/config');
75
102
  const environment = await resolveEnvironment();
@@ -99,4 +126,9 @@ async function runCredentialPush(systemKey) {
99
126
  return { pushed: pushResult.pushed || 0 };
100
127
  }
101
128
 
102
- module.exports = { runCredentialPush, validateSystemKeyFormat, buildPayload };
129
+ module.exports = {
130
+ runCredentialPush,
131
+ validateSystemKeyFormat,
132
+ buildPayload,
133
+ resolveCredentialEnvFilePath
134
+ };
@@ -0,0 +1,72 @@
1
+ /**
2
+ * CLI: score candidate integration package vs a reference package directory.
3
+ *
4
+ * @fileoverview package-score command — score-only parity; reference is never generation input
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const path = require('path');
10
+ const chalk = require('chalk');
11
+ const logger = require('../utils/logger');
12
+ const { scorePackageParity } = require('../lab/package-parity-score');
13
+
14
+ /**
15
+ * @param {Object} options
16
+ * @param {string} options.reference
17
+ * @param {string} options.candidate
18
+ * @param {boolean} [options.json]
19
+ * @returns {Promise<number>} process exit code
20
+ */
21
+ async function runPackageScoreCommand(options = {}) {
22
+ const reference = String(options.reference || '').trim();
23
+ const candidate = String(options.candidate || '').trim();
24
+ if (!reference || !candidate) {
25
+ throw new Error('Both --reference <dir> and --candidate <dir> are required.');
26
+ }
27
+ const report = scorePackageParity(path.resolve(reference), path.resolve(candidate));
28
+ if (options.json === true) {
29
+ logger.log(JSON.stringify(report, null, 2));
30
+ } else {
31
+ logger.log(chalk.bold(`Package parity score: ${report.score}/${report.maxScore}`));
32
+ logger.log(chalk.gray(report.summary));
33
+ logger.log(chalk.gray(`Reference (score-only): ${report.referenceDir}`));
34
+ logger.log(chalk.gray(`Candidate: ${report.candidateDir}`));
35
+ for (const row of report.facets) {
36
+ const mark = row.ok ? chalk.green('✔') : chalk.red('✖');
37
+ logger.log(` ${mark} ${row.id}: ${row.detail}`);
38
+ }
39
+ if (report.goldIsScoreOnly) {
40
+ logger.log(chalk.yellow('Note: reference package is for scoring only — never use it as generation input.'));
41
+ }
42
+ }
43
+ return report.identical ? 0 : 1;
44
+ }
45
+
46
+ /**
47
+ * @param {import('commander').Command} program
48
+ */
49
+ function setupPackageScoreCommand(program) {
50
+ program
51
+ .command('package-score')
52
+ .description(
53
+ 'Score a candidate integration package directory against a reference package (score-only; not generation)'
54
+ )
55
+ .option('--reference <dir>', 'Reference integration package directory (score baseline only)')
56
+ .option('--candidate <dir>', 'Candidate integration package directory')
57
+ .option('--json', 'Emit structured JSON report', false)
58
+ .action(async(options) => {
59
+ try {
60
+ const code = await runPackageScoreCommand(options);
61
+ process.exitCode = code;
62
+ } catch (error) {
63
+ logger.error(chalk.red(`Error: ${error.message}`));
64
+ process.exitCode = 1;
65
+ }
66
+ });
67
+ }
68
+
69
+ module.exports = {
70
+ setupPackageScoreCommand,
71
+ runPackageScoreCommand
72
+ };
@@ -50,6 +50,29 @@ const {
50
50
  roleAssistantSystemKey
51
51
  } = require('../utils/role-assistant-paths');
52
52
 
53
+ function roleAssistantGroupHelpAfter() {
54
+ return `
55
+ Subcommands:
56
+ test <roleKey> Run lifecycle checks, optional YAML cases/suites, and print a portal deep link
57
+
58
+ Package workspace (top-level commands — same digital-worker package):
59
+ download / validate / upload / deploy <roleKey> Manage role-assistant/ra-<roleKey>/
60
+
61
+ Examples:
62
+ $ aifabrix role-assistant test customer-support-bot
63
+ $ aifabrix role-assistant test customer-support-bot --suite 001
64
+ $ aifabrix role-assistant test customer-support-bot --list-cases
65
+ $ aifabrix role-assistant test customer-support-bot --env tst --json
66
+ $ aifabrix upload customer-support-bot --env dev
67
+ $ aifabrix test ra-customer-support-bot
68
+
69
+ Notes:
70
+ - Local packages live under role-assistant/ra-<roleKey>/ (bare <roleKey> in CLI args).
71
+ - test uses the public/running worker by default; pass --publish (or upload) to refresh from local files.
72
+ - See: aifabrix role-assistant test -h
73
+ `;
74
+ }
75
+
53
76
  function roleAssistantHelpAfter() {
54
77
  return `
55
78
  Examples:
@@ -60,14 +83,16 @@ Examples:
60
83
  $ aifabrix role-assistant test customer-support-bot --suite 001
61
84
  $ aifabrix role-assistant test customer-support-bot --list-cases
62
85
  $ aifabrix role-assistant test customer-support-bot --suite 001 --fail-fast --json
86
+ $ aifabrix test ra-customer-support-bot
63
87
 
64
88
  Notes:
89
+ - <roleKey> is bare (e.g. customer-support-bot); package folder is role-assistant/ra-<roleKey>/.
65
90
  - Uses the public/running Role Assistant on the dataplane by default (does not publish local datasources).
66
91
  - Use \`aifabrix upload <roleKey> --env …\` (or \`--publish\`) when you intentionally refresh the package.
67
92
  - Runs lifecycle checks, then prints a portal deep link for full step-by-step status.
68
93
  - Case files with input.message/input.content execute Work Hub plan→accept (or work.start when executionPath: intent).
69
94
  - answers[] and approvals[] are matched to runtime wait payloads (questionKey/requestKey/waitReason).
70
- - --suite runs all YAML under tests/role-assistant/ra-<roleKey>/<suite>/ (or --cases-root).
95
+ - --suite runs YAML under role-assistant/ra-<roleKey>/tests/<suite>/ (preferred), or tests/ra-<roleKey>/<roleKey>/<suite>/, or legacy tests/role-assistant/ra-<roleKey>/<suite>/ (override with --cases-root).
71
96
  - Use the portal URL for detailed human review while automation reads CLI/JSON output.
72
97
  `;
73
98
  }
@@ -163,13 +188,19 @@ function listRoleAssistantCases(roleKey, opts) {
163
188
  }
164
189
 
165
190
  async function resolveSuiteCaseResults(systemKey, opts, result, verdict, portalUrl) {
166
- const root = resolveCasesRoot(systemKey, { casesRoot: opts.casesRoot || undefined });
191
+ const cwd = process.cwd();
192
+ const root = resolveCasesRoot(systemKey, { casesRoot: opts.casesRoot || undefined, cwd });
167
193
  const discovered = discoverSuiteCaseFiles(root, { suite: opts.suite || undefined });
168
194
  const selected = filterDiscoveredCases(discovered, opts.case);
169
195
  if (selected.length === 0) {
196
+ const { listCasesRootCandidates } = require('../role-assistant/test-suite');
197
+ const tried = opts.casesRoot
198
+ ? [root]
199
+ : listCasesRootCandidates(systemKey, cwd);
170
200
  throw new Error(
171
201
  `No role-assistant cases found under ${root}` +
172
- (opts.suite ? ` suite=${opts.suite}` : '')
202
+ (opts.suite ? ` suite=${opts.suite}` : '') +
203
+ `. Looked in: ${tried.join(', ')}`
173
204
  );
174
205
  }
175
206
  const outcomes = [];
@@ -269,21 +300,26 @@ async function roleAssistantTestHandler(roleKey, options, cmd) {
269
300
  function setupRoleAssistantCommands(program) {
270
301
  const roleAssistant = program
271
302
  .command('role-assistant')
272
- .description('Role Assistant lifecycle and portal workflows');
303
+ .description(
304
+ 'Role Assistant packages: lifecycle checks, YAML cases/suites, and portal deep links'
305
+ )
306
+ .addHelpText('after', roleAssistantGroupHelpAfter());
273
307
 
274
308
  roleAssistant
275
309
  .command('test <roleKey>')
276
- .description('Run role-assistant lifecycle checks and print portal deep link')
310
+ .description(
311
+ 'Lifecycle checks + optional cases/suites; prints portal deep link (bare <roleKey>)'
312
+ )
277
313
  .option('-e, --env <env>', 'Environment: dev, tst, or pro', 'dev')
278
314
  .option('--case <caseIdOrPath>', 'Run and evaluate one role-assistant test case')
279
315
  .option('--tests-dir <dir>', 'Override role-assistant test case directory')
280
316
  .option(
281
317
  '--suite <suiteId>',
282
- 'Run all YAML cases under tests/role-assistant/ra-<roleKey>/<suiteId>/'
318
+ 'Run all YAML under role-assistant/ra-<roleKey>/tests/<suiteId>/ (or tests/ra-<roleKey>/<roleKey>/<suiteId>/)'
283
319
  )
284
320
  .option(
285
321
  '--cases-root <dir>',
286
- 'Override cases root (default: tests/role-assistant/ra-<roleKey>/)'
322
+ 'Override cases root (default: package tests/, then tests/ra-<roleKey>/<roleKey>/)'
287
323
  )
288
324
  .option('--list-cases', 'List discoverable case files and exit')
289
325
  .option('--fail-fast', 'Stop suite run after the first failing case')
@@ -326,5 +362,7 @@ module.exports = {
326
362
  resolveRoleAssistantCaseResult,
327
363
  resolveSuiteCaseResults,
328
364
  listRoleAssistantCases,
329
- formatStepsRunSummary
365
+ formatStepsRunSummary,
366
+ roleAssistantGroupHelpAfter,
367
+ roleAssistantHelpAfter
330
368
  };
@@ -31,7 +31,7 @@ const { createDeployPollHandlers } = require('./deploy-poll-ui');
31
31
  */
32
32
  async function pollDeploymentStatus(deploymentId, controllerUrl, envKey, authConfig, options = {}) {
33
33
  const interval = options.interval || 5000;
34
- const maxAttempts = options.maxAttempts || 60;
34
+ const maxAttempts = options.maxAttempts || 300;
35
35
 
36
36
  const validatedEnvKey = validateEnvironmentKey(envKey);
37
37
  const pipelineAuthConfig = convertToPipelineAuthConfig(authConfig);
@@ -74,7 +74,7 @@ async function pollDeployment(result, url, validatedEnvKey, authConfig, options)
74
74
  }
75
75
 
76
76
  const resolvedInterval = await resolvePollIntervalFromController(url, options.pollInterval);
77
- const maxAttempts = options.pollMaxAttempts || 60;
77
+ const maxAttempts = options.pollMaxAttempts || 300;
78
78
  const ownsPollUi = !options.pollUi;
79
79
  const pollUi =
80
80
  options.pollUi ||
@@ -387,7 +387,7 @@ async function deployToController(manifest, controllerUrl, envKey, authConfig, o
387
387
 
388
388
  const usePollUi = options.poll !== false && options.silentPoll !== true;
389
389
  const pollUi = usePollUi
390
- ? createDeployPollHandlers(options.pollMaxAttempts || 60, { silent: false })
390
+ ? createDeployPollHandlers(options.pollMaxAttempts || 300, { silent: false })
391
391
  : null;
392
392
  const deployOpts = pollUi ? { ...options, pollUi } : options;
393
393
 
@@ -253,7 +253,7 @@ async function pollEnvironmentStatus(deploymentId, controllerUrl, envKey, authCo
253
253
  const validatedUrl = validateControllerUrl(controllerUrl);
254
254
  const validatedEnvKey = validateEnvironmentKey(envKey);
255
255
  const pollInterval = options.pollInterval || 5000;
256
- const maxAttempts = options.maxAttempts || 60;
256
+ const maxAttempts = options.maxAttempts || 300;
257
257
  const apiAuthConfig = { type: 'bearer', token: authConfig.token };
258
258
 
259
259
  logger.log(chalk.blue(`⏳ Polling environment status (${pollInterval}ms intervals)...`));
@@ -398,7 +398,7 @@ async function pollDeploymentStatusIfEnabled(result, validatedControllerUrl, env
398
398
  authConfig,
399
399
  {
400
400
  pollInterval,
401
- maxAttempts: options.pollMaxAttempts || 60
401
+ maxAttempts: options.pollMaxAttempts || 300
402
402
  }
403
403
  );
404
404
  result.status = pollResult.status;