@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,123 @@
1
+ /**
2
+ * Read Azure subscription / resource group and Entra tenant from Miso Controller.
3
+ *
4
+ * @fileoverview Same controller sources as Copilot Entra SSO — not operator CLI flags
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { getEnvironment } = require('../../api/environments.api');
10
+ const { getKeycloakEntraIdentityProvider } = require('../../api/keycloak.api');
11
+ const { trimFlag } = require('./teams-bot-args');
12
+
13
+ /**
14
+ * @param {unknown} response
15
+ * @returns {Object|null}
16
+ */
17
+ function unwrapEnvironment(response) {
18
+ if (!response || typeof response !== 'object') {
19
+ return null;
20
+ }
21
+ const first = response.data !== undefined ? response.data : response;
22
+ if (first && first.configuration) {
23
+ return first;
24
+ }
25
+ if (first && first.data && first.data.configuration) {
26
+ return first.data;
27
+ }
28
+ return first && typeof first === 'object' ? first : null;
29
+ }
30
+
31
+ /**
32
+ * Azure IDs live on environment.configuration (JSON). Do not use Environment.subscriptionId —
33
+ * that field is the Miso Subscription foreign key.
34
+ * @param {Object|null} env
35
+ * @returns {{ subscription: string, resourceGroup: string }}
36
+ */
37
+ function extractAzureFromEnvironment(env) {
38
+ const cfg = (env && env.configuration) || {};
39
+ return {
40
+ subscription: trimFlag(cfg.subscriptionId),
41
+ resourceGroup: trimFlag(cfg.resourceGroupName || cfg.resourceGroup)
42
+ };
43
+ }
44
+
45
+ /**
46
+ * @param {Object|null} idp
47
+ * @returns {string}
48
+ */
49
+ function extractTenantFromIdp(idp) {
50
+ return trimFlag(idp && idp.tenantId);
51
+ }
52
+
53
+ /**
54
+ * @param {{ subscription?: string, resourceGroup?: string }|null} current
55
+ * @param {{ subscription?: string, resourceGroup?: string }} next
56
+ * @returns {{ subscription: string, resourceGroup: string }}
57
+ */
58
+ function mergeAzure(current, next) {
59
+ return {
60
+ subscription: trimFlag(current && current.subscription) || trimFlag(next.subscription),
61
+ resourceGroup: trimFlag(current && current.resourceGroup) || trimFlag(next.resourceGroup)
62
+ };
63
+ }
64
+
65
+ /**
66
+ * @param {Object} ctx
67
+ * @param {Object} [deps]
68
+ * @returns {Promise<{ azure: { subscription: string, resourceGroup: string }, tenantId: string }>}
69
+ */
70
+ async function loadTeamsBotControllerContext(ctx, deps = {}) {
71
+ let azure = mergeAzure(deps.azure, { subscription: '', resourceGroup: '' });
72
+ let tenantId = trimFlag(deps.expectedTenantId);
73
+ if (!ctx || !ctx.controllerUrl) {
74
+ return { azure, tenantId };
75
+ }
76
+ azure = await fillAzureFromEnvironment(ctx, azure, deps);
77
+ if (!tenantId) {
78
+ tenantId = await loadEntraTenantId(ctx, deps);
79
+ }
80
+ return { azure, tenantId };
81
+ }
82
+
83
+ /**
84
+ * @param {Object} ctx
85
+ * @param {{ subscription: string, resourceGroup: string }} azure
86
+ * @param {Object} deps
87
+ * @returns {Promise<{ subscription: string, resourceGroup: string }>}
88
+ */
89
+ async function fillAzureFromEnvironment(ctx, azure, deps) {
90
+ if (azure.subscription && azure.resourceGroup) {
91
+ return azure;
92
+ }
93
+ const getEnv = deps.getEnvironment || getEnvironment;
94
+ try {
95
+ const env = unwrapEnvironment(
96
+ await getEnv(ctx.controllerUrl, ctx.environment || 'dev', ctx.authConfig)
97
+ );
98
+ return mergeAzure(azure, extractAzureFromEnvironment(env));
99
+ } catch {
100
+ return azure;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * @param {Object} ctx
106
+ * @param {Object} deps
107
+ * @returns {Promise<string>}
108
+ */
109
+ async function loadEntraTenantId(ctx, deps) {
110
+ const getIdp = deps.getKeycloakEntraIdentityProvider || getKeycloakEntraIdentityProvider;
111
+ try {
112
+ return extractTenantFromIdp(await getIdp(ctx.controllerUrl, ctx.authConfig));
113
+ } catch {
114
+ return '';
115
+ }
116
+ }
117
+
118
+ module.exports = {
119
+ unwrapEnvironment,
120
+ extractAzureFromEnvironment,
121
+ extractTenantFromIdp,
122
+ loadTeamsBotControllerContext
123
+ };
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Parse Teams Developer CLI credentials without logging secrets.
3
+ *
4
+ * @fileoverview CLIENT_ID / CLIENT_SECRET / TENANT_ID from --env or --json only
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { microsoftProvisioningError } = require('./errors');
10
+ const { extractTenantId, assertExpectedTenant } = require('./auth');
11
+
12
+ /**
13
+ * @param {string} [content]
14
+ * @returns {Object.<string, string>}
15
+ */
16
+ function parseEnvMap(content) {
17
+ const map = {};
18
+ for (const line of String(content || '').split(/\r?\n/)) {
19
+ const trimmed = line.trim();
20
+ if (!trimmed || trimmed.startsWith('#')) {
21
+ continue;
22
+ }
23
+ const eq = trimmed.indexOf('=');
24
+ if (eq > 0) {
25
+ map[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim();
26
+ }
27
+ }
28
+ return map;
29
+ }
30
+
31
+ /**
32
+ * @param {string} [stdout]
33
+ * @returns {Object|null}
34
+ */
35
+ function parseJsonCreateResult(stdout) {
36
+ const text = String(stdout || '').trim();
37
+ if (!text) {
38
+ return null;
39
+ }
40
+ try {
41
+ return JSON.parse(text);
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * @param {Object|null} result
49
+ * @param {Object.<string, string>} envMap
50
+ * @returns {Object}
51
+ */
52
+ function firstValue(...values) {
53
+ for (const value of values) {
54
+ if (typeof value === 'string' && value.trim()) {
55
+ return value.trim();
56
+ }
57
+ }
58
+ return undefined;
59
+ }
60
+
61
+ /**
62
+ * @param {Object|null} result
63
+ * @param {Object.<string, string>} envMap
64
+ * @returns {Object}
65
+ */
66
+ function credentialsFromCreate(result, envMap) {
67
+ const creds = (result && result.credentials) || {};
68
+ const map = envMap || {};
69
+ return {
70
+ clientId: firstValue(map.CLIENT_ID, map.clientId, creds.CLIENT_ID, creds.clientId),
71
+ clientSecret: firstValue(map.CLIENT_SECRET, map.clientSecret, creds.CLIENT_SECRET),
72
+ tenantId: extractTenantId(
73
+ firstValue(map.TENANT_ID, map.tenantId, creds.TENANT_ID, result && result.tenantId) || ''
74
+ ),
75
+ teamsAppId: result && (result.teamsAppId || result.appId),
76
+ botId: result && result.botId,
77
+ botLocation: result && result.botLocation,
78
+ installLink: result && result.installLink
79
+ };
80
+ }
81
+
82
+ /**
83
+ * @param {Object} parsed
84
+ * @returns {void}
85
+ */
86
+ function assertAzureManaged(parsed) {
87
+ const loc = String((parsed && parsed.botLocation) || '').toLowerCase();
88
+ if (!loc) {
89
+ return;
90
+ }
91
+ if (loc.includes('teams-managed') || loc === 'teams') {
92
+ throw microsoftProvisioningError(
93
+ 'Teams-managed bot is not supported. Azure-managed is required for OAuth/SSO.',
94
+ 'TEAMS_MANAGED'
95
+ );
96
+ }
97
+ if (!loc.includes('azure')) {
98
+ throw microsoftProvisioningError(
99
+ `Unexpected bot location "${parsed.botLocation}". Azure-managed is required.`,
100
+ 'TEAMS_MANAGED'
101
+ );
102
+ }
103
+ }
104
+
105
+ /**
106
+ * @param {Object} parsed
107
+ * @param {string} [expectedTenant]
108
+ * @returns {void}
109
+ */
110
+ function assertParsedCredentials(parsed, expectedTenant) {
111
+ if (!parsed || !parsed.clientId || !parsed.clientSecret) {
112
+ throw microsoftProvisioningError(
113
+ 'Teams CLI did not return CLIENT_ID and CLIENT_SECRET.',
114
+ 'CREDENTIALS'
115
+ );
116
+ }
117
+ if (expectedTenant && !parsed.tenantId) {
118
+ throw microsoftProvisioningError(
119
+ 'Could not determine the Microsoft tenant from Teams CLI output.',
120
+ 'TENANT'
121
+ );
122
+ }
123
+ if (expectedTenant) {
124
+ assertExpectedTenant(expectedTenant, parsed.tenantId);
125
+ }
126
+ }
127
+
128
+ module.exports = {
129
+ parseEnvMap,
130
+ parseJsonCreateResult,
131
+ credentialsFromCreate,
132
+ assertAzureManaged,
133
+ assertParsedCredentials
134
+ };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Persist non-secret Teams Bot correlation IDs only.
3
+ *
4
+ * @fileoverview Azure-managed app ids; never CLIENT_SECRET
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const path = require('path');
10
+ const fs = require('../../internal/fs-real-sync');
11
+ const {
12
+ TEAMS_BOT_STATE_FILE,
13
+ TEAMS_BOT_STATE_KIND,
14
+ SECRET_KEY_RE
15
+ } = require('./constants');
16
+ const { microsoftProvisioningError } = require('./errors');
17
+
18
+ /**
19
+ * @param {string} projectRoot
20
+ * @returns {string}
21
+ */
22
+ function teamsBotStatePath(projectRoot) {
23
+ const root = projectRoot || process.cwd();
24
+ return path.join(root, 'dist', 'channels', 'teams-bot', TEAMS_BOT_STATE_FILE);
25
+ }
26
+
27
+ /**
28
+ * @param {Object} record
29
+ * @returns {void}
30
+ */
31
+ function assertNoSecrets(record) {
32
+ const keys = record && typeof record === 'object' ? Object.keys(record) : [];
33
+ const hit = keys.find((key) => SECRET_KEY_RE.test(key));
34
+ if (hit) {
35
+ throw microsoftProvisioningError(
36
+ `Refusing to persist Microsoft secret field "${hit}".`,
37
+ 'SECRET'
38
+ );
39
+ }
40
+ }
41
+
42
+ /**
43
+ * @param {string} projectRoot
44
+ * @returns {Object|null}
45
+ */
46
+ function readTeamsBotState(projectRoot) {
47
+ const filePath = teamsBotStatePath(projectRoot);
48
+ if (!fs.existsSync(filePath)) {
49
+ return null;
50
+ }
51
+ try {
52
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
53
+ if (!parsed || parsed.kind !== TEAMS_BOT_STATE_KIND) {
54
+ return null;
55
+ }
56
+ assertNoSecrets(parsed);
57
+ return parsed;
58
+ } catch {
59
+ return null;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * @param {Object|null} current
65
+ * @param {Object} next
66
+ * @returns {boolean}
67
+ */
68
+ function isReusableTeamsBot(current, next) {
69
+ if (!current || current.azureManaged !== true) {
70
+ return false;
71
+ }
72
+ if (typeof current.teamsAppId !== 'string' || !current.teamsAppId.trim()) {
73
+ return false;
74
+ }
75
+ if (current.environment !== next.environment) {
76
+ return false;
77
+ }
78
+ if (current.tenantId && next.tenantId) {
79
+ return String(current.tenantId).toLowerCase() === String(next.tenantId).toLowerCase();
80
+ }
81
+ return true;
82
+ }
83
+
84
+ /**
85
+ * @param {string} projectRoot
86
+ * @param {Object} state
87
+ * @returns {void}
88
+ */
89
+ function writeTeamsBotState(projectRoot, state) {
90
+ const record = {
91
+ kind: TEAMS_BOT_STATE_KIND,
92
+ environment: state.environment,
93
+ tenantId: state.tenantId || null,
94
+ endpoint: state.endpoint || null,
95
+ teamsAppId: state.teamsAppId,
96
+ azureManaged: true,
97
+ subscriptionId: state.subscriptionId || null,
98
+ resourceGroup: state.resourceGroup || null,
99
+ stored: state.stored === true
100
+ };
101
+ assertNoSecrets(record);
102
+ const filePath = teamsBotStatePath(projectRoot);
103
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
104
+ fs.writeFileSync(filePath, `${JSON.stringify(record, null, 2)}\n`, 'utf8');
105
+ }
106
+
107
+ module.exports = {
108
+ teamsBotStatePath,
109
+ readTeamsBotState,
110
+ writeTeamsBotState,
111
+ isReusableTeamsBot,
112
+ assertNoSecrets
113
+ };
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Provision an Azure-managed Teams Bot around the existing Runtime webhook.
3
+ *
4
+ * @fileoverview teams app create --azure; reuse per environment × tenant
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const os = require('os');
10
+ const path = require('path');
11
+ const fs = require('../../internal/fs-real-sync');
12
+ const { TEAMS_OPEN_URL, TEAMS_POLICY_REMAINING, TEAMS_CLI_MISSING_REMAINING } = require('./constants');
13
+ const { wrapTeamsCliError } = require('./errors');
14
+ const { resolveTeamsBinary, runTeams, isTeamsCliLoggedIn } = require('./teams-cli');
15
+ const {
16
+ assertHttpsEndpoint,
17
+ buildAppCreateArgs,
18
+ requireAzureFlags,
19
+ resolveAzureFlags,
20
+ trimFlag
21
+ } = require('./teams-bot-args');
22
+ const {
23
+ assertAzureManaged,
24
+ assertParsedCredentials,
25
+ credentialsFromCreate,
26
+ parseEnvMap,
27
+ parseJsonCreateResult
28
+ } = require('./teams-bot-parse');
29
+ const { isReusableTeamsBot, readTeamsBotState, writeTeamsBotState } = require('./teams-bot-persist');
30
+ const { storeTeamsBotSecrets } = require('./teams-bot-secrets');
31
+ const { loadTeamsBotControllerContext } = require('./teams-bot-controller');
32
+
33
+ /**
34
+ * @returns {Object}
35
+ */
36
+ function skipClosedMissingCli() {
37
+ return {
38
+ connected: false,
39
+ skipped: true,
40
+ remaining: TEAMS_CLI_MISSING_REMAINING
41
+ };
42
+ }
43
+
44
+ /**
45
+ * @returns {Object}
46
+ */
47
+ function skipClosedNotLoggedIn() {
48
+ return {
49
+ connected: false,
50
+ skipped: true,
51
+ remaining: ['Sign in with Microsoft, then continue after sign-in']
52
+ };
53
+ }
54
+
55
+ /**
56
+ * @param {Object} existing
57
+ * @returns {Object}
58
+ */
59
+ function reusedConnected(existing) {
60
+ return {
61
+ connected: true,
62
+ reused: true,
63
+ remaining: [TEAMS_POLICY_REMAINING],
64
+ openUrl: TEAMS_OPEN_URL,
65
+ teamsAppId: existing.teamsAppId,
66
+ azureManaged: true
67
+ };
68
+ }
69
+
70
+ /**
71
+ * @param {Object} params
72
+ * @param {Object} [deps]
73
+ * @returns {Object}
74
+ */
75
+ function provisionContext(params) {
76
+ const messagesUrl = params.runtimeValues && params.runtimeValues.messagesUrl;
77
+ assertHttpsEndpoint(messagesUrl);
78
+ const options = params.options || {};
79
+ return {
80
+ messagesUrl,
81
+ azure: resolveAzureFlags(options),
82
+ environment:
83
+ params.environment || (params.runtimeValues && params.runtimeValues.environment) || 'dev',
84
+ expectedTenant: trimFlag(options.expectedTenantId),
85
+ appName: (params.runtimeValues && params.runtimeValues.appName) || 'AI Fabrix',
86
+ projectRoot: params.projectRoot,
87
+ ctx: params.ctx
88
+ };
89
+ }
90
+
91
+ /**
92
+ * @param {string} envPath
93
+ * @returns {Object.<string, string>}
94
+ */
95
+ function readEnvMap(envPath) {
96
+ if (!envPath || !fs.existsSync(envPath)) {
97
+ return {};
98
+ }
99
+ return parseEnvMap(fs.readFileSync(envPath, 'utf8'));
100
+ }
101
+
102
+ /**
103
+ * @param {string} envPath
104
+ * @returns {void}
105
+ */
106
+ function unlinkQuiet(envPath) {
107
+ try {
108
+ if (envPath && fs.existsSync(envPath)) {
109
+ fs.unlinkSync(envPath);
110
+ }
111
+ } catch {
112
+ // best-effort: temp credentials file must not remain in dist/
113
+ }
114
+ }
115
+
116
+ /**
117
+ * @param {Object} params
118
+ * @param {Object} [deps]
119
+ * @returns {Promise<Object>}
120
+ */
121
+ async function createAzureManagedBot(params, deps = {}) {
122
+ const envPath =
123
+ params.envPath || path.join(os.tmpdir(), `aifabrix-teams-bot-${process.pid}.env`);
124
+ const args = buildAppCreateArgs({ ...params, envPath });
125
+ try {
126
+ const result = await runTeams(params.bin, args, { timeout: 180000 }, deps);
127
+ const parsed = credentialsFromCreate(parseJsonCreateResult(result.stdout), readEnvMap(envPath));
128
+ assertAzureManaged(parsed);
129
+ assertParsedCredentials(parsed, params.expectedTenantId);
130
+ return parsed;
131
+ } catch (error) {
132
+ const code = error && error.code;
133
+ if (code === 'WRONG_TENANT' || code === 'TEAMS_MANAGED' || code === 'CREDENTIALS' || code === 'TENANT' || code === 'SCAFFOLD') {
134
+ throw error;
135
+ }
136
+ throw wrapTeamsCliError(error);
137
+ } finally {
138
+ unlinkQuiet(envPath);
139
+ }
140
+ }
141
+
142
+ /**
143
+ * @param {Object} context
144
+ * @param {Object} created
145
+ * @param {Object} [deps]
146
+ * @returns {Promise<void>}
147
+ */
148
+ async function persistCreatedBot(context, created, deps = {}) {
149
+ await storeTeamsBotSecrets(context.ctx, created, deps);
150
+ const write = deps.writeState || writeTeamsBotState;
151
+ write(context.projectRoot, {
152
+ environment: context.environment,
153
+ tenantId: created.tenantId || null,
154
+ endpoint: context.messagesUrl,
155
+ teamsAppId: created.teamsAppId || created.clientId,
156
+ azureManaged: true,
157
+ subscriptionId: context.azure.subscription,
158
+ resourceGroup: context.azure.resourceGroup,
159
+ stored: true
160
+ });
161
+ }
162
+
163
+ /**
164
+ * @param {Object} created
165
+ * @returns {Object}
166
+ */
167
+ function connectedResult(created) {
168
+ return {
169
+ connected: true,
170
+ remaining: [TEAMS_POLICY_REMAINING],
171
+ openUrl: created.installLink || TEAMS_OPEN_URL,
172
+ teamsAppId: created.teamsAppId || created.clientId,
173
+ azureManaged: true
174
+ };
175
+ }
176
+
177
+ /**
178
+ * @param {Object} params
179
+ * @param {Object} [deps]
180
+ * @returns {Promise<Object>}
181
+ */
182
+ async function applyControllerContext(context, deps = {}) {
183
+ const controller = await loadTeamsBotControllerContext(context.ctx, {
184
+ ...deps,
185
+ azure: context.azure,
186
+ expectedTenantId: context.expectedTenant
187
+ });
188
+ context.azure = controller.azure;
189
+ context.expectedTenant = controller.tenantId || context.expectedTenant;
190
+ }
191
+
192
+ /**
193
+ * @param {Object} params
194
+ * @param {Object} [deps]
195
+ * @returns {Promise<Object>}
196
+ */
197
+ async function provisionTeamsBot(params, deps = {}) {
198
+ const context = provisionContext(params);
199
+ const read = deps.readState || readTeamsBotState;
200
+ const existing = read(context.projectRoot);
201
+ if (isReusableTeamsBot(existing, { environment: context.environment, tenantId: context.expectedTenant })) {
202
+ return reusedConnected(existing);
203
+ }
204
+ const bin = await resolveTeamsBinary(deps);
205
+ if (!bin) {
206
+ return skipClosedMissingCli();
207
+ }
208
+ if (!(await isTeamsCliLoggedIn({ ...deps, teamsBinary: bin }))) {
209
+ return skipClosedNotLoggedIn();
210
+ }
211
+ await applyControllerContext(context, deps);
212
+ requireAzureFlags(context.azure);
213
+ const created = await createAzureManagedBot(
214
+ {
215
+ bin,
216
+ name: context.appName,
217
+ endpoint: context.messagesUrl,
218
+ subscription: context.azure.subscription,
219
+ resourceGroup: context.azure.resourceGroup,
220
+ expectedTenantId: context.expectedTenant
221
+ },
222
+ deps
223
+ );
224
+ await persistCreatedBot(context, created, deps);
225
+ return connectedResult(created);
226
+ }
227
+
228
+ module.exports = {
229
+ TEAMS_CLI_MISSING_REMAINING,
230
+ skipClosedMissingCli,
231
+ provisionTeamsBot,
232
+ buildAppCreateArgs,
233
+ isReusableTeamsBot
234
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Store Teams Bot App ID + secret via existing dataplane credential APIs.
3
+ *
4
+ * @fileoverview Never log or persist secret values in dist /
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { storeCredentialSecrets } = require('../../api/credential.api');
10
+ const { KV_TEAMS_BOT_APP_ID, KV_TEAMS_BOT_APP_SECRET } = require('./constants');
11
+
12
+ /**
13
+ * @param {string} [value]
14
+ * @returns {boolean}
15
+ */
16
+ function hasSecretValue(value) {
17
+ return typeof value === 'string' && Boolean(value.trim());
18
+ }
19
+
20
+ /**
21
+ * @param {Object} ctx
22
+ * @param {Object} parsed
23
+ * @param {Object} [deps]
24
+ * @returns {Promise<{ stored: boolean }>}
25
+ */
26
+ async function storeTeamsBotSecrets(ctx, parsed, deps = {}) {
27
+ const items = [];
28
+ if (hasSecretValue(parsed && parsed.clientId)) {
29
+ items.push({ key: KV_TEAMS_BOT_APP_ID, value: String(parsed.clientId).trim() });
30
+ }
31
+ if (hasSecretValue(parsed && parsed.clientSecret)) {
32
+ items.push({ key: KV_TEAMS_BOT_APP_SECRET, value: String(parsed.clientSecret).trim() });
33
+ }
34
+ if (items.length === 0) {
35
+ return { stored: false };
36
+ }
37
+ const store = deps.storeCredentialSecrets || storeCredentialSecrets;
38
+ await store(ctx.dataplaneUrl, ctx.authConfig, items);
39
+ return { stored: true };
40
+ }
41
+
42
+ module.exports = {
43
+ hasSecretValue,
44
+ storeTeamsBotSecrets
45
+ };