@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
@@ -0,0 +1,323 @@
1
+ /**
2
+ * Official Slack CLI installer (Mac/Linux install.sh only).
3
+ *
4
+ * @fileoverview Do not apt/snap install the Slack desktop app
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const fs = require('fs');
10
+ const https = require('https');
11
+ const os = require('os');
12
+ const path = require('path');
13
+ const { promisify } = require('util');
14
+ const { spawn } = require('child_process');
15
+ const execFileDefault = promisify(require('child_process').execFile);
16
+ const chalk = require('chalk');
17
+ const ora = require('ora');
18
+ const logger = require('../../utils/logger');
19
+ const { formatSuccessLine } = require('../../utils/cli-layout-chalk');
20
+ const { SLACK_CLI_INSTALL_SCRIPT_URL } = require('./constants');
21
+ const { slackProvisioningError } = require('./errors');
22
+ const { withGoTrustedCa } = require('./go-ca-bundle');
23
+ const { withCurlOnPath } = require('./with-curl-on-path');
24
+
25
+ const MAX_SCRIPT_BYTES = 512 * 1024;
26
+ const MAX_REDIRECTS = 5;
27
+ const SLACK_CLI_FALLBACK_HOME = 'aifabrix-slack-home';
28
+
29
+ /**
30
+ * @param {string} dir
31
+ * @returns {boolean}
32
+ */
33
+ function isRootHomeDir(dir) {
34
+ const resolved = path.resolve(dir);
35
+ return resolved === path.parse(resolved).root;
36
+ }
37
+
38
+ /**
39
+ * @param {unknown} value
40
+ * @returns {string}
41
+ */
42
+ function usableHomeDir(value) {
43
+ const trimmed = typeof value === 'string' ? value.trim() : '';
44
+ if (!trimmed || isRootHomeDir(trimmed)) {
45
+ return '';
46
+ }
47
+ return path.resolve(trimmed);
48
+ }
49
+
50
+ /**
51
+ * HOME=/ in builder-api cannot mkdir ~/.slack. Use a writable temp home instead.
52
+ * @param {NodeJS.ProcessEnv} [env]
53
+ * @returns {string}
54
+ */
55
+ function slackCliHomeDir(env = process.env) {
56
+ const ownHome = Boolean(env && Object.prototype.hasOwnProperty.call(env, 'HOME'));
57
+ const home = ownHome
58
+ ? usableHomeDir(env.HOME)
59
+ : usableHomeDir(env && env.HOME) || usableHomeDir(process.env.HOME);
60
+ if (home) {
61
+ return home;
62
+ }
63
+ const fallback = path.join(os.tmpdir(), SLACK_CLI_FALLBACK_HOME);
64
+ fs.mkdirSync(fallback, { recursive: true });
65
+ return fallback;
66
+ }
67
+
68
+ /**
69
+ * @returns {boolean}
70
+ */
71
+ function isSlackCliInstallerSupported() {
72
+ return process.platform === 'linux' || process.platform === 'darwin';
73
+ }
74
+
75
+ /**
76
+ * @param {NodeJS.ProcessEnv} [env]
77
+ * @returns {string}
78
+ */
79
+ function slackCliLocalBinDir(env = process.env) {
80
+ return path.join(slackCliHomeDir(env), '.local', 'bin');
81
+ }
82
+
83
+ /**
84
+ * @param {NodeJS.ProcessEnv} [env]
85
+ * @returns {NodeJS.ProcessEnv}
86
+ */
87
+ function withSlackCliPath(env = process.env) {
88
+ const home = slackCliHomeDir(env);
89
+ const dir = slackCliLocalBinDir({ ...env, HOME: home });
90
+ const current = env.PATH || '';
91
+ const parts = current.split(path.delimiter).filter(Boolean);
92
+ const nextPath = parts.includes(dir) ? current : `${dir}${path.delimiter}${current}`;
93
+ return withGoTrustedCa({ ...env, HOME: home, PATH: nextPath }, home);
94
+ }
95
+
96
+ /**
97
+ * @param {Object} [deps]
98
+ * @returns {void}
99
+ */
100
+ function applySlackCliPath(deps = {}) {
101
+ const next = withSlackCliPath(deps.env || process.env);
102
+ deps.env = next;
103
+ process.env.PATH = next.PATH;
104
+ if (next.SSL_CERT_FILE) {
105
+ process.env.SSL_CERT_FILE = next.SSL_CERT_FILE;
106
+ }
107
+ if (!usableHomeDir(process.env.HOME)) {
108
+ process.env.HOME = next.HOME;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * @param {string} url
114
+ * @param {import('http').IncomingMessage} res
115
+ * @param {number} redirects
116
+ * @param {(value: string) => void} resolve
117
+ * @param {(reason: Error) => void} reject
118
+ * @returns {boolean}
119
+ */
120
+ function followHttpsRedirect(url, res, redirects, resolve, reject) {
121
+ const status = res.statusCode || 0;
122
+ if (!(status >= 300 && status < 400 && res.headers.location)) {
123
+ return false;
124
+ }
125
+ res.resume();
126
+ if (redirects >= MAX_REDIRECTS) {
127
+ reject(new Error('Too many redirects downloading Slack CLI installer.'));
128
+ return true;
129
+ }
130
+ const nextUrl = new URL(res.headers.location, url).toString();
131
+ if (!nextUrl.startsWith('https://')) {
132
+ reject(new Error('Slack CLI installer redirect was not HTTPS.'));
133
+ return true;
134
+ }
135
+ fetchHttpsText(nextUrl, redirects + 1).then(resolve, reject);
136
+ return true;
137
+ }
138
+
139
+ /**
140
+ * @param {import('http').IncomingMessage} res
141
+ * @param {(value: string) => void} resolve
142
+ * @param {(reason: Error) => void} reject
143
+ * @returns {void}
144
+ */
145
+ function readInstallScriptBody(res, resolve, reject) {
146
+ const chunks = [];
147
+ let size = 0;
148
+ res.on('data', (chunk) => {
149
+ size += chunk.length;
150
+ if (size > MAX_SCRIPT_BYTES) {
151
+ res.destroy(new Error('Slack CLI installer script is larger than expected.'));
152
+ return;
153
+ }
154
+ chunks.push(chunk);
155
+ });
156
+ res.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
157
+ res.on('error', reject);
158
+ }
159
+
160
+ /**
161
+ * @param {string} url
162
+ * @param {number} [redirects]
163
+ * @returns {Promise<string>}
164
+ */
165
+ function fetchHttpsText(url, redirects = 0) {
166
+ return new Promise((resolve, reject) => {
167
+ const req = https.get(url, { timeout: 30000 }, (res) => {
168
+ if (followHttpsRedirect(url, res, redirects, resolve, reject)) {
169
+ return;
170
+ }
171
+ if ((res.statusCode || 0) !== 200) {
172
+ res.resume();
173
+ reject(new Error(`Slack CLI installer download failed (${res.statusCode || 0}).`));
174
+ return;
175
+ }
176
+ readInstallScriptBody(res, resolve, reject);
177
+ });
178
+ req.on('error', reject);
179
+ });
180
+ }
181
+
182
+ /**
183
+ * Prefer child stderr over Node's "Command failed: bash /tmp/…" wrapper.
184
+ * @param {unknown} error
185
+ * @returns {string}
186
+ */
187
+ function slackCliInstallFailureMessage(error) {
188
+ const stderr = error && error.stderr ? String(error.stderr).trim() : '';
189
+ const stdout = error && error.stdout ? String(error.stdout).trim() : '';
190
+ const raw = error && error.message ? String(error.message) : '';
191
+ const fromChild = stderr || stdout;
192
+ if (fromChild) {
193
+ const lines = fromChild.split('\n').map((line) => line.trim()).filter(Boolean);
194
+ return lines[lines.length - 1] || fromChild;
195
+ }
196
+ const stripped = raw.replace(/^Command failed: bash \S+\s*/i, '').trim();
197
+ return stripped || 'Slack CLI install failed.';
198
+ }
199
+
200
+ function assertInstallScript(script) {
201
+ const text = String(script || '').trim();
202
+ if (!text.startsWith('#!')) {
203
+ throw slackProvisioningError('Slack CLI installer script is not a shell script.', 'SLACK_CLI');
204
+ }
205
+ }
206
+
207
+ /**
208
+ * @param {Object} [deps]
209
+ * @returns {Promise<void>}
210
+ */
211
+ async function runInstallScript(deps = {}) {
212
+ const fetchFn = deps.fetchInstallScript || fetchHttpsText;
213
+ const script = await fetchFn(SLACK_CLI_INSTALL_SCRIPT_URL);
214
+ assertInstallScript(script);
215
+ const scriptPath = path.join(os.tmpdir(), `aifabrix-slack-cli-install-${process.pid}.sh`);
216
+ fs.writeFileSync(scriptPath, script, { mode: 0o700 });
217
+ const execFileImpl = deps.execFile || execFileDefault;
218
+ const env = await withCurlOnPath(withSlackCliPath(deps.env || process.env));
219
+ try {
220
+ await execFileImpl('bash', [scriptPath], {
221
+ env,
222
+ timeout: deps.timeout || 180000,
223
+ maxBuffer: 2 * 1024 * 1024
224
+ });
225
+ } catch (error) {
226
+ throw slackProvisioningError(slackCliInstallFailureMessage(error), 'SLACK_CLI');
227
+ } finally {
228
+ try {
229
+ fs.unlinkSync(scriptPath);
230
+ } catch {
231
+ // ignore
232
+ }
233
+ }
234
+ }
235
+
236
+ /**
237
+ * @param {string} text
238
+ * @returns {import('ora').Ora|null}
239
+ */
240
+ function startInstallSpinner(text) {
241
+ if (!process.stdout || !process.stdout.isTTY) {
242
+ logger.log(chalk.white(text));
243
+ return null;
244
+ }
245
+ return ora({ text: chalk.white(text), spinner: 'dots' }).start();
246
+ }
247
+
248
+ /**
249
+ * Official Slack install.sh. Never apt/snap `slack` (desktop app).
250
+ * @param {Object} [deps]
251
+ * @returns {Promise<void>}
252
+ */
253
+ async function installOfficialSlackCli(deps = {}) {
254
+ if (typeof deps.installCli === 'function') {
255
+ await deps.installCli();
256
+ applySlackCliPath(deps);
257
+ return;
258
+ }
259
+ if (!isSlackCliInstallerSupported()) {
260
+ throw slackProvisioningError(
261
+ 'Automatic Slack CLI install is available on Linux and macOS only.',
262
+ 'SLACK_CLI_MISSING'
263
+ );
264
+ }
265
+ const spinner = startInstallSpinner('Installing Slack CLI…');
266
+ try {
267
+ await runInstallScript(deps);
268
+ applySlackCliPath(deps);
269
+ if (spinner) {
270
+ spinner.stop();
271
+ }
272
+ logger.log(formatSuccessLine('Slack CLI installed'));
273
+ } catch (error) {
274
+ if (spinner) {
275
+ spinner.stop();
276
+ }
277
+ throw slackProvisioningError(
278
+ error && error.message ? error.message : 'Slack CLI install failed.',
279
+ 'SLACK_CLI'
280
+ );
281
+ }
282
+ }
283
+
284
+ /**
285
+ * @param {Object} [deps]
286
+ * @returns {Promise<void>}
287
+ */
288
+ async function runSlackLogin(deps = {}) {
289
+ if (typeof deps.slackLogin === 'function') {
290
+ await deps.slackLogin();
291
+ return;
292
+ }
293
+ const bin = typeof deps.slackBinary === 'string' && deps.slackBinary.trim()
294
+ ? deps.slackBinary.trim()
295
+ : 'slack';
296
+ await new Promise((resolve, reject) => {
297
+ const child = spawn(bin, ['login'], {
298
+ stdio: 'inherit',
299
+ env: withSlackCliPath(deps.env || process.env)
300
+ });
301
+ child.on('error', reject);
302
+ child.on('exit', (code) => {
303
+ if (code === 0) {
304
+ resolve();
305
+ return;
306
+ }
307
+ reject(slackProvisioningError(`slack login exited ${code}.`, 'SLACK_CLI'));
308
+ });
309
+ });
310
+ }
311
+
312
+ module.exports = {
313
+ isSlackCliInstallerSupported,
314
+ slackCliHomeDir,
315
+ slackCliLocalBinDir,
316
+ withSlackCliPath,
317
+ applySlackCliPath,
318
+ fetchHttpsText,
319
+ assertInstallScript,
320
+ slackCliInstallFailureMessage,
321
+ installOfficialSlackCli,
322
+ runSlackLogin
323
+ };
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Local Slack manifest checks — generated artifact is SSOT.
3
+ *
4
+ * @fileoverview Do not rebuild Slack fields; validate the generated JSON
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { slackProvisioningError } = require('./errors');
10
+ const { SLACK_APP_LONG_DESCRIPTION_MIN_LENGTH } = require('../channel-description');
11
+
12
+ /**
13
+ * @param {string} content
14
+ * @returns {Object}
15
+ */
16
+ function parseGeneratedManifest(content) {
17
+ if (typeof content !== 'string' || !content.trim()) {
18
+ throw slackProvisioningError('Generated Slack manifest is empty.', 'MANIFEST');
19
+ }
20
+ try {
21
+ const parsed = JSON.parse(content);
22
+ if (!parsed || typeof parsed !== 'object') {
23
+ throw new Error('not an object');
24
+ }
25
+ return parsed;
26
+ } catch {
27
+ throw slackProvisioningError('Generated Slack manifest is not valid JSON.', 'MANIFEST');
28
+ }
29
+ }
30
+
31
+ /**
32
+ * @param {Object} [display]
33
+ * @returns {boolean}
34
+ */
35
+ function hasDisplayParts(display) {
36
+ const name = display && display.name;
37
+ const longDescription = String((display && display.long_description) || '').trim();
38
+ return Boolean(name && longDescription.length >= SLACK_APP_LONG_DESCRIPTION_MIN_LENGTH);
39
+ }
40
+
41
+ /**
42
+ * @param {Object} [features]
43
+ * @returns {boolean}
44
+ */
45
+ function hasFeatureParts(features) {
46
+ const commands = features && features.slash_commands;
47
+ return Boolean(features && features.bot_user && Array.isArray(commands) && commands.length > 0);
48
+ }
49
+
50
+ /**
51
+ * @param {Object} [settings]
52
+ * @returns {boolean}
53
+ */
54
+ function hasSettingsParts(settings) {
55
+ return Boolean(settings && settings.event_subscriptions && settings.interactivity);
56
+ }
57
+
58
+ /**
59
+ * @param {Object} manifest
60
+ * @returns {boolean}
61
+ */
62
+ function hasRequiredManifestParts(manifest) {
63
+ return Boolean(
64
+ hasDisplayParts(manifest && manifest.display_information) &&
65
+ hasFeatureParts(manifest && manifest.features) &&
66
+ hasSettingsParts(manifest && manifest.settings)
67
+ );
68
+ }
69
+
70
+ /**
71
+ * @param {Object} manifest
72
+ * @returns {void}
73
+ */
74
+ function assertLocalManifest(manifest) {
75
+ if (!hasRequiredManifestParts(manifest)) {
76
+ throw slackProvisioningError(
77
+ 'Generated Slack manifest is missing required display, long_description (min 175 characters), bot, events, interactivity, or slash commands.',
78
+ 'MANIFEST'
79
+ );
80
+ }
81
+ if (manifest.settings && manifest.settings.socket_mode_enabled === true) {
82
+ throw slackProvisioningError('Slack Socket Mode must stay disabled for this channel.', 'MANIFEST');
83
+ }
84
+ }
85
+
86
+ module.exports = {
87
+ parseGeneratedManifest,
88
+ assertLocalManifest
89
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Keep Slack OAuth fields Slack will not let us turn off.
3
+ *
4
+ * @fileoverview Token rotation cannot be disabled after it was enabled.
5
+ * Never copy remote OAuth redirect URLs — Slack HMAC does not use a callback.
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ /**
11
+ * @param {Object} manifest
12
+ * @param {Object|null} remote
13
+ * @returns {Object}
14
+ */
15
+ function preserveSlackOAuthLocks(manifest, remote) {
16
+ if (!manifest || typeof manifest !== 'object') {
17
+ return manifest;
18
+ }
19
+ const next = {
20
+ ...manifest,
21
+ settings: { ...(manifest.settings || {}) },
22
+ oauth_config: { ...(manifest.oauth_config || {}) }
23
+ };
24
+ if (remote && remote.settings && remote.settings.token_rotation_enabled === true) {
25
+ next.settings.token_rotation_enabled = true;
26
+ }
27
+ next.oauth_config.redirect_urls = [];
28
+ return next;
29
+ }
30
+
31
+ module.exports = {
32
+ preserveSlackOAuthLocks
33
+ };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Offer to install Slack CLI and sign in during channel add.
3
+ *
4
+ * @fileoverview Interactive TTY only — CI / JSON keep remaining steps
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const inquirer = require('inquirer');
10
+ const { isChannelAddInteractive } = require('../channel-add-wizard');
11
+ const { shouldSkipLiveSlackCli, resolveSlackBinary } = require('./cli');
12
+ const {
13
+ isSlackCliInstallerSupported,
14
+ installOfficialSlackCli,
15
+ runSlackLogin
16
+ } = require('./install-cli');
17
+ const { slackProvisioningError } = require('./errors');
18
+
19
+ /**
20
+ * @param {Object} [deps]
21
+ * @returns {boolean}
22
+ */
23
+ function shouldSkipCliOffer(deps = {}) {
24
+ if (typeof deps.confirm === 'function') {
25
+ return false;
26
+ }
27
+ if (typeof deps.slackApi === 'function') {
28
+ return true;
29
+ }
30
+ return shouldSkipLiveSlackCli(deps);
31
+ }
32
+
33
+ /**
34
+ * @param {Object} [options]
35
+ * @param {Object} [deps]
36
+ * @returns {boolean}
37
+ */
38
+ function canOfferSlackCliInstall(options = {}, deps = {}) {
39
+ if (typeof deps.confirm === 'function') {
40
+ return true;
41
+ }
42
+ if (!isSlackCliInstallerSupported()) {
43
+ return false;
44
+ }
45
+ return isChannelAddInteractive(options);
46
+ }
47
+
48
+ /**
49
+ * @param {string} message
50
+ * @param {Object} [deps]
51
+ * @returns {Promise<boolean>}
52
+ */
53
+ async function confirmYesNo(message, deps = {}) {
54
+ if (typeof deps.confirm === 'function') {
55
+ return deps.confirm(message);
56
+ }
57
+ const answers = await inquirer.prompt([
58
+ {
59
+ type: 'confirm',
60
+ name: 'ok',
61
+ message,
62
+ default: true
63
+ }
64
+ ]);
65
+ return answers.ok === true;
66
+ }
67
+
68
+ /**
69
+ * @param {Object} [deps]
70
+ * @returns {Promise<void>}
71
+ */
72
+ async function offerInstallAndLogin(deps = {}) {
73
+ const accepted = await confirmYesNo('Install Slack CLI now?', deps);
74
+ if (!accepted) {
75
+ throw slackProvisioningError('Slack CLI install declined.', 'SLACK_CLI_MISSING');
76
+ }
77
+ await installOfficialSlackCli(deps);
78
+ const signIn = await confirmYesNo('Sign in to Slack now?', deps);
79
+ if (signIn) {
80
+ await runSlackLogin(deps);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Prompt + install Slack CLI when missing, then continue channel add.
86
+ * @param {Object} [options]
87
+ * @param {Object} [deps]
88
+ * @returns {Promise<void>}
89
+ */
90
+ async function ensureSlackCliReady(options = {}, deps = {}) {
91
+ if (shouldSkipCliOffer(deps)) {
92
+ return;
93
+ }
94
+ const existing = await resolveSlackBinary(deps);
95
+ if (existing) {
96
+ return;
97
+ }
98
+ if (!canOfferSlackCliInstall(options, deps)) {
99
+ throw slackProvisioningError(
100
+ 'Slack CLI (`slack`) is not installed or not on PATH.',
101
+ 'SLACK_CLI_MISSING'
102
+ );
103
+ }
104
+ await offerInstallAndLogin(deps);
105
+ }
106
+
107
+ module.exports = {
108
+ shouldSkipCliOffer,
109
+ canOfferSlackCliInstall,
110
+ confirmYesNo,
111
+ offerInstallAndLogin,
112
+ ensureSlackCliReady
113
+ };
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Slack workspace member list + email-join helpers for channel onboard.
3
+ *
4
+ * @fileoverview Skip bots; never print emails or Slack user ids on TTY
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { listUsers } = require('../../api/users.api');
10
+ const { extractUserListItems } = require('../../identity/resolve-user-id');
11
+ const { runSlackApi } = require('./cli');
12
+ const { slackProvisioningError } = require('./errors');
13
+
14
+ /**
15
+ * @param {Object} member
16
+ * @returns {'skip'|'unmatched'|'candidate'}
17
+ */
18
+ function classifySlackMember(member) {
19
+ if (!member || member.deleted === true) {
20
+ return 'skip';
21
+ }
22
+ if (member.is_bot === true || member.is_app_user === true || member.id === 'USLACKBOT') {
23
+ return 'skip';
24
+ }
25
+ if (typeof member.id !== 'string' || !member.id.trim()) {
26
+ return 'skip';
27
+ }
28
+ const email = member.profile && String(member.profile.email || '').trim();
29
+ if (!email || !email.includes('@')) {
30
+ return 'unmatched';
31
+ }
32
+ return 'candidate';
33
+ }
34
+
35
+ /**
36
+ * @param {Object} [deps]
37
+ * @returns {Promise<Object[]>}
38
+ */
39
+ async function listSlackWorkspaceMembers(deps = {}) {
40
+ const members = [];
41
+ let cursor = '';
42
+ for (let page = 0; page < 50; page += 1) {
43
+ const payload = { limit: 200 };
44
+ if (cursor) {
45
+ payload.cursor = cursor;
46
+ }
47
+ const body = await runSlackApi('users.list', payload, deps);
48
+ if (!body || body.ok !== true) {
49
+ throw slackProvisioningError('Slack users.list failed.', 'SLACK_API');
50
+ }
51
+ members.push(...(Array.isArray(body.members) ? body.members : []));
52
+ cursor = (body.response_metadata && body.response_metadata.next_cursor) || '';
53
+ if (!cursor) {
54
+ break;
55
+ }
56
+ }
57
+ return members;
58
+ }
59
+
60
+ /**
61
+ * @async
62
+ * @param {string} controllerUrl
63
+ * @param {Object} authConfig
64
+ * @param {string} email
65
+ * @returns {Promise<Object|null>}
66
+ */
67
+ async function lookupUniqueUserByEmail(controllerUrl, authConfig, email) {
68
+ const value = String(email || '').trim();
69
+ if (!value) {
70
+ return null;
71
+ }
72
+ const listed = await listUsers(controllerUrl, authConfig, {
73
+ filter: `email:eq:${value}`,
74
+ pageSize: 2,
75
+ page: 1
76
+ });
77
+ if (!listed || listed.success === false) {
78
+ return null;
79
+ }
80
+ const lower = value.toLowerCase();
81
+ const matches = extractUserListItems(listed).filter(
82
+ (user) => String(user.email || '').toLowerCase() === lower && user.id
83
+ );
84
+ return matches.length === 1 ? matches[0] : null;
85
+ }
86
+
87
+ /**
88
+ * @async
89
+ * @param {Object[]} members
90
+ * @param {{ controllerUrl: string, authConfig: Object }} ctx
91
+ * @param {Object} [deps]
92
+ * @returns {Promise<{ items: Object[], unmatched: number }>}
93
+ */
94
+ async function matchSlackMembersToUsers(members, ctx, deps = {}) {
95
+ const lookup = deps.lookupUniqueUserByEmail || lookupUniqueUserByEmail;
96
+ const items = [];
97
+ let unmatched = 0;
98
+ for (const member of members || []) {
99
+ const kind = classifySlackMember(member);
100
+ if (kind === 'skip') {
101
+ continue;
102
+ }
103
+ if (kind !== 'candidate') {
104
+ unmatched += 1;
105
+ continue;
106
+ }
107
+ const user = await lookup(ctx.controllerUrl, ctx.authConfig, member.profile.email.trim());
108
+ if (user && user.id) {
109
+ items.push({ externalUserId: member.id, platformUserId: user.id });
110
+ } else {
111
+ unmatched += 1;
112
+ }
113
+ }
114
+ return { items, unmatched };
115
+ }
116
+
117
+ module.exports = {
118
+ classifySlackMember,
119
+ listSlackWorkspaceMembers,
120
+ lookupUniqueUserByEmail,
121
+ matchSlackMembersToUsers
122
+ };