@aifabrix/builder 2.53.1 → 2.54.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 (321) hide show
  1. package/bin/aifabrix.js +2 -2
  2. package/lib/api/assistants.api.js +8 -5
  3. package/lib/api/channel-identity.api.js +41 -0
  4. package/lib/api/evidence-authoring.api.js +5 -1
  5. package/lib/api/evidence.api.js +2 -0
  6. package/lib/api/types/evidence.types.js +2 -0
  7. package/lib/api/work-search.api.js +136 -0
  8. package/lib/channels/add-channel.js +163 -126
  9. package/lib/channels/approval-guides/chatgpt.js +14 -18
  10. package/lib/channels/approval-guides/claude.js +29 -0
  11. package/lib/channels/approval-guides/index.js +88 -22
  12. package/lib/channels/approval-guides/mcp-client.js +27 -0
  13. package/lib/channels/approval-guides/microsoft-copilot.js +52 -3
  14. package/lib/channels/approval-guides/slack.js +10 -10
  15. package/lib/channels/approval-guides/teams-bot.js +8 -12
  16. package/lib/channels/channel-add-continuations.js +91 -0
  17. package/lib/channels/channel-add-ensure.js +124 -27
  18. package/lib/channels/channel-add-existing.js +160 -28
  19. package/lib/channels/channel-add-failure.js +60 -0
  20. package/lib/channels/channel-add-remaining.js +486 -0
  21. package/lib/channels/channel-add-tty.js +190 -0
  22. package/lib/channels/channel-add-wizard-config.js +138 -0
  23. package/lib/channels/channel-add-wizard.js +250 -0
  24. package/lib/channels/channel-artifact-generator.js +27 -13
  25. package/lib/channels/channel-cardinality.js +83 -0
  26. package/lib/channels/channel-catalog-icon.js +45 -0
  27. package/lib/channels/channel-cli-instructions.js +75 -0
  28. package/lib/channels/channel-configuration-map.js +213 -0
  29. package/lib/channels/channel-credential.js +11 -14
  30. package/lib/channels/channel-definition-project.js +101 -0
  31. package/lib/channels/channel-definition-registry.js +154 -0
  32. package/lib/channels/channel-description.js +59 -2
  33. package/lib/channels/channel-discovery.js +16 -2
  34. package/lib/channels/channel-external-system.js +86 -9
  35. package/lib/channels/channel-install-envelope-actions.js +88 -0
  36. package/lib/channels/channel-install-envelope-microsoft.js +155 -0
  37. package/lib/channels/channel-install-envelope.js +356 -0
  38. package/lib/channels/channel-install-help-md.js +172 -0
  39. package/lib/channels/channel-install-http-payload.js +62 -0
  40. package/lib/channels/channel-install-inventory.js +115 -0
  41. package/lib/channels/channel-install-profiles.js +90 -0
  42. package/lib/channels/channel-instance-collision.js +118 -0
  43. package/lib/channels/channel-instance-key.js +100 -0
  44. package/lib/channels/channel-instance-materialize.js +240 -0
  45. package/lib/channels/channel-integration-generator.js +16 -9
  46. package/lib/channels/channel-integration-template.js +101 -22
  47. package/lib/channels/channel-inventory-fetch.js +79 -0
  48. package/lib/channels/channel-oauth-preflight.js +104 -0
  49. package/lib/channels/channel-package-version.js +76 -0
  50. package/lib/channels/channel-predefined-client.js +154 -0
  51. package/lib/channels/channel-probe.js +7 -2
  52. package/lib/channels/channel-runtime-resolver.js +180 -72
  53. package/lib/channels/channel-status-display.js +86 -16
  54. package/lib/channels/channel-targets.js +106 -92
  55. package/lib/channels/chatgpt-callback.js +318 -0
  56. package/lib/channels/chatgpt-channel-add.js +229 -0
  57. package/lib/channels/chatgpt-preflight.js +396 -0
  58. package/lib/channels/chatgpt-runtime-contract.js +17 -0
  59. package/lib/channels/chatgpt-setup-values.js +124 -0
  60. package/lib/channels/claude-callback.js +67 -0
  61. package/lib/channels/claude-channel-add.js +254 -0
  62. package/lib/channels/claude-runtime-contract.js +40 -0
  63. package/lib/channels/claude-runtime.js +58 -0
  64. package/lib/channels/claude-setup-values.js +89 -0
  65. package/lib/channels/mcp-client/config-merge.js +148 -0
  66. package/lib/channels/mcp-client/ensure-client.js +231 -0
  67. package/lib/channels/mcp-client/paths.js +53 -0
  68. package/lib/channels/mcp-client/profiles.js +122 -0
  69. package/lib/channels/mcp-client/redirect-uris.js +99 -0
  70. package/lib/channels/mcp-client/runtime.js +59 -0
  71. package/lib/channels/mcp-client-channel-add.js +186 -0
  72. package/lib/channels/microsoft-copilot-channel-add.js +117 -7
  73. package/lib/channels/microsoft-copilot-entra-identifier-uris.js +187 -0
  74. package/lib/channels/microsoft-copilot-entra-sso-form.js +106 -18
  75. package/lib/channels/microsoft-copilot-entra-sso.js +31 -2
  76. package/lib/channels/microsoft-copilot-package-prior.js +107 -0
  77. package/lib/channels/microsoft-copilot-package.js +29 -28
  78. package/lib/channels/microsoft-provisioning/a365.js +80 -0
  79. package/lib/channels/microsoft-provisioning/auth-config.js +240 -0
  80. package/lib/channels/microsoft-provisioning/auth.js +179 -0
  81. package/lib/channels/microsoft-provisioning/cli.js +71 -0
  82. package/lib/channels/microsoft-provisioning/constants.js +75 -0
  83. package/lib/channels/microsoft-provisioning/errors.js +117 -0
  84. package/lib/channels/microsoft-provisioning/index.js +55 -0
  85. package/lib/channels/microsoft-provisioning/install-atk.js +158 -0
  86. package/lib/channels/microsoft-provisioning/install-teams.js +115 -0
  87. package/lib/channels/microsoft-provisioning/login-portal-device.js +236 -0
  88. package/lib/channels/microsoft-provisioning/login-portal-parse.js +71 -0
  89. package/lib/channels/microsoft-provisioning/login-portal-teams.js +193 -0
  90. package/lib/channels/microsoft-provisioning/login-portal.js +230 -0
  91. package/lib/channels/microsoft-provisioning/persist.js +110 -0
  92. package/lib/channels/microsoft-provisioning/teams-bot-args.js +119 -0
  93. package/lib/channels/microsoft-provisioning/teams-bot-controller.js +123 -0
  94. package/lib/channels/microsoft-provisioning/teams-bot-parse.js +134 -0
  95. package/lib/channels/microsoft-provisioning/teams-bot-persist.js +113 -0
  96. package/lib/channels/microsoft-provisioning/teams-bot-provision.js +234 -0
  97. package/lib/channels/microsoft-provisioning/teams-bot-secrets.js +45 -0
  98. package/lib/channels/microsoft-provisioning/teams-cli.js +82 -0
  99. package/lib/channels/runtime-tool-catalog.js +5 -2
  100. package/lib/channels/slack-channel-add-portal.js +130 -0
  101. package/lib/channels/slack-channel-add-tty-auth.js +95 -0
  102. package/lib/channels/slack-channel-add.js +258 -0
  103. package/lib/channels/slack-provisioning/apps.js +181 -0
  104. package/lib/channels/slack-provisioning/auth-store.js +110 -0
  105. package/lib/channels/slack-provisioning/auth-ticket.js +185 -0
  106. package/lib/channels/slack-provisioning/auth.js +115 -0
  107. package/lib/channels/slack-provisioning/callbacks.js +41 -0
  108. package/lib/channels/slack-provisioning/cli.js +226 -0
  109. package/lib/channels/slack-provisioning/constants.js +37 -0
  110. package/lib/channels/slack-provisioning/credentials.js +169 -0
  111. package/lib/channels/slack-provisioning/curl-shim.js +132 -0
  112. package/lib/channels/slack-provisioning/developer-install.js +118 -0
  113. package/lib/channels/slack-provisioning/env-file.js +120 -0
  114. package/lib/channels/slack-provisioning/errors.js +47 -0
  115. package/lib/channels/slack-provisioning/go-ca-bundle.js +137 -0
  116. package/lib/channels/slack-provisioning/index.js +250 -0
  117. package/lib/channels/slack-provisioning/install-cli.js +323 -0
  118. package/lib/channels/slack-provisioning/manifest.js +89 -0
  119. package/lib/channels/slack-provisioning/oauth-lock.js +33 -0
  120. package/lib/channels/slack-provisioning/offer-cli.js +113 -0
  121. package/lib/channels/slack-provisioning/onboard-members.js +122 -0
  122. package/lib/channels/slack-provisioning/onboard.js +89 -0
  123. package/lib/channels/slack-provisioning/persist.js +95 -0
  124. package/lib/channels/slack-provisioning/remaining-urls.js +158 -0
  125. package/lib/channels/slack-provisioning/with-curl-on-path.js +48 -0
  126. package/lib/channels/teams-bot-channel-add.js +94 -0
  127. package/lib/channels/vendor-dataplane-url.js +249 -0
  128. package/lib/cli/setup-certification.help.js +2 -0
  129. package/lib/cli/setup-external-system-help.js +2 -0
  130. package/lib/cli/setup-external-system.js +1 -1
  131. package/lib/commands/channel-add-help.js +366 -0
  132. package/lib/commands/channel-add-setup.js +162 -80
  133. package/lib/commands/channel-configure.js +118 -0
  134. package/lib/commands/channel.js +10 -1
  135. package/lib/commands/governance-upload-external.js +6 -3
  136. package/lib/commands/lifecycle-command-action.js +2 -5
  137. package/lib/commands/lifecycle-external.js +93 -21
  138. package/lib/commands/repair-datasource-test-payload.js +4 -2
  139. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +23 -2
  140. package/lib/commands/repair-datasource.js +3 -0
  141. package/lib/commands/repair-external-run.js +59 -25
  142. package/lib/commands/repair-openapi-rest-runtime.js +127 -0
  143. package/lib/commands/repair-orchestration-tag-merge.js +339 -0
  144. package/lib/commands/repair-rbac-catalog-defaults.js +96 -0
  145. package/lib/commands/repair-rbac-platform-preset.js +26 -15
  146. package/lib/commands/repair-rbac-reader-roles.js +8 -21
  147. package/lib/commands/repair-rbac.js +10 -34
  148. package/lib/commands/repair-system-permissions.js +24 -22
  149. package/lib/commands/role-assistant-test-output.js +14 -0
  150. package/lib/commands/role-assistant.js +59 -21
  151. package/lib/commands/test-e2e-external.js +148 -21
  152. package/lib/commands/upload-credential-push-log.js +2 -1
  153. package/lib/commands/upload-publish-context.js +1 -1
  154. package/lib/commands/upload-role-assistant-shell.js +124 -0
  155. package/lib/commands/upload-sidecar-followups.js +6 -3
  156. package/lib/commands/upload.js +18 -3
  157. package/lib/core/secrets-docker-env.js +4 -1
  158. package/lib/core/secrets-env-content.js +17 -3
  159. package/lib/datasource/capability/capability-manifest.js +0 -3
  160. package/lib/datasource/capability/copy-test-payload.js +3 -0
  161. package/lib/external-system/deploy-helpers.js +26 -1
  162. package/lib/external-system/deploy.js +10 -6
  163. package/lib/external-system/download-helpers.js +29 -2
  164. package/lib/external-system/download-role-assistant.js +83 -4
  165. package/lib/external-system/download.js +40 -30
  166. package/lib/external-system/sync-deploy-manifest.js +3 -2
  167. package/lib/external-system/upload-identity.js +42 -0
  168. package/lib/generator/index.js +19 -15
  169. package/lib/generator/split.js +13 -2
  170. package/lib/lifecycle/role-assistant-governance.js +146 -0
  171. package/lib/lifecycle/scenario-upload.js +181 -30
  172. package/lib/programmatic/builder-api-cors.js +143 -0
  173. package/lib/programmatic/builder-api-request.js +6 -0
  174. package/lib/programmatic/builder-api-route-dispatch.js +56 -42
  175. package/lib/programmatic/builder-api-server.js +8 -1
  176. package/lib/programmatic/capability-registry.js +54 -0
  177. package/lib/programmatic/channel-install-help-resolve.js +359 -0
  178. package/lib/programmatic/channel-install-session.js +218 -0
  179. package/lib/programmatic/help-content/channel-onboarding.md +2 -0
  180. package/lib/programmatic/help-content/cip-overview.md +3 -1
  181. package/lib/programmatic/http-raw-response.js +62 -0
  182. package/lib/programmatic/openapi-channel-install-bindings.js +179 -0
  183. package/lib/programmatic/openapi-channel-install-schemas.js +286 -0
  184. package/lib/programmatic/openapi-component-schemas.js +18 -1
  185. package/lib/programmatic/openapi-description-enricher.js +3 -1
  186. package/lib/programmatic/openapi-descriptions.js +74 -3
  187. package/lib/programmatic/openapi-envelope-examples.js +117 -0
  188. package/lib/programmatic/openapi-operation-bindings.js +4 -1
  189. package/lib/programmatic/openapi-schema-descriptions-channel.js +316 -0
  190. package/lib/programmatic/openapi-spec.js +12 -8
  191. package/lib/programmatic/platform-rbac-catalog.js +36 -18
  192. package/lib/programmatic/route-handler-map.js +51 -1
  193. package/lib/programmatic/route-handlers-context.js +2 -1
  194. package/lib/programmatic/run-auth-permissions.js +54 -0
  195. package/lib/programmatic/run-channel-catalog.js +196 -0
  196. package/lib/programmatic/run-channel-install-artifacts.js +137 -0
  197. package/lib/programmatic/run-channel-install-copilot.js +353 -0
  198. package/lib/programmatic/run-channel-install-options.js +232 -0
  199. package/lib/programmatic/run-channel-install-teams-bot.js +204 -0
  200. package/lib/programmatic/run-channel-install.js +346 -0
  201. package/lib/programmatic/run-channel-package.js +87 -7
  202. package/lib/programmatic/workspace-context.js +2 -0
  203. package/lib/role-assistant/evidence-sync.js +4 -2
  204. package/lib/role-assistant/evidence-validation-issues.js +51 -0
  205. package/lib/role-assistant/knowledge-sync.js +10 -4
  206. package/lib/role-assistant/settings.js +49 -2
  207. package/lib/role-assistant/test-cases-evaluate.js +30 -2
  208. package/lib/role-assistant/test-cases-evidence.js +175 -0
  209. package/lib/role-assistant/test-cases-search.js +229 -0
  210. package/lib/role-assistant/test-cases.js +37 -4
  211. package/lib/role-assistant/test-runner-search.js +149 -0
  212. package/lib/role-assistant/test-runner-workhub-helpers.js +40 -2
  213. package/lib/role-assistant/test-runner-workhub-soft.js +9 -3
  214. package/lib/role-assistant/test-runner-workhub.js +29 -11
  215. package/lib/role-assistant/test-runner.js +16 -1
  216. package/lib/schema/external-datasource.schema.json +112 -18
  217. package/lib/schema/external-system.schema.json +159 -2
  218. package/lib/schema/mcp-client-profile.schema.json +103 -0
  219. package/lib/utils/app-path-resolve.js +4 -0
  220. package/lib/utils/authentication-env-template.js +44 -6
  221. package/lib/utils/credential-secrets-env.js +6 -1
  222. package/lib/utils/env-copy.js +19 -7
  223. package/lib/utils/external-env-template.js +20 -2
  224. package/lib/utils/paths.js +18 -1
  225. package/lib/utils/platform-shell-package.js +69 -0
  226. package/lib/utils/port-resolver.js +50 -1
  227. package/lib/utils/rewrite-name-flag.js +20 -0
  228. package/lib/utils/role-assistant-paths.js +13 -10
  229. package/lib/utils/schema-resolver.js +11 -1
  230. package/lib/utils/secrets-helpers.js +12 -21
  231. package/lib/utils/token-manager.js +10 -5
  232. package/lib/validation/validate.js +40 -0
  233. package/package.json +1 -1
  234. package/templates/README.md +1 -1
  235. package/templates/applications/builder-api/Dockerfile +1 -1
  236. package/templates/applications/builder-api/env.template +7 -0
  237. package/templates/applications/dataplane/application.yaml +6 -5
  238. package/templates/applications/dataplane/env.template +2 -2
  239. package/templates/applications/dataplane/rbac.yaml +7 -2
  240. package/templates/applications/keycloak/Dockerfile +30 -4
  241. package/templates/applications/keycloak/application.yaml +2 -2
  242. package/templates/applications/keycloak/cimd-lenient-parse/AbstractClientIdMetadataDocumentExecutor.java +896 -0
  243. package/templates/applications/keycloak/cimd-lenient-parse/SOURCE.md +19 -0
  244. package/templates/applications/keycloak/env.template +3 -0
  245. package/templates/applications/miso-controller/application.yaml +1 -2
  246. package/templates/applications/miso-controller/env.template +2 -0
  247. package/templates/channels/README.md +5 -5
  248. package/templates/channels/chatgpt/TROUBLESHOOTING.md +51 -0
  249. package/templates/channels/chatgpt/channel.json +59 -0
  250. package/templates/channels/chatgpt/icon.svg +1 -0
  251. package/templates/channels/chatgpt/install.template.md.hbs +20 -95
  252. package/templates/channels/chatgpt/integration/README.md +1 -1
  253. package/templates/channels/chatgpt/integration/application.json +2 -2
  254. package/templates/channels/chatgpt/integration/chatgpt-system.json +49 -4
  255. package/templates/channels/chatgpt/integration/env.template +2 -4
  256. package/templates/channels/chatgpt/integration/rbac.json +13 -0
  257. package/templates/channels/claude/TROUBLESHOOTING.md +41 -0
  258. package/templates/channels/claude/channel.json +59 -0
  259. package/templates/channels/claude/icon.svg +1 -0
  260. package/templates/channels/claude/install.template.md.hbs +21 -0
  261. package/templates/channels/claude/integration/README.md +14 -0
  262. package/templates/channels/claude/integration/application.json +15 -0
  263. package/templates/channels/claude/integration/claude-system.json +59 -0
  264. package/templates/channels/claude/integration/env.template +2 -0
  265. package/templates/channels/claude/integration/rbac.json +13 -0
  266. package/templates/channels/claude/manifest.template.json.hbs +40 -0
  267. package/templates/channels/mcp-client/channel.json +72 -0
  268. package/templates/channels/mcp-client/icon.svg +1 -0
  269. package/templates/channels/mcp-client/install.template.md.hbs +24 -0
  270. package/templates/channels/mcp-client/integration/README.md +13 -0
  271. package/templates/channels/mcp-client/integration/application.json +15 -0
  272. package/templates/channels/mcp-client/integration/env.template +2 -0
  273. package/templates/channels/mcp-client/integration/mcp-client-system.json +72 -0
  274. package/templates/channels/mcp-client/integration/rbac.json +13 -0
  275. package/templates/channels/mcp-client/manifest.template.json.hbs +41 -0
  276. package/templates/channels/mcp-client/profiles/cursor.json +26 -0
  277. package/templates/channels/mcp-client/profiles/github-copilot.json +29 -0
  278. package/templates/channels/mcp-client/profiles/vscode.json +29 -0
  279. package/templates/channels/microsoft-copilot/channel.json +73 -0
  280. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +8 -4
  281. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +65 -5
  282. package/templates/channels/microsoft-copilot/icon.svg +1 -0
  283. package/templates/channels/microsoft-copilot/install.template.md.hbs +9 -9
  284. package/templates/channels/microsoft-copilot/integration/README.md +1 -1
  285. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  286. package/templates/channels/microsoft-copilot/integration/env.template +3 -6
  287. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +63 -4
  288. package/templates/channels/microsoft-copilot/integration/rbac.json +13 -0
  289. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +5 -5
  290. package/templates/channels/slack/channel.json +223 -0
  291. package/templates/channels/slack/icon.svg +1 -0
  292. package/templates/channels/slack/install.template.md.hbs +36 -28
  293. package/templates/channels/slack/integration/README.md +245 -22
  294. package/templates/channels/slack/integration/application.json +44 -7
  295. package/templates/channels/slack/integration/env.template +15 -6
  296. package/templates/channels/slack/integration/openapi/openapi.json +126 -0
  297. package/templates/channels/slack/integration/rbac.json +84 -0
  298. package/templates/channels/slack/integration/slack-datasource-commands.json +154 -0
  299. package/templates/channels/slack/integration/slack-datasource-conversations.json +28 -3
  300. package/templates/channels/slack/integration/slack-datasource-events.json +154 -0
  301. package/templates/channels/slack/integration/slack-datasource-interactive.json +154 -0
  302. package/templates/channels/slack/integration/slack-datasource-messages.json +40 -2
  303. package/templates/channels/slack/integration/slack-system.json +155 -77
  304. package/templates/channels/slack/manifest.template.json.hbs +5 -2
  305. package/templates/channels/teams-bot/channel.json +161 -0
  306. package/templates/channels/teams-bot/icon.svg +1 -0
  307. package/templates/channels/teams-bot/install.template.md.hbs +12 -13
  308. package/templates/channels/teams-bot/integration/README.md +5 -4
  309. package/templates/channels/teams-bot/integration/application.json +6 -4
  310. package/templates/channels/teams-bot/integration/env.template +5 -4
  311. package/templates/channels/teams-bot/integration/openapi/openapi.json +68 -0
  312. package/templates/channels/teams-bot/integration/rbac.json +21 -0
  313. package/templates/channels/teams-bot/integration/teams-bot-datasource-activities.json +154 -0
  314. package/templates/channels/teams-bot/integration/teams-bot-system.json +128 -23
  315. package/templates/channels/web-embed/channel.json +60 -0
  316. package/templates/channels/web-embed/install.template.md.hbs +1 -1
  317. package/templates/channels/web-embed/integration/README.md +2 -2
  318. package/templates/channels/web-embed/integration/application.json +2 -2
  319. package/templates/channels/web-embed/integration/env.template +1 -1
  320. package/templates/channels/web-embed/integration/rbac.json +13 -0
  321. package/templates/channels/web-embed/integration/web-embed-system.json +50 -4
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Wizard prompts from Channel Definition configuration/portalInput.
3
+ *
4
+ * @fileoverview Prompt only required missing portal fields
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const inquirer = require('inquirer');
12
+ const { getChannelDefinition } = require('./channel-definition-registry');
13
+ const {
14
+ envNameToPropertyKey,
15
+ listDefinitionConfiguration,
16
+ isSecretShapedValue
17
+ } = require('./channel-configuration-map');
18
+
19
+ /**
20
+ * @param {unknown} options
21
+ * @returns {boolean}
22
+ */
23
+ function isTrueFalseOptions(options) {
24
+ if (!Array.isArray(options) || options.length !== 2) {
25
+ return false;
26
+ }
27
+ const values = new Set(options.map((item) => String(item).trim().toLowerCase()));
28
+ return values.has('true') && values.has('false');
29
+ }
30
+
31
+ /**
32
+ * @param {string} field
33
+ * @param {unknown} [options]
34
+ * @returns {string}
35
+ */
36
+ function inquirerTypeForPortalField(field, options) {
37
+ if (isTrueFalseOptions(options)) {
38
+ return 'confirm';
39
+ }
40
+ if (field === 'select') {
41
+ return 'list';
42
+ }
43
+ if (field === 'password') {
44
+ return 'password';
45
+ }
46
+ return 'input';
47
+ }
48
+
49
+ /**
50
+ * @param {Object} options
51
+ * @param {string} propertyKey
52
+ * @returns {boolean}
53
+ */
54
+ function isMissingConfigValue(options, propertyKey) {
55
+ const value = options[propertyKey];
56
+ return value === undefined || value === null || value === '';
57
+ }
58
+
59
+ /**
60
+ * @param {Object} row
61
+ * @param {Object} options
62
+ * @returns {boolean}
63
+ */
64
+ function shouldPromptConfigurationRow(row, options) {
65
+ if (!row || !row.portalInput || !row.portalInput.label) {
66
+ return false;
67
+ }
68
+ if (isSecretShapedValue(row.value)) {
69
+ return false;
70
+ }
71
+ const propertyKey = envNameToPropertyKey(row.name);
72
+ if (!isMissingConfigValue(options, propertyKey)) {
73
+ return false;
74
+ }
75
+ return row.required === true;
76
+ }
77
+
78
+ /**
79
+ * @param {Object} row
80
+ * @returns {Object}
81
+ */
82
+ function configurationQuestionFromRow(row) {
83
+ const portal = row.portalInput;
84
+ const propertyKey = envNameToPropertyKey(row.name);
85
+ const question = {
86
+ type: inquirerTypeForPortalField(portal.field, portal.options),
87
+ name: propertyKey,
88
+ message: portal.label
89
+ };
90
+ if (question.type === 'confirm') {
91
+ question.default = /^(true|1|yes|on)$/i.test(String(row.value || ''));
92
+ return question;
93
+ }
94
+ if (portal.placeholder) {
95
+ question.default = portal.placeholder;
96
+ }
97
+ if (Array.isArray(portal.options)) {
98
+ question.choices = portal.options;
99
+ }
100
+ return question;
101
+ }
102
+
103
+ /**
104
+ * @param {Object} definition
105
+ * @param {Object} [options]
106
+ * @returns {Object[]}
107
+ */
108
+ function missingConfigurationQuestions(definition, options = {}) {
109
+ return listDefinitionConfiguration(definition)
110
+ .filter((row) => shouldPromptConfigurationRow(row, options))
111
+ .map(configurationQuestionFromRow);
112
+ }
113
+
114
+ /**
115
+ * @param {string} definitionKey
116
+ * @param {Object} options
117
+ * @param {Object} [deps]
118
+ * @returns {Promise<Object>}
119
+ */
120
+ async function promptMissingDefinitionConfiguration(definitionKey, options, deps = {}) {
121
+ const definition = getChannelDefinition(definitionKey);
122
+ if (!definition) {
123
+ return {};
124
+ }
125
+ const questions = missingConfigurationQuestions(definition, options);
126
+ if (questions.length === 0) {
127
+ return {};
128
+ }
129
+ const prompt = deps.prompt || ((qs) => inquirer.prompt(qs));
130
+ return prompt(questions);
131
+ }
132
+
133
+ module.exports = {
134
+ inquirerTypeForPortalField,
135
+ shouldPromptConfigurationRow,
136
+ missingConfigurationQuestions,
137
+ promptMissingDefinitionConfiguration
138
+ };
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Interactive prompts when `aifabrix channel add` omits the channel target.
3
+ *
4
+ * @fileoverview TTY wizard for name + live channel selection; no mutations until dispatch
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const inquirer = require('inquirer');
12
+ const config = require('../core/config');
13
+ const {
14
+ formatSupportedChannelTargetsHelp,
15
+ listWizardChannelChoices
16
+ } = require('./channel-targets');
17
+ const { listMcpClientProfileChoices } = require('./mcp-client/profiles');
18
+ const { promptMissingDefinitionConfiguration } = require('./channel-add-wizard-config');
19
+
20
+ const CHANNEL_ADD_CANCELLED = 'CHANNEL_ADD_CANCELLED';
21
+
22
+ /**
23
+ * Usage text when the wizard cannot run (CI, non-TTY, JSON).
24
+ * @returns {string}
25
+ */
26
+ function formatNonInteractiveChannelAddError() {
27
+ return [
28
+ 'Channel add needs a terminal to choose a channel, or an explicit target:',
29
+ ' $ aifabrix channel add slack --name "Elsa"',
30
+ formatSupportedChannelTargetsHelp()
31
+ ].join('\n');
32
+ }
33
+
34
+ /**
35
+ * @param {Error|null|undefined} error
36
+ * @returns {boolean}
37
+ */
38
+ function isChannelAddCancelled(error) {
39
+ return Boolean(error && error.code === CHANNEL_ADD_CANCELLED);
40
+ }
41
+
42
+ /**
43
+ * @returns {Error}
44
+ */
45
+ function createChannelAddCancelledError() {
46
+ const error = new Error('Channel add cancelled.');
47
+ error.code = CHANNEL_ADD_CANCELLED;
48
+ return error;
49
+ }
50
+
51
+ /**
52
+ * @param {Error} error
53
+ * @returns {boolean}
54
+ */
55
+ function isInquirerCancel(error) {
56
+ if (!error) {
57
+ return false;
58
+ }
59
+ if (error.name === 'ExitPromptError') {
60
+ return true;
61
+ }
62
+ const message = String(error.message || '');
63
+ return /force closed|user force closed|prompt was canceled/i.test(message);
64
+ }
65
+
66
+ /**
67
+ * TTY wizard only; CI and JSON stay on the explicit command.
68
+ * @param {Object} [options]
69
+ * @returns {boolean}
70
+ */
71
+ function isChannelAddInteractive(options = {}) {
72
+ if (options.json === true) {
73
+ return false;
74
+ }
75
+ if (process.env.CI === 'true' || process.env.CI === '1') {
76
+ return false;
77
+ }
78
+ return Boolean(process.stdin && process.stdin.isTTY);
79
+ }
80
+
81
+ /**
82
+ * @param {string} value
83
+ * @returns {true|string}
84
+ */
85
+ function validateChannelName(value) {
86
+ if (!value || !String(value).trim()) {
87
+ return 'Channel name is required';
88
+ }
89
+ return true;
90
+ }
91
+
92
+ /**
93
+ * @returns {{ type: string, name: string, message: string, validate: Function }}
94
+ */
95
+ function channelNameQuestion() {
96
+ return {
97
+ type: 'input',
98
+ name: 'name',
99
+ message: 'Channel name:',
100
+ validate: validateChannelName
101
+ };
102
+ }
103
+
104
+ /**
105
+ * @param {Object} options
106
+ * @returns {{ type: string, name: string, message: string, choices: Object[] }}
107
+ */
108
+ function mcpClientProfileQuestion(options = {}) {
109
+ const already = options.target || options.profile || options.mcpClientProfile;
110
+ return {
111
+ type: 'list',
112
+ name: 'mcpClientProfile',
113
+ message: 'MCP client:',
114
+ choices: listMcpClientProfileChoices(),
115
+ when: (answers) => answers.channelTarget === 'mcp-client' && !already
116
+ };
117
+ }
118
+
119
+ /**
120
+ * @param {Object} options
121
+ * @returns {{ type: string, name: string, message: string, choices: Object[] }}
122
+ */
123
+ function channelTargetQuestion(options = {}) {
124
+ const choices = listWizardChannelChoices({
125
+ allowExperimental: options.allowExperimental === true
126
+ });
127
+ return {
128
+ type: 'list',
129
+ name: 'channelTarget',
130
+ message: 'Select channel:',
131
+ choices
132
+ };
133
+ }
134
+
135
+ /**
136
+ * @returns {{ type: string, name: string, message: string, default: string }}
137
+ */
138
+ function channelEnvironmentQuestion() {
139
+ return {
140
+ type: 'input',
141
+ name: 'environment',
142
+ message: 'Environment:',
143
+ default: 'dev'
144
+ };
145
+ }
146
+
147
+ /**
148
+ * @param {Object} [deps]
149
+ * @returns {Promise<string>}
150
+ */
151
+ async function resolveWizardEnvironment(deps = {}) {
152
+ const resolveEnvironment = deps.resolveEnvironment || (() => config.resolveEnvironment());
153
+ const env = await resolveEnvironment();
154
+ return env && String(env).trim() ? String(env).trim() : '';
155
+ }
156
+
157
+ /**
158
+ * @param {Object} options
159
+ * @param {Object} [deps]
160
+ * @returns {Promise<Object[]>}
161
+ */
162
+ async function buildChannelAddWizardQuestions(options = {}, deps = {}) {
163
+ const questions = [];
164
+ if (!options.name || !String(options.name).trim()) {
165
+ questions.push(channelNameQuestion());
166
+ }
167
+ questions.push(channelTargetQuestion(options));
168
+ questions.push(mcpClientProfileQuestion(options));
169
+ const env = await resolveWizardEnvironment(deps);
170
+ if (!env) {
171
+ questions.push(channelEnvironmentQuestion());
172
+ }
173
+ return questions;
174
+ }
175
+
176
+ /**
177
+ * @param {Object[]} questions
178
+ * @param {Object} [deps]
179
+ * @returns {Promise<Object>}
180
+ */
181
+ async function runChannelAddWizardPrompt(questions, deps = {}) {
182
+ const prompt = deps.prompt || ((qs) => inquirer.prompt(qs));
183
+ try {
184
+ return await prompt(questions);
185
+ } catch (error) {
186
+ if (isInquirerCancel(error)) {
187
+ throw createChannelAddCancelledError();
188
+ }
189
+ throw error;
190
+ }
191
+ }
192
+
193
+ /**
194
+ * @param {Object} options
195
+ * @param {Object} [deps]
196
+ * @returns {void}
197
+ */
198
+ function assertChannelAddWizardInteractive(options = {}, deps = {}) {
199
+ const isInteractive = deps.isInteractive || isChannelAddInteractive;
200
+ if (!isInteractive(options)) {
201
+ throw new Error(formatNonInteractiveChannelAddError());
202
+ }
203
+ }
204
+
205
+ /**
206
+ * @param {Object} options
207
+ * @param {Object} answers
208
+ * @returns {{ channelTarget: string, name: string, environment?: string, mcpClientProfile?: string }}
209
+ */
210
+ function selectionFromWizardAnswers(options, answers) {
211
+ const name = String(answers.name || options.name || '').trim();
212
+ if (validateChannelName(name) !== true) {
213
+ throw new Error('Channel name is required');
214
+ }
215
+ return {
216
+ channelTarget: answers.channelTarget,
217
+ name,
218
+ environment: answers.environment,
219
+ mcpClientProfile: answers.mcpClientProfile || options.target || options.profile
220
+ };
221
+ }
222
+
223
+ /**
224
+ * Prompt for missing channel add inputs. Does not write files or credentials.
225
+ * @param {Object} [options]
226
+ * @param {Object} [deps] - Inject prompt / resolveEnvironment / isInteractive in tests
227
+ * @returns {Promise<{ channelTarget: string, name: string, environment?: string }>}
228
+ */
229
+ async function promptChannelAddWizard(options = {}, deps = {}) {
230
+ assertChannelAddWizardInteractive(options, deps);
231
+ const questions = await buildChannelAddWizardQuestions(options, deps);
232
+ const answers = await runChannelAddWizardPrompt(questions, deps);
233
+ const selection = selectionFromWizardAnswers(options, answers);
234
+ const config = await promptMissingDefinitionConfiguration(
235
+ selection.channelTarget,
236
+ { ...options, ...selection },
237
+ deps
238
+ );
239
+ return { ...selection, ...config };
240
+ }
241
+
242
+ module.exports = {
243
+ CHANNEL_ADD_CANCELLED,
244
+ formatNonInteractiveChannelAddError,
245
+ isChannelAddCancelled,
246
+ isChannelAddInteractive,
247
+ validateChannelName,
248
+ buildChannelAddWizardQuestions,
249
+ promptChannelAddWizard
250
+ };
@@ -15,6 +15,7 @@ const Handlebars = require('handlebars');
15
15
  const { getChannelTarget } = require('./channel-targets');
16
16
  const { BUILDER_ROOT } = require('./channel-integration-template');
17
17
  const { generateMicrosoftCopilotPackage } = require('./microsoft-copilot-package');
18
+ const { fillSlackManifestDescriptions } = require('./channel-description');
18
19
 
19
20
  const SECRET_PATTERNS = [/xoxb-/i, /xoxp-/i, /client_secret/i, /signing_secret/i, /Bearer\s+/i];
20
21
  const PLACEHOLDER_DOMAIN_RE = /\byour-domain\.com\b/i;
@@ -106,6 +107,23 @@ function renderInstallGuideIfPresent(templatesDir, runtimeValues, outputDir, dry
106
107
  return { installPath, content };
107
108
  }
108
109
 
110
+ /**
111
+ * @param {Object} params
112
+ * @param {string} templatesDir
113
+ * @param {string} outputPath
114
+ * @returns {{ outputPath: string, artifactHash: string, content: string, installPath?: string }}
115
+ */
116
+ function generateCopilotChannelArtifact(params, templatesDir, outputPath) {
117
+ return generateMicrosoftCopilotPackage({
118
+ runtimeValues: params.runtimeValues,
119
+ templatesDir,
120
+ outputPath,
121
+ dryRun: params.dryRun === true,
122
+ assertNoSecretsInArtifact,
123
+ renderTemplateFile
124
+ });
125
+ }
126
+
109
127
  /**
110
128
  * @param {Object} params
111
129
  * @param {string} params.projectRoot
@@ -117,20 +135,16 @@ function renderInstallGuideIfPresent(templatesDir, runtimeValues, outputDir, dry
117
135
  function generateChannelArtifact(params) {
118
136
  const templatesDir = getChannelTemplatesDir(params.channelTarget);
119
137
  const outputPath = getArtifactOutputPath(params.projectRoot, params.channelTarget);
120
-
121
138
  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
- });
139
+ return generateCopilotChannelArtifact(params, templatesDir, outputPath);
130
140
  }
131
141
 
142
+ const runtimeValues = fillSlackManifestDescriptions(
143
+ params.channelTarget,
144
+ params.runtimeValues
145
+ );
132
146
  const manifestTemplate = path.join(templatesDir, 'manifest.template.json.hbs');
133
- const content = renderTemplateFile(manifestTemplate, params.runtimeValues);
147
+ const content = renderTemplateFile(manifestTemplate, runtimeValues);
134
148
  assertNoSecretsInArtifact(content);
135
149
  const artifactHash = crypto.createHash('sha256').update(content, 'utf8').digest('hex');
136
150
  const outputDir = path.dirname(outputPath);
@@ -142,13 +156,13 @@ function generateChannelArtifact(params) {
142
156
 
143
157
  const install = renderInstallGuideIfPresent(
144
158
  templatesDir,
145
- params.runtimeValues,
159
+ runtimeValues,
146
160
  outputDir,
147
161
  params.dryRun === true
148
162
  );
149
163
 
150
- const installReady = params.runtimeValues?.installReady === true;
151
- const blocker = params.runtimeValues?.discoveryBlocker || null;
164
+ const installReady = runtimeValues?.installReady === true;
165
+ const blocker = runtimeValues?.discoveryBlocker || null;
152
166
  return {
153
167
  outputPath,
154
168
  artifactHash,
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Channel Definition instance cardinality (projection + install gate).
3
+ *
4
+ * @fileoverview maxInstances is schema; canInstall is Builder-only
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { getChannelDefinition } = require('./channel-definition-registry');
12
+ const { installationsForDefinition } = require('./channel-install-inventory');
13
+ const { listConnectedSystemsForInventory } = require('./channel-inventory-fetch');
14
+ const { httpError } = require('../programmatic/channel-install-session');
15
+
16
+ const AT_CAP_REASON = 'Maximum installations reached for this channel.';
17
+
18
+ /**
19
+ * @param {Object|null} definition
20
+ * @returns {number|null}
21
+ */
22
+ function maxInstancesOf(definition) {
23
+ const value = definition && definition.channel && definition.channel.maxInstances;
24
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < 1) {
25
+ return null;
26
+ }
27
+ return Math.floor(value);
28
+ }
29
+
30
+ /**
31
+ * Catalog/detail projection. Not an External System schema field except maxInstances.
32
+ * @param {Object|null} definition
33
+ * @param {Object[]} [installations]
34
+ * @returns {Object}
35
+ */
36
+ function cardinalityProjection(definition, installations = []) {
37
+ const maxInstances = maxInstancesOf(definition);
38
+ const installationCount = Array.isArray(installations) ? installations.length : 0;
39
+ const projection = {
40
+ installationCount,
41
+ canInstall: true
42
+ };
43
+ if (maxInstances === null) {
44
+ return projection;
45
+ }
46
+ projection.maxInstances = maxInstances;
47
+ projection.canInstall = installationCount < maxInstances;
48
+ if (!projection.canInstall) {
49
+ projection.blockedReason = AT_CAP_REASON;
50
+ }
51
+ return projection;
52
+ }
53
+
54
+ /**
55
+ * Refuse a new install when the definition is already at maxInstances.
56
+ * Continue/force of an existing instance skips this gate.
57
+ * @param {import('../programmatic/workspace-context').WorkspaceContext} ctx
58
+ * @param {string} definitionKey
59
+ * @param {Object} [options]
60
+ * @returns {Promise<void>}
61
+ */
62
+ async function assertChannelCardinality(ctx, definitionKey, options = {}) {
63
+ if (options.continueInstall === true || options.force === true) {
64
+ return;
65
+ }
66
+ const definition = getChannelDefinition(definitionKey);
67
+ if (maxInstancesOf(definition) === null) {
68
+ return;
69
+ }
70
+ const systems = await listConnectedSystemsForInventory(ctx, options.deps || {});
71
+ const installations = installationsForDefinition(systems, definitionKey);
72
+ const projection = cardinalityProjection(definition, installations);
73
+ if (projection.canInstall !== true) {
74
+ throw httpError(projection.blockedReason || AT_CAP_REASON, 409);
75
+ }
76
+ }
77
+
78
+ module.exports = {
79
+ AT_CAP_REASON,
80
+ maxInstancesOf,
81
+ cardinalityProjection,
82
+ assertChannelCardinality
83
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Catalog icons live next to Channel Definitions (templates/channels/<key>/icon.svg).
3
+ *
4
+ * @fileoverview Safe data-URL icon for published listChannelTargets rows
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const { TEMPLATES_ROOT, channelDiskFs } = require('./channel-definition-registry');
13
+
14
+ /**
15
+ * @param {string} raw
16
+ * @returns {string|undefined}
17
+ */
18
+ function svgDataUrl(raw) {
19
+ const svg = String(raw || '').trim();
20
+ if (!svg.startsWith('<svg') && !svg.startsWith('<?xml')) {
21
+ return undefined;
22
+ }
23
+ return `data:image/svg+xml;base64,${Buffer.from(svg, 'utf8').toString('base64')}`;
24
+ }
25
+
26
+ /**
27
+ * @param {string} definitionKey
28
+ * @returns {string|undefined}
29
+ */
30
+ function catalogIconHref(definitionKey) {
31
+ if (!definitionKey || typeof definitionKey !== 'string') {
32
+ return undefined;
33
+ }
34
+ const iconPath = path.join(TEMPLATES_ROOT, definitionKey, 'icon.svg');
35
+ try {
36
+ return svgDataUrl(channelDiskFs().readFileSync(iconPath, 'utf8'));
37
+ } catch (err) {
38
+ if (err && err.code === 'ENOENT') {
39
+ return undefined;
40
+ }
41
+ throw err;
42
+ }
43
+ }
44
+
45
+ module.exports = { catalogIconHref };
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Generated CLI install instructions for provisioning.mode=cli.
3
+ *
4
+ * @fileoverview Copy-pasteable aifabrix channel add command
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { listDefinitionConfiguration, envNameToPropertyKey } = require('./channel-configuration-map');
12
+
13
+ /**
14
+ * @param {string} name
15
+ * @returns {string}
16
+ */
17
+ function quoteCliName(name) {
18
+ return `"${String(name || '').replace(/"/g, '\\"')}"`;
19
+ }
20
+
21
+ /**
22
+ * @param {Object} definition
23
+ * @returns {string[]}
24
+ */
25
+ function requiredConfigurationFlags(definition) {
26
+ const flags = [];
27
+ for (const row of listDefinitionConfiguration(definition)) {
28
+ if (row.required !== true) {
29
+ continue;
30
+ }
31
+ const propertyKey = envNameToPropertyKey(row.name);
32
+ flags.push(`--${propertyKey.replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`)} <${propertyKey}>`);
33
+ }
34
+ return flags;
35
+ }
36
+
37
+ /**
38
+ * @param {Object} definition
39
+ * @param {{ name?: string }} [opts]
40
+ * @returns {string}
41
+ */
42
+ function formatChannelCliInstructions(definition, opts = {}) {
43
+ const name = opts.name || 'Finance';
44
+ const required = requiredConfigurationFlags(definition).join(' ');
45
+ const command = [
46
+ `aifabrix channel add ${definition.key} --name ${quoteCliName(name)}`,
47
+ required
48
+ ]
49
+ .filter(Boolean)
50
+ .join(' ');
51
+ return [
52
+ 'Installation method: AI Fabrix CLI',
53
+ '',
54
+ command,
55
+ '',
56
+ 'Run this from an authenticated AI Fabrix Builder environment.'
57
+ ].join('\n');
58
+ }
59
+
60
+ /**
61
+ * @param {Object} definition
62
+ * @returns {boolean}
63
+ */
64
+ function isCliProvisioningMode(definition) {
65
+ const mode = definition && definition.channel && definition.channel.provisioning
66
+ ? definition.channel.provisioning.mode
67
+ : '';
68
+ return mode === 'cli';
69
+ }
70
+
71
+ module.exports = {
72
+ formatChannelCliInstructions,
73
+ isCliProvisioningMode,
74
+ requiredConfigurationFlags
75
+ };