@aifabrix/builder 2.53.0 → 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 +119 -16
  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
@@ -1,92 +1,72 @@
1
1
  /**
2
- * Channel target registry for assistant communication channels (plan 152).
2
+ * Channel target registry projected from Channel Definitions.
3
3
  *
4
- * @fileoverview Channel install targets — templates, capabilities, approval semantics
4
+ * @fileoverview CHANNEL_TARGETS is a projection of templates/channels/<key>/channel.json
5
5
  * @author AI Fabrix Team
6
- * @version 1.0.0
6
+ * @version 1.2.0
7
7
  */
8
8
 
9
9
  'use strict';
10
10
 
11
11
  const {
12
- CHATGPT_RUNTIME_SYSTEM_KEY,
13
- COPILOT_RUNTIME_SYSTEM_KEY
14
- } = require('./chatgpt-runtime-contract');
12
+ getChannelDefinition,
13
+ getChannelDefinitionMap,
14
+ listChannelDefinitionKeys
15
+ } = require('./channel-definition-registry');
16
+ const { toChannelTargetProjection } = require('./channel-definition-project');
15
17
 
16
- /** @typedef {'teams-bot'|'slack'|'microsoft-copilot'|'chatgpt'|'web-embed'} ChannelTargetKey */
18
+ function buildChannelTargets() {
19
+ const targets = {};
20
+ for (const [key, definition] of getChannelDefinitionMap().entries()) {
21
+ targets[key] = toChannelTargetProjection(definition);
22
+ }
23
+ return targets;
24
+ }
17
25
 
18
- const DEFAULT_CAPABILITIES = ['chat', 'notify', 'documentApproval'];
26
+ /** @type {Record<string, Object>|null} */
27
+ let cachedChannelTargets = null;
19
28
 
20
- const CHANNEL_TARGETS = {
21
- 'teams-bot': {
22
- key: 'teams-bot',
23
- channelType: 'teamsBot',
24
- templateDir: 'teams-bot',
25
- artifactFileName: 'channel-teams-bot.json',
26
- artifactFormat: 'json',
27
- credentialKey: 'teams-bot',
28
- routeSystemKey: 'teams',
29
- capabilities: DEFAULT_CAPABILITIES,
30
- approvalRequired: true,
31
- experimental: false
32
- },
33
- slack: {
34
- key: 'slack',
35
- channelType: 'slack',
36
- templateDir: 'slack',
37
- artifactFileName: 'channel-slack.json',
38
- artifactFormat: 'json',
39
- credentialKey: 'slack-app',
40
- routeSystemKey: 'slack',
41
- capabilities: DEFAULT_CAPABILITIES,
42
- approvalRequired: true,
43
- experimental: false
44
- },
45
- 'microsoft-copilot': {
46
- key: 'microsoft-copilot',
47
- channelType: 'microsoftCopilot',
48
- templateDir: 'microsoft-copilot',
49
- artifactFileName: 'channel-microsoft-copilot.json',
50
- artifactFormat: 'json',
51
- credentialKey: 'microsoft-copilot-channel',
52
- routeSystemKey: COPILOT_RUNTIME_SYSTEM_KEY,
53
- capabilities: [...DEFAULT_CAPABILITIES, 'enterpriseActions'],
54
- approvalRequired: true,
55
- experimental: false
56
- },
57
- chatgpt: {
58
- key: 'chatgpt',
59
- channelType: 'chatgpt',
60
- templateDir: 'chatgpt',
61
- artifactFileName: 'channel-chatgpt.json',
62
- artifactFormat: 'json',
63
- credentialKey: 'chatgpt-channel',
64
- routeSystemKey: CHATGPT_RUNTIME_SYSTEM_KEY,
65
- capabilities: ['chat', 'enterpriseActions'],
66
- approvalRequired: true,
67
- experimental: false
68
- },
69
- 'web-embed': {
70
- key: 'web-embed',
71
- channelType: 'webEmbed',
72
- templateDir: 'web-embed',
73
- artifactFileName: 'channel-web-embed.json',
74
- artifactFormat: 'json',
75
- credentialKey: 'web-embed-channel',
76
- capabilities: DEFAULT_CAPABILITIES,
77
- approvalRequired: false,
78
- experimental: true
29
+ function resolveChannelTargets() {
30
+ if (cachedChannelTargets && Object.keys(cachedChannelTargets).length > 0) {
31
+ return cachedChannelTargets;
79
32
  }
80
- };
81
-
82
- const CHANNEL_TARGET_KEYS = Object.keys(CHANNEL_TARGETS);
33
+ cachedChannelTargets = buildChannelTargets();
34
+ return cachedChannelTargets;
35
+ }
83
36
 
84
37
  /**
85
38
  * @param {string} channelTarget
86
39
  * @returns {boolean}
87
40
  */
88
41
  function isChannelTargetKey(channelTarget) {
89
- return typeof channelTarget === 'string' && CHANNEL_TARGET_KEYS.includes(channelTarget);
42
+ return typeof channelTarget === 'string' && channelTarget in resolveChannelTargets();
43
+ }
44
+
45
+ /**
46
+ * Human-readable supported channel list for CLI help and error hints.
47
+ * @returns {string}
48
+ */
49
+ function formatSupportedChannelTargetsHelp() {
50
+ const targets = resolveChannelTargets();
51
+ const keys = Object.keys(targets);
52
+ const stable = keys.filter((key) => !targets[key].experimental);
53
+ const experimental = keys.filter((key) => targets[key].experimental);
54
+ const lines = ['Supported channels:', ` ${stable.join(', ')}`];
55
+ if (experimental.length > 0) {
56
+ lines.push(
57
+ ` ${experimental.join(', ')} (experimental — scaffold with --allow-experimental)`
58
+ );
59
+ }
60
+ return lines.join('\n');
61
+ }
62
+
63
+ /**
64
+ * @param {string} channelTarget
65
+ * @returns {string}
66
+ */
67
+ function formatUnknownChannelTargetError(channelTarget) {
68
+ const live = listLiveChannelTargetKeys().join(', ');
69
+ return `Unknown channel: ${channelTarget}\nLive channels: ${live}`;
90
70
  }
91
71
 
92
72
  /**
@@ -95,11 +75,51 @@ function isChannelTargetKey(channelTarget) {
95
75
  */
96
76
  function getChannelTarget(channelTarget) {
97
77
  if (!isChannelTargetKey(channelTarget)) {
78
+ throw new Error(formatUnknownChannelTargetError(channelTarget));
79
+ }
80
+ return resolveChannelTargets()[channelTarget];
81
+ }
82
+
83
+ /**
84
+ * Live keys, plus experimental when `--allow-experimental` is set.
85
+ * @param {{ allowExperimental?: boolean }} [options]
86
+ * @returns {string[]}
87
+ */
88
+ function listLiveChannelTargetKeys(options = {}) {
89
+ const targets = resolveChannelTargets();
90
+ return Object.keys(targets).filter((key) => {
91
+ const target = targets[key];
92
+ return !target.experimental || options.allowExperimental === true;
93
+ });
94
+ }
95
+
96
+ /**
97
+ * Inquirer list choices for `aifabrix channel add` (labels from the registry).
98
+ * @param {{ allowExperimental?: boolean }} [options]
99
+ * @returns {{ name: string, value: string }[]}
100
+ */
101
+ function listWizardChannelChoices(options = {}) {
102
+ const targets = resolveChannelTargets();
103
+ return listLiveChannelTargetKeys(options)
104
+ .map((key) => targets[key])
105
+ .sort((a, b) => (a.wizardOrder || 100) - (b.wizardOrder || 100))
106
+ .map((target) => ({ name: target.displayLabel, value: target.key }));
107
+ }
108
+
109
+ /**
110
+ * Reject experimental targets unless `--allow-experimental` is set.
111
+ * @param {string} channelTarget
112
+ * @param {{ allowExperimental?: boolean }} [options]
113
+ * @returns {typeof CHANNEL_TARGETS[string]}
114
+ */
115
+ function assertChannelTargetInstallable(channelTarget, options = {}) {
116
+ const target = getChannelTarget(channelTarget);
117
+ if (target.experimental && options.allowExperimental !== true) {
98
118
  throw new Error(
99
- `Unknown channel target "${channelTarget}". Expected one of: ${CHANNEL_TARGET_KEYS.join(', ')}`
119
+ `Channel target "${channelTarget}" is experimental. Pass --allow-experimental to install.`
100
120
  );
101
121
  }
102
- return CHANNEL_TARGETS[channelTarget];
122
+ return target;
103
123
  }
104
124
 
105
125
  /**
@@ -113,27 +133,21 @@ function getDefaultChannelDescription() {
113
133
  );
114
134
  }
115
135
 
116
- /**
117
- * Human-readable supported channel list for CLI help and error hints.
118
- * @returns {string}
119
- */
120
- function formatSupportedChannelTargetsHelp() {
121
- const stable = CHANNEL_TARGET_KEYS.filter((key) => !CHANNEL_TARGETS[key].experimental);
122
- const experimental = CHANNEL_TARGET_KEYS.filter((key) => CHANNEL_TARGETS[key].experimental);
123
- const lines = ['Supported channels:', ` ${stable.join(', ')}`];
124
- if (experimental.length > 0) {
125
- lines.push(
126
- ` ${experimental.join(', ')} (experimental — scaffold with --allow-experimental)`
127
- );
128
- }
129
- return lines.join('\n');
130
- }
131
-
132
136
  module.exports = {
133
- CHANNEL_TARGETS,
134
- CHANNEL_TARGET_KEYS,
137
+ get CHANNEL_TARGETS() {
138
+ return resolveChannelTargets();
139
+ },
140
+ get CHANNEL_TARGET_KEYS() {
141
+ return Object.keys(resolveChannelTargets());
142
+ },
135
143
  isChannelTargetKey,
136
144
  getChannelTarget,
137
145
  getDefaultChannelDescription,
138
- formatSupportedChannelTargetsHelp
146
+ formatSupportedChannelTargetsHelp,
147
+ formatUnknownChannelTargetError,
148
+ listLiveChannelTargetKeys,
149
+ listWizardChannelChoices,
150
+ assertChannelTargetInstallable,
151
+ getChannelDefinition,
152
+ listChannelDefinitionKeys
139
153
  };
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Register ChatGPT callback URLs on the existing chatgpt Integration Client.
3
+ *
4
+ * Never rotates the client secret. Catalog sync is additive — extra URIs stay.
5
+ *
6
+ * @fileoverview HTTPS callback validation + idempotent redirect URI merge
7
+ * @author AI Fabrix Team
8
+ * @version 1.0.0
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const {
14
+ listIntegrationClients,
15
+ getIntegrationClient,
16
+ updateIntegrationClientRedirectUris
17
+ } = require('../api/integration-clients.api');
18
+ const {
19
+ CHATGPT_INTEGRATION_CLIENT_KEY,
20
+ CHATGPT_OAUTH2_PROFILE_ID,
21
+ CHATGPT_OAUTH2_PROFILE_PATH
22
+ } = require('./chatgpt-runtime-contract');
23
+
24
+ const CHATGPT_CALLBACK_CONTRACT = {
25
+ integrationClientKey: CHATGPT_INTEGRATION_CLIENT_KEY,
26
+ oauth2ProfileId: CHATGPT_OAUTH2_PROFILE_ID,
27
+ oauth2ProfilePath: CHATGPT_OAUTH2_PROFILE_PATH,
28
+ vendorLabel: 'ChatGPT'
29
+ };
30
+
31
+ /**
32
+ * @param {Object|null|undefined} contract
33
+ * @returns {typeof CHATGPT_CALLBACK_CONTRACT}
34
+ */
35
+ function resolveCallbackContract(contract) {
36
+ return {
37
+ integrationClientKey: (contract && contract.integrationClientKey) || CHATGPT_CALLBACK_CONTRACT.integrationClientKey,
38
+ oauth2ProfileId: (contract && contract.oauth2ProfileId) || CHATGPT_CALLBACK_CONTRACT.oauth2ProfileId,
39
+ oauth2ProfilePath: (contract && contract.oauth2ProfilePath) || CHATGPT_CALLBACK_CONTRACT.oauth2ProfilePath,
40
+ vendorLabel: (contract && contract.vendorLabel) || CHATGPT_CALLBACK_CONTRACT.vendorLabel
41
+ };
42
+ }
43
+
44
+ /**
45
+ * @param {string} message
46
+ * @param {string} [boundary]
47
+ * @param {Object} [contract]
48
+ * @returns {Error}
49
+ */
50
+ function createChatGptCallbackError(message, boundary, contract) {
51
+ const resolved = resolveCallbackContract(contract);
52
+ const error = new Error(`${resolved.vendorLabel} callback: ${message}`);
53
+ error.boundary = boundary || 'callback';
54
+ return error;
55
+ }
56
+
57
+ /**
58
+ * @param {string|undefined} value
59
+ * @returns {string}
60
+ */
61
+ function assertHttpsRedirectUri(value, contract) {
62
+ const trimmed = typeof value === 'string' ? value.trim() : '';
63
+ if (!trimmed) {
64
+ throw createChatGptCallbackError('Callback URL is required.', 'callback', contract);
65
+ }
66
+ let parsed;
67
+ try {
68
+ parsed = new URL(trimmed);
69
+ } catch {
70
+ throw createChatGptCallbackError(`Callback URL is not a valid URL: ${trimmed}`, 'callback', contract);
71
+ }
72
+ if (parsed.protocol !== 'https:') {
73
+ throw createChatGptCallbackError(`Callback URL must be HTTPS: ${trimmed}`, 'callback', contract);
74
+ }
75
+ return trimmed;
76
+ }
77
+
78
+ /**
79
+ * @param {string[]|undefined} existing
80
+ * @param {string} nextUri
81
+ * @returns {{ uris: string[], added: boolean }}
82
+ */
83
+ function mergeRedirectUris(existing, nextUri) {
84
+ const list = Array.isArray(existing)
85
+ ? existing.filter((uri) => typeof uri === 'string' && uri.trim())
86
+ : [];
87
+ if (list.some((uri) => uri.trim() === nextUri)) {
88
+ return { uris: list, added: false };
89
+ }
90
+ return { uris: [...list, nextUri], added: true };
91
+ }
92
+
93
+ /**
94
+ * @param {string[]|undefined} existing
95
+ * @param {string[]} nextUris
96
+ * @returns {{ uris: string[], added: boolean }}
97
+ */
98
+ function mergeRedirectUriList(existing, nextUris) {
99
+ let uris = Array.isArray(existing) ? [...existing] : [];
100
+ let added = false;
101
+ for (const nextUri of nextUris) {
102
+ const merged = mergeRedirectUris(uris, nextUri);
103
+ uris = merged.uris;
104
+ added = added || merged.added;
105
+ }
106
+ return { uris, added };
107
+ }
108
+
109
+ /**
110
+ * @param {string[]|undefined} values
111
+ * @returns {string[]}
112
+ */
113
+ function normalizeCallbackUrls(values, contract) {
114
+ const unique = [];
115
+ for (const value of Array.isArray(values) ? values : []) {
116
+ const uri = assertHttpsRedirectUri(value, contract);
117
+ if (!unique.includes(uri)) {
118
+ unique.push(uri);
119
+ }
120
+ }
121
+ if (unique.length === 0) {
122
+ throw createChatGptCallbackError('Callback URL is required.', 'callback', contract);
123
+ }
124
+ return unique;
125
+ }
126
+
127
+ /**
128
+ * @param {Object} response
129
+ * @returns {Object|Array}
130
+ */
131
+ function unwrapApiBody(response) {
132
+ if (response && response.data && response.data.data !== undefined && response.data.data !== null) {
133
+ return response.data.data;
134
+ }
135
+ if (response && response.data !== undefined) {
136
+ return response.data;
137
+ }
138
+ return response || {};
139
+ }
140
+
141
+ /**
142
+ * @param {Object} response
143
+ * @returns {Array}
144
+ */
145
+ function unwrapIntegrationClientList(response) {
146
+ const body = unwrapApiBody(response);
147
+ if (Array.isArray(body)) {
148
+ return body;
149
+ }
150
+ if (Array.isArray(body.data)) {
151
+ return body.data;
152
+ }
153
+ if (Array.isArray(body.items)) {
154
+ return body.items;
155
+ }
156
+ return [];
157
+ }
158
+
159
+ /**
160
+ * @param {Object} response
161
+ * @returns {Object}
162
+ */
163
+ function unwrapIntegrationClientRecord(response) {
164
+ const body = unwrapApiBody(response);
165
+ return body.integrationClient || body;
166
+ }
167
+
168
+ /**
169
+ * @param {Object} ctx
170
+ * @param {Object} [contract]
171
+ * @returns {string}
172
+ */
173
+ function chatgptProfileUrl(ctx, contract) {
174
+ const resolved = resolveCallbackContract(contract);
175
+ const base = ctx && typeof ctx.controllerUrl === 'string' ? ctx.controllerUrl.replace(/\/$/, '') : '';
176
+ if (!base) {
177
+ return resolved.oauth2ProfilePath;
178
+ }
179
+ return `${base}${resolved.oauth2ProfilePath}`;
180
+ }
181
+
182
+ /**
183
+ * @param {Object} ctx
184
+ * @param {Object} [contract]
185
+ * @returns {string}
186
+ */
187
+ function formatMissingChatGptClientError(ctx, contract) {
188
+ const resolved = resolveCallbackContract(contract);
189
+ return [
190
+ `Integration client "${resolved.integrationClientKey}" was not found.`,
191
+ `Open the ${resolved.vendorLabel} OAuth2 profile (${resolved.oauth2ProfileId}): ${chatgptProfileUrl(ctx, resolved)}`,
192
+ `Enable the ${resolved.vendorLabel} profile, then retry callback registration.`
193
+ ].join(' ');
194
+ }
195
+
196
+ /**
197
+ * @param {Error|Object} errorOrResponse
198
+ * @returns {boolean}
199
+ */
200
+ function isForbiddenClientError(errorOrResponse) {
201
+ const status = errorOrResponse && errorOrResponse.status;
202
+ if (status === 403) {
203
+ return true;
204
+ }
205
+ const message = errorOrResponse && errorOrResponse.message
206
+ ? String(errorOrResponse.message)
207
+ : '';
208
+ return /\b403\b/.test(message) || /forbidden|not allowed|integration-client:update/i.test(message);
209
+ }
210
+
211
+ /**
212
+ * @param {Object} ctx
213
+ * @param {Object} [deps]
214
+ * @returns {Promise<Object>}
215
+ */
216
+ async function findChatGptIntegrationClient(ctx, deps = {}) {
217
+ const contract = resolveCallbackContract(deps.contract);
218
+ const listFn = deps.listIntegrationClients || listIntegrationClients;
219
+ const getFn = deps.getIntegrationClient || getIntegrationClient;
220
+ const response = await listFn(ctx.controllerUrl, ctx.authConfig, { pageSize: 100 });
221
+ if (response && response.success === false) {
222
+ throw createChatGptCallbackError(
223
+ response.formattedError || response.error || 'Failed to list integration clients.',
224
+ response.status === 403 ? 'permissions' : 'callback',
225
+ contract
226
+ );
227
+ }
228
+ const match = unwrapIntegrationClientList(response).find(
229
+ (row) => row && row.key === contract.integrationClientKey
230
+ );
231
+ if (!match || !match.id) {
232
+ throw createChatGptCallbackError(formatMissingChatGptClientError(ctx, contract), 'callback', contract);
233
+ }
234
+ const detail = await getFn(ctx.controllerUrl, ctx.authConfig, match.id);
235
+ const record = unwrapIntegrationClientRecord(detail);
236
+ return { ...match, ...record, id: match.id };
237
+ }
238
+
239
+ /**
240
+ * @param {Object} params
241
+ * @param {Object} client
242
+ * @param {string[]} uris
243
+ * @returns {Promise<Object|null>}
244
+ */
245
+ async function applyRedirectUriUpdate(params, client, uris) {
246
+ const contract = resolveCallbackContract(params.contract);
247
+ const updateFn = params.updateRedirectUris || updateIntegrationClientRedirectUris;
248
+ try {
249
+ const response = await updateFn(params.ctx.controllerUrl, params.ctx.authConfig, client.id, {
250
+ redirectUris: uris
251
+ });
252
+ if (response && response.success === false && response.status === 403) {
253
+ return { ok: false, fallback: true, profileUrl: chatgptProfileUrl(params.ctx, contract) };
254
+ }
255
+ if (response && response.success === false) {
256
+ throw createChatGptCallbackError(
257
+ response.formattedError || response.error || 'Failed to update redirect URIs.',
258
+ 'callback',
259
+ contract
260
+ );
261
+ }
262
+ } catch (error) {
263
+ if (isForbiddenClientError(error)) {
264
+ return { ok: false, fallback: true, profileUrl: chatgptProfileUrl(params.ctx, contract) };
265
+ }
266
+ throw error;
267
+ }
268
+ return null;
269
+ }
270
+
271
+ /**
272
+ * Idempotent HTTPS callback registration. Never rotates secrets.
273
+ * @param {Object} params
274
+ * @returns {Promise<{ ok: boolean, added?: boolean, fallback?: boolean, profileUrl?: string, redirectUris?: string[] }>}
275
+ */
276
+ async function registerChatGptCallbackUrls(params) {
277
+ const contract = resolveCallbackContract(params.contract);
278
+ const callbackUrls = normalizeCallbackUrls(params.callbackUrls, contract);
279
+ let client;
280
+ try {
281
+ client = await findChatGptIntegrationClient(params.ctx, { ...params, contract });
282
+ } catch (error) {
283
+ if (isForbiddenClientError(error)) {
284
+ return { ok: false, fallback: true, profileUrl: chatgptProfileUrl(params.ctx, contract) };
285
+ }
286
+ throw error;
287
+ }
288
+ const merged = mergeRedirectUriList(client.redirectUris, callbackUrls);
289
+ const forbidden = await applyRedirectUriUpdate(params, client, merged.uris);
290
+ if (forbidden) {
291
+ return forbidden;
292
+ }
293
+ return { ok: true, added: merged.added, redirectUris: merged.uris };
294
+ }
295
+
296
+ /**
297
+ * @param {Object} params
298
+ * @returns {Promise<Object>}
299
+ */
300
+ async function registerChatGptCallbackUrl(params) {
301
+ return registerChatGptCallbackUrls({
302
+ ...params,
303
+ callbackUrls: [params.callbackUrl]
304
+ });
305
+ }
306
+
307
+ module.exports = {
308
+ assertHttpsRedirectUri,
309
+ mergeRedirectUris,
310
+ unwrapIntegrationClientList,
311
+ findChatGptIntegrationClient,
312
+ formatMissingChatGptClientError,
313
+ registerChatGptCallbackUrl,
314
+ registerChatGptCallbackUrls,
315
+ chatgptProfileUrl,
316
+ resolveCallbackContract,
317
+ CHATGPT_CALLBACK_CONTRACT
318
+ };