@mastra/factory 0.0.0-esbuild-bundle-worker-20260807173433

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 (329) hide show
  1. package/CHANGELOG.md +1008 -0
  2. package/LICENSE.md +30 -0
  3. package/README.md +27 -0
  4. package/dist/auth.d.ts +183 -0
  5. package/dist/auth.d.ts.map +1 -0
  6. package/dist/auth.js +507 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/capabilities/connection.d.ts +9 -0
  9. package/dist/capabilities/connection.d.ts.map +1 -0
  10. package/dist/capabilities/connection.js +1 -0
  11. package/dist/capabilities/intake.d.ts +141 -0
  12. package/dist/capabilities/intake.d.ts.map +1 -0
  13. package/dist/capabilities/intake.js +1 -0
  14. package/dist/capabilities/version-control.d.ts +247 -0
  15. package/dist/capabilities/version-control.d.ts.map +1 -0
  16. package/dist/capabilities/version-control.js +1 -0
  17. package/dist/factory.d.ts +159 -0
  18. package/dist/factory.d.ts.map +1 -0
  19. package/dist/factory.js +491 -0
  20. package/dist/factory.js.map +1 -0
  21. package/dist/index.d.ts +14 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +7 -0
  24. package/dist/integrations/base.d.ts +254 -0
  25. package/dist/integrations/base.d.ts.map +1 -0
  26. package/dist/integrations/base.js +1 -0
  27. package/dist/integrations/github/config.d.ts +60 -0
  28. package/dist/integrations/github/config.d.ts.map +1 -0
  29. package/dist/integrations/github/config.js +42 -0
  30. package/dist/integrations/github/config.js.map +1 -0
  31. package/dist/integrations/github/integration.d.ts +220 -0
  32. package/dist/integrations/github/integration.d.ts.map +1 -0
  33. package/dist/integrations/github/integration.js +1162 -0
  34. package/dist/integrations/github/integration.js.map +1 -0
  35. package/dist/integrations/github/issue-triage.d.ts +27 -0
  36. package/dist/integrations/github/issue-triage.d.ts.map +1 -0
  37. package/dist/integrations/github/issue-triage.js +82 -0
  38. package/dist/integrations/github/issue-triage.js.map +1 -0
  39. package/dist/integrations/github/pat.d.ts +38 -0
  40. package/dist/integrations/github/pat.d.ts.map +1 -0
  41. package/dist/integrations/github/pat.js +59 -0
  42. package/dist/integrations/github/pat.js.map +1 -0
  43. package/dist/integrations/github/provenance.d.ts +27 -0
  44. package/dist/integrations/github/provenance.d.ts.map +1 -0
  45. package/dist/integrations/github/provenance.js +75 -0
  46. package/dist/integrations/github/provenance.js.map +1 -0
  47. package/dist/integrations/github/reconcile-worker.d.ts +41 -0
  48. package/dist/integrations/github/reconcile-worker.d.ts.map +1 -0
  49. package/dist/integrations/github/reconcile-worker.js +125 -0
  50. package/dist/integrations/github/reconcile-worker.js.map +1 -0
  51. package/dist/integrations/github/routes.d.ts +69 -0
  52. package/dist/integrations/github/routes.d.ts.map +1 -0
  53. package/dist/integrations/github/routes.js +1335 -0
  54. package/dist/integrations/github/routes.js.map +1 -0
  55. package/dist/integrations/github/rules.d.ts +105 -0
  56. package/dist/integrations/github/rules.d.ts.map +1 -0
  57. package/dist/integrations/github/rules.js +551 -0
  58. package/dist/integrations/github/rules.js.map +1 -0
  59. package/dist/integrations/github/sandbox-release.d.ts +53 -0
  60. package/dist/integrations/github/sandbox-release.d.ts.map +1 -0
  61. package/dist/integrations/github/sandbox-release.js +115 -0
  62. package/dist/integrations/github/sandbox-release.js.map +1 -0
  63. package/dist/integrations/github/sandbox.d.ts +281 -0
  64. package/dist/integrations/github/sandbox.d.ts.map +1 -0
  65. package/dist/integrations/github/sandbox.js +547 -0
  66. package/dist/integrations/github/sandbox.js.map +1 -0
  67. package/dist/integrations/github/session-subscriptions.d.ts +26 -0
  68. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -0
  69. package/dist/integrations/github/session-subscriptions.js +192 -0
  70. package/dist/integrations/github/session-subscriptions.js.map +1 -0
  71. package/dist/integrations/github/subscriptions.d.ts +54 -0
  72. package/dist/integrations/github/subscriptions.d.ts.map +1 -0
  73. package/dist/integrations/github/subscriptions.js +62 -0
  74. package/dist/integrations/github/subscriptions.js.map +1 -0
  75. package/dist/integrations/github/token-refresh.d.ts +12 -0
  76. package/dist/integrations/github/token-refresh.d.ts.map +1 -0
  77. package/dist/integrations/github/token-refresh.js +24 -0
  78. package/dist/integrations/github/token-refresh.js.map +1 -0
  79. package/dist/integrations/github/webhook.d.ts +107 -0
  80. package/dist/integrations/github/webhook.d.ts.map +1 -0
  81. package/dist/integrations/github/webhook.js +371 -0
  82. package/dist/integrations/github/webhook.js.map +1 -0
  83. package/dist/integrations/linear/agent-tools.d.ts +34 -0
  84. package/dist/integrations/linear/agent-tools.d.ts.map +1 -0
  85. package/dist/integrations/linear/agent-tools.js +85 -0
  86. package/dist/integrations/linear/agent-tools.js.map +1 -0
  87. package/dist/integrations/linear/integration.d.ts +235 -0
  88. package/dist/integrations/linear/integration.d.ts.map +1 -0
  89. package/dist/integrations/linear/integration.js +676 -0
  90. package/dist/integrations/linear/integration.js.map +1 -0
  91. package/dist/integrations/linear/routes.d.ts +61 -0
  92. package/dist/integrations/linear/routes.d.ts.map +1 -0
  93. package/dist/integrations/linear/routes.js +240 -0
  94. package/dist/integrations/linear/routes.js.map +1 -0
  95. package/dist/integrations/linear/rules.d.ts +44 -0
  96. package/dist/integrations/linear/rules.d.ts.map +1 -0
  97. package/dist/integrations/linear/rules.js +133 -0
  98. package/dist/integrations/linear/rules.js.map +1 -0
  99. package/dist/integrations/linear/storage.d.ts +65 -0
  100. package/dist/integrations/linear/storage.d.ts.map +1 -0
  101. package/dist/integrations/linear/storage.js +1 -0
  102. package/dist/integrations/platform/api-client.d.ts +26 -0
  103. package/dist/integrations/platform/api-client.d.ts.map +1 -0
  104. package/dist/integrations/platform/api-client.js +156 -0
  105. package/dist/integrations/platform/api-client.js.map +1 -0
  106. package/dist/integrations/platform/github/event-worker.d.ts +44 -0
  107. package/dist/integrations/platform/github/event-worker.d.ts.map +1 -0
  108. package/dist/integrations/platform/github/event-worker.js +349 -0
  109. package/dist/integrations/platform/github/event-worker.js.map +1 -0
  110. package/dist/integrations/platform/github/integration.d.ts +64 -0
  111. package/dist/integrations/platform/github/integration.d.ts.map +1 -0
  112. package/dist/integrations/platform/github/integration.js +1006 -0
  113. package/dist/integrations/platform/github/integration.js.map +1 -0
  114. package/dist/integrations/platform/linear/integration.d.ts +46 -0
  115. package/dist/integrations/platform/linear/integration.d.ts.map +1 -0
  116. package/dist/integrations/platform/linear/integration.js +483 -0
  117. package/dist/integrations/platform/linear/integration.js.map +1 -0
  118. package/dist/integrations/slack/connect-route.d.ts +46 -0
  119. package/dist/integrations/slack/connect-route.d.ts.map +1 -0
  120. package/dist/integrations/slack/connect-route.js +186 -0
  121. package/dist/integrations/slack/connect-route.js.map +1 -0
  122. package/dist/integrations/slack/integration.d.ts +62 -0
  123. package/dist/integrations/slack/integration.d.ts.map +1 -0
  124. package/dist/integrations/slack/integration.js +68 -0
  125. package/dist/integrations/slack/integration.js.map +1 -0
  126. package/dist/integrations/slack/slack.d.ts +200 -0
  127. package/dist/integrations/slack/slack.d.ts.map +1 -0
  128. package/dist/integrations/slack/slack.js +411 -0
  129. package/dist/integrations/slack/slack.js.map +1 -0
  130. package/dist/integrations/workos/integration.d.ts +39 -0
  131. package/dist/integrations/workos/integration.d.ts.map +1 -0
  132. package/dist/integrations/workos/integration.js +91 -0
  133. package/dist/integrations/workos/integration.js.map +1 -0
  134. package/dist/routes/config.d.ts +235 -0
  135. package/dist/routes/config.d.ts.map +1 -0
  136. package/dist/routes/config.js +972 -0
  137. package/dist/routes/config.js.map +1 -0
  138. package/dist/routes/custom-provider-source.d.ts +52 -0
  139. package/dist/routes/custom-provider-source.d.ts.map +1 -0
  140. package/dist/routes/custom-provider-source.js +124 -0
  141. package/dist/routes/custom-provider-source.js.map +1 -0
  142. package/dist/routes/fs.d.ts +151 -0
  143. package/dist/routes/fs.d.ts.map +1 -0
  144. package/dist/routes/fs.js +708 -0
  145. package/dist/routes/fs.js.map +1 -0
  146. package/dist/routes/intake.d.ts +23 -0
  147. package/dist/routes/intake.d.ts.map +1 -0
  148. package/dist/routes/intake.js +179 -0
  149. package/dist/routes/intake.js.map +1 -0
  150. package/dist/routes/oauth.d.ts +43 -0
  151. package/dist/routes/oauth.d.ts.map +1 -0
  152. package/dist/routes/oauth.js +463 -0
  153. package/dist/routes/oauth.js.map +1 -0
  154. package/dist/routes/projects.d.ts +19 -0
  155. package/dist/routes/projects.d.ts.map +1 -0
  156. package/dist/routes/projects.js +461 -0
  157. package/dist/routes/projects.js.map +1 -0
  158. package/dist/routes/provider-credentials.d.ts +81 -0
  159. package/dist/routes/provider-credentials.d.ts.map +1 -0
  160. package/dist/routes/provider-credentials.js +86 -0
  161. package/dist/routes/provider-credentials.js.map +1 -0
  162. package/dist/routes/route.d.ts +48 -0
  163. package/dist/routes/route.d.ts.map +1 -0
  164. package/dist/routes/route.js +15 -0
  165. package/dist/routes/route.js.map +1 -0
  166. package/dist/routes/skills.d.ts +30 -0
  167. package/dist/routes/skills.d.ts.map +1 -0
  168. package/dist/routes/skills.js +168 -0
  169. package/dist/routes/skills.js.map +1 -0
  170. package/dist/routes/surface.d.ts +107 -0
  171. package/dist/routes/surface.d.ts.map +1 -0
  172. package/dist/routes/surface.js +297 -0
  173. package/dist/routes/surface.js.map +1 -0
  174. package/dist/routes/tenant-credentials.d.ts +74 -0
  175. package/dist/routes/tenant-credentials.d.ts.map +1 -0
  176. package/dist/routes/tenant-credentials.js +156 -0
  177. package/dist/routes/tenant-credentials.js.map +1 -0
  178. package/dist/routes/work-items.d.ts +40 -0
  179. package/dist/routes/work-items.d.ts.map +1 -0
  180. package/dist/routes/work-items.js +695 -0
  181. package/dist/routes/work-items.js.map +1 -0
  182. package/dist/rules/binding-context.d.ts +5 -0
  183. package/dist/rules/binding-context.d.ts.map +1 -0
  184. package/dist/rules/binding-context.js +28 -0
  185. package/dist/rules/binding-context.js.map +1 -0
  186. package/dist/rules/defaults.d.ts +9 -0
  187. package/dist/rules/defaults.d.ts.map +1 -0
  188. package/dist/rules/defaults.js +322 -0
  189. package/dist/rules/defaults.js.map +1 -0
  190. package/dist/rules/dispatcher.d.ts +43 -0
  191. package/dist/rules/dispatcher.d.ts.map +1 -0
  192. package/dist/rules/dispatcher.js +547 -0
  193. package/dist/rules/dispatcher.js.map +1 -0
  194. package/dist/rules/index.d.ts +7 -0
  195. package/dist/rules/index.d.ts.map +1 -0
  196. package/dist/rules/index.js +5 -0
  197. package/dist/rules/processor.d.ts +54 -0
  198. package/dist/rules/processor.d.ts.map +1 -0
  199. package/dist/rules/processor.js +378 -0
  200. package/dist/rules/processor.js.map +1 -0
  201. package/dist/rules/resolve.d.ts +17 -0
  202. package/dist/rules/resolve.d.ts.map +1 -0
  203. package/dist/rules/resolve.js +30 -0
  204. package/dist/rules/resolve.js.map +1 -0
  205. package/dist/rules/start-coordinator.d.ts +60 -0
  206. package/dist/rules/start-coordinator.d.ts.map +1 -0
  207. package/dist/rules/start-coordinator.js +175 -0
  208. package/dist/rules/start-coordinator.js.map +1 -0
  209. package/dist/rules/tools.d.ts +10 -0
  210. package/dist/rules/tools.d.ts.map +1 -0
  211. package/dist/rules/tools.js +59 -0
  212. package/dist/rules/tools.js.map +1 -0
  213. package/dist/rules/transition-service.d.ts +49 -0
  214. package/dist/rules/transition-service.d.ts.map +1 -0
  215. package/dist/rules/transition-service.js +232 -0
  216. package/dist/rules/transition-service.js.map +1 -0
  217. package/dist/rules/types.d.ts +261 -0
  218. package/dist/rules/types.d.ts.map +1 -0
  219. package/dist/rules/types.js +43 -0
  220. package/dist/rules/types.js.map +1 -0
  221. package/dist/rules/validation.d.ts +11 -0
  222. package/dist/rules/validation.d.ts.map +1 -0
  223. package/dist/rules/validation.js +308 -0
  224. package/dist/rules/validation.js.map +1 -0
  225. package/dist/sandbox/fleet.d.ts +222 -0
  226. package/dist/sandbox/fleet.d.ts.map +1 -0
  227. package/dist/sandbox/fleet.js +322 -0
  228. package/dist/sandbox/fleet.js.map +1 -0
  229. package/dist/sandbox/reattach.d.ts +3 -0
  230. package/dist/sandbox/reattach.d.ts.map +1 -0
  231. package/dist/sandbox/reattach.js +16 -0
  232. package/dist/sandbox/reattach.js.map +1 -0
  233. package/dist/server-error.d.ts +12 -0
  234. package/dist/server-error.d.ts.map +1 -0
  235. package/dist/server-error.js +16 -0
  236. package/dist/server-error.js.map +1 -0
  237. package/dist/session/factory-session.d.ts +116 -0
  238. package/dist/session/factory-session.d.ts.map +1 -0
  239. package/dist/session/factory-session.js +162 -0
  240. package/dist/session/factory-session.js.map +1 -0
  241. package/dist/skills/service.d.ts +48 -0
  242. package/dist/skills/service.d.ts.map +1 -0
  243. package/dist/skills/service.js +40 -0
  244. package/dist/skills/service.js.map +1 -0
  245. package/dist/spa-static.d.ts +33 -0
  246. package/dist/spa-static.d.ts.map +1 -0
  247. package/dist/spa-static.js +104 -0
  248. package/dist/spa-static.js.map +1 -0
  249. package/dist/state-signing.d.ts +59 -0
  250. package/dist/state-signing.d.ts.map +1 -0
  251. package/dist/state-signing.js +83 -0
  252. package/dist/state-signing.js.map +1 -0
  253. package/dist/storage/domains/audit/agent-audit.d.ts +27 -0
  254. package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -0
  255. package/dist/storage/domains/audit/agent-audit.js +66 -0
  256. package/dist/storage/domains/audit/agent-audit.js.map +1 -0
  257. package/dist/storage/domains/audit/base.d.ts +140 -0
  258. package/dist/storage/domains/audit/base.d.ts.map +1 -0
  259. package/dist/storage/domains/audit/base.js +184 -0
  260. package/dist/storage/domains/audit/base.js.map +1 -0
  261. package/dist/storage/domains/audit/domain.d.ts +76 -0
  262. package/dist/storage/domains/audit/domain.d.ts.map +1 -0
  263. package/dist/storage/domains/audit/domain.js +269 -0
  264. package/dist/storage/domains/audit/domain.js.map +1 -0
  265. package/dist/storage/domains/channel-identity/base.d.ts +96 -0
  266. package/dist/storage/domains/channel-identity/base.d.ts.map +1 -0
  267. package/dist/storage/domains/channel-identity/base.js +149 -0
  268. package/dist/storage/domains/channel-identity/base.js.map +1 -0
  269. package/dist/storage/domains/credentials/base.d.ts +134 -0
  270. package/dist/storage/domains/credentials/base.d.ts.map +1 -0
  271. package/dist/storage/domains/credentials/base.js +273 -0
  272. package/dist/storage/domains/credentials/base.js.map +1 -0
  273. package/dist/storage/domains/custom-providers/base.d.ts +57 -0
  274. package/dist/storage/domains/custom-providers/base.d.ts.map +1 -0
  275. package/dist/storage/domains/custom-providers/base.js +145 -0
  276. package/dist/storage/domains/custom-providers/base.js.map +1 -0
  277. package/dist/storage/domains/intake/base.d.ts +38 -0
  278. package/dist/storage/domains/intake/base.d.ts.map +1 -0
  279. package/dist/storage/domains/intake/base.js +80 -0
  280. package/dist/storage/domains/intake/base.js.map +1 -0
  281. package/dist/storage/domains/integrations/base.d.ts +126 -0
  282. package/dist/storage/domains/integrations/base.d.ts.map +1 -0
  283. package/dist/storage/domains/integrations/base.js +317 -0
  284. package/dist/storage/domains/integrations/base.js.map +1 -0
  285. package/dist/storage/domains/memory-settings/base.d.ts +62 -0
  286. package/dist/storage/domains/memory-settings/base.d.ts.map +1 -0
  287. package/dist/storage/domains/memory-settings/base.js +126 -0
  288. package/dist/storage/domains/memory-settings/base.js.map +1 -0
  289. package/dist/storage/domains/model-packs/base.d.ts +49 -0
  290. package/dist/storage/domains/model-packs/base.d.ts.map +1 -0
  291. package/dist/storage/domains/model-packs/base.js +96 -0
  292. package/dist/storage/domains/model-packs/base.js.map +1 -0
  293. package/dist/storage/domains/projects/base.d.ts +58 -0
  294. package/dist/storage/domains/projects/base.d.ts.map +1 -0
  295. package/dist/storage/domains/projects/base.js +111 -0
  296. package/dist/storage/domains/projects/base.js.map +1 -0
  297. package/dist/storage/domains/queue-health/base.d.ts +52 -0
  298. package/dist/storage/domains/queue-health/base.d.ts.map +1 -0
  299. package/dist/storage/domains/queue-health/base.js +122 -0
  300. package/dist/storage/domains/queue-health/base.js.map +1 -0
  301. package/dist/storage/domains/source-control/base.d.ts +360 -0
  302. package/dist/storage/domains/source-control/base.d.ts.map +1 -0
  303. package/dist/storage/domains/source-control/base.js +914 -0
  304. package/dist/storage/domains/source-control/base.js.map +1 -0
  305. package/dist/storage/domains/source-control/inmemory.d.ts +175 -0
  306. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -0
  307. package/dist/storage/domains/source-control/inmemory.js +380 -0
  308. package/dist/storage/domains/source-control/inmemory.js.map +1 -0
  309. package/dist/storage/domains/work-items/base.d.ts +412 -0
  310. package/dist/storage/domains/work-items/base.d.ts.map +1 -0
  311. package/dist/storage/domains/work-items/base.js +1320 -0
  312. package/dist/storage/domains/work-items/base.js.map +1 -0
  313. package/dist/storage/domains/work-items/metrics.d.ts +101 -0
  314. package/dist/storage/domains/work-items/metrics.d.ts.map +1 -0
  315. package/dist/storage/domains/work-items/metrics.js +221 -0
  316. package/dist/storage/domains/work-items/metrics.js.map +1 -0
  317. package/dist/timing.d.ts +15 -0
  318. package/dist/timing.d.ts.map +1 -0
  319. package/dist/timing.js +34 -0
  320. package/dist/timing.js.map +1 -0
  321. package/dist/workspace.d.ts +24 -0
  322. package/dist/workspace.d.ts.map +1 -0
  323. package/dist/workspace.js +287 -0
  324. package/dist/workspace.js.map +1 -0
  325. package/factory-skills/configure-factory-rules/SKILL.md +67 -0
  326. package/factory-skills/factory-plan/SKILL.md +61 -0
  327. package/factory-skills/factory-review/SKILL.md +157 -0
  328. package/factory-skills/factory-triage/SKILL.md +99 -0
  329. package/package.json +83 -0
@@ -0,0 +1,186 @@
1
+ import { registerApiRoute } from "@mastra/core/server";
2
+ //#region src/integrations/slack/connect-route.ts
3
+ const SLACK_AUTHORIZE_URL = "https://slack.com/openid/connect/authorize";
4
+ const SLACK_TOKEN_URL = "https://slack.com/api/openid.connect.token";
5
+ const OIDC_CALLBACK_PATH = "/connect/slack/oidc/callback";
6
+ const SLACK_TOKEN_TIMEOUT_MS = 1e4;
7
+ /** Matches the signer's own `state` lifetime — past it, `verify` rejects anyway. */
8
+ const STATE_REPLAY_WINDOW_MS = 600 * 1e3;
9
+ /**
10
+ * Decode a JWT's payload WITHOUT signature verification. Safe here because the
11
+ * `id_token` arrives directly from Slack's token endpoint over TLS in a
12
+ * confidential-client code exchange — the transport authenticates the issuer,
13
+ * which is the standard OIDC allowance for this flow. `iss`/`aud`/`exp` are
14
+ * still checked by the caller.
15
+ */
16
+ function decodeJwtPayload(jwt) {
17
+ const payload = jwt.split(".")[1];
18
+ if (!payload) return null;
19
+ try {
20
+ return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+ /**
26
+ * Slack account-linking routes.
27
+ *
28
+ * A link is only ever written by the OIDC flow below, where Slack itself
29
+ * asserts the `(team, user)` pair in the id_token — so the web user has to
30
+ * actually control the Slack account they bind. `/connect/slack` is just the
31
+ * Slack-side entry point: it carries no identity and writes nothing, it only
32
+ * sends the visitor to Connections, where the flow starts.
33
+ */
34
+ function createSlackConnectRoutes(deps) {
35
+ const { auth, accountLinks, tenantStateSigner, oidc, projects } = deps;
36
+ const oidcEnabled = Boolean(oidc && tenantStateSigner);
37
+ const uiOrigin = oidc?.uiOrigin?.replace(/\/$/, "") ?? "";
38
+ const consumedNonces = /* @__PURE__ */ new Map();
39
+ const consumeNonce = (nonce) => {
40
+ const now = Date.now();
41
+ for (const [seen, expiresAt] of consumedNonces) if (expiresAt <= now) consumedNonces.delete(seen);
42
+ if (consumedNonces.has(nonce)) return false;
43
+ consumedNonces.set(nonce, now + STATE_REPLAY_WINDOW_MS);
44
+ return true;
45
+ };
46
+ const loose = (c) => c;
47
+ return [
48
+ registerApiRoute("/connect/slack", {
49
+ method: "GET",
50
+ requiresAuth: false,
51
+ handler: async (c) => c.redirect(`${uiOrigin}/settings/connections`)
52
+ }),
53
+ registerApiRoute("/connect/slack/oidc/start", {
54
+ method: "GET",
55
+ requiresAuth: false,
56
+ handler: async (c) => {
57
+ if (!oidcEnabled) return c.redirect(`${uiOrigin}/?slack=error`);
58
+ const factoryProjectId = c.req.query("factoryId")?.trim() || void 0;
59
+ const startPath = factoryProjectId ? `/connect/slack/oidc/start?factoryId=${encodeURIComponent(factoryProjectId)}` : "/connect/slack/oidc/start";
60
+ await auth.ensureUser(loose(c));
61
+ const tenant = auth.tenant(loose(c));
62
+ if (!tenant) return c.redirect(`/auth/login?returnTo=${encodeURIComponent(startPath)}`);
63
+ const params = new URLSearchParams({
64
+ response_type: "code",
65
+ scope: "openid profile",
66
+ client_id: oidc.clientId,
67
+ state: tenantStateSigner.sign(tenant.orgId ?? "", tenant.userId, { factoryProjectId }),
68
+ redirect_uri: `${oidc.redirectBaseUrl.replace(/\/$/, "")}${OIDC_CALLBACK_PATH}`
69
+ });
70
+ return c.redirect(`${SLACK_AUTHORIZE_URL}?${params.toString()}`);
71
+ }
72
+ }),
73
+ registerApiRoute(OIDC_CALLBACK_PATH, {
74
+ method: "GET",
75
+ requiresAuth: false,
76
+ handler: async (c) => {
77
+ if (!oidcEnabled) return c.redirect(`${uiOrigin}/?slack=error`);
78
+ const tenant = tenantStateSigner.verify(c.req.query("state"));
79
+ const code = c.req.query("code");
80
+ if (!tenant || !code) return c.redirect(`${uiOrigin}/?slack=error`);
81
+ if (!consumeNonce(tenant.nonce)) return c.redirect(`${uiOrigin}/?slack=error`);
82
+ const tokenRes = await fetch(SLACK_TOKEN_URL, {
83
+ method: "POST",
84
+ headers: { "content-type": "application/x-www-form-urlencoded" },
85
+ body: new URLSearchParams({
86
+ client_id: oidc.clientId,
87
+ client_secret: oidc.clientSecret,
88
+ code,
89
+ redirect_uri: `${oidc.redirectBaseUrl.replace(/\/$/, "")}${OIDC_CALLBACK_PATH}`
90
+ }),
91
+ signal: AbortSignal.timeout(SLACK_TOKEN_TIMEOUT_MS)
92
+ }).catch(() => null);
93
+ const token = await tokenRes?.json().catch(() => null);
94
+ if (!tokenRes?.ok || !token?.ok || typeof token.id_token !== "string") return c.redirect(`${uiOrigin}/?slack=error`);
95
+ const claims = decodeJwtPayload(token.id_token);
96
+ const teamId = claims?.["https://slack.com/team_id"];
97
+ const slackUserId = claims?.["https://slack.com/user_id"];
98
+ const teamName = claims?.["https://slack.com/team_name"];
99
+ const userName = claims?.name;
100
+ const expiresAt = typeof claims?.exp === "number" ? claims.exp * 1e3 : null;
101
+ if (!claims || claims.iss !== "https://slack.com" || claims.aud !== oidc.clientId || expiresAt === null || expiresAt <= Date.now() || typeof teamId !== "string" || typeof slackUserId !== "string") return c.redirect(`${uiOrigin}/?slack=error`);
102
+ await accountLinks.saveAccountLink({
103
+ platform: "slack",
104
+ externalTeamId: teamId,
105
+ externalUserId: slackUserId,
106
+ orgId: tenant.orgId || void 0,
107
+ userId: tenant.userId,
108
+ externalTeamName: typeof teamName === "string" ? teamName : void 0,
109
+ externalUserName: typeof userName === "string" ? userName : void 0
110
+ });
111
+ const successPath = tenant.factoryProjectId ? `/factories/${encodeURIComponent(tenant.factoryProjectId)}/settings/connections/slack` : "/settings/connections";
112
+ return c.redirect(`${uiOrigin}${successPath}?slack=connected`);
113
+ }
114
+ }),
115
+ registerApiRoute("/web/channel-accounts", {
116
+ method: "GET",
117
+ requiresAuth: false,
118
+ handler: async (c) => {
119
+ await auth.ensureUser(loose(c));
120
+ const tenant = auth.tenant(loose(c));
121
+ if (!tenant) return c.json({ error: "unauthorized" }, 401);
122
+ const accounts = (await accountLinks.listAccountLinksForUser(tenant.userId)).map((link) => ({
123
+ platform: link.platform,
124
+ externalTeamId: link.externalTeamId,
125
+ externalUserId: link.externalUserId,
126
+ externalTeamName: link.externalTeamName,
127
+ externalUserName: link.externalUserName,
128
+ defaultFactoryProjectId: link.defaultFactoryProjectId,
129
+ linkedAt: link.linkedAt.toISOString()
130
+ }));
131
+ return c.json({
132
+ accounts,
133
+ canConnect: oidcEnabled
134
+ });
135
+ }
136
+ }),
137
+ registerApiRoute("/web/channel-accounts", {
138
+ method: "DELETE",
139
+ requiresAuth: false,
140
+ handler: async (c) => {
141
+ await auth.ensureUser(loose(c));
142
+ const tenant = auth.tenant(loose(c));
143
+ if (!tenant) return c.json({ error: "unauthorized" }, 401);
144
+ const body = await c.req.json().catch(() => null);
145
+ if (!body?.platform || !body.externalTeamId || !body.externalUserId) return c.json({ error: "platform, externalTeamId and externalUserId are required" }, 400);
146
+ const deleted = await accountLinks.deleteAccountLinkForUser({
147
+ userId: tenant.userId,
148
+ platform: body.platform,
149
+ externalTeamId: body.externalTeamId,
150
+ externalUserId: body.externalUserId
151
+ });
152
+ return c.json({ deleted });
153
+ }
154
+ }),
155
+ registerApiRoute("/web/channel-accounts/default-factory", {
156
+ method: "PATCH",
157
+ requiresAuth: false,
158
+ handler: async (c) => {
159
+ await auth.ensureUser(loose(c));
160
+ const tenant = auth.tenant(loose(c));
161
+ if (!tenant) return c.json({ error: "unauthorized" }, 401);
162
+ const body = await c.req.json().catch(() => null);
163
+ if (!body?.platform || !body.externalTeamId || !body.externalUserId || body.factoryProjectId === void 0) return c.json({ error: "platform, externalTeamId, externalUserId and factoryProjectId (nullable) are required" }, 400);
164
+ if (body.factoryProjectId !== null) {
165
+ if (!projects) return c.json({ error: "factory routing is not configured" }, 400);
166
+ if (!await projects.get({
167
+ orgId: tenant.orgId ?? "",
168
+ id: body.factoryProjectId
169
+ })) return c.json({ error: "unknown factory" }, 400);
170
+ }
171
+ const updated = await accountLinks.setDefaultFactory({
172
+ userId: tenant.userId,
173
+ platform: body.platform,
174
+ externalTeamId: body.externalTeamId,
175
+ externalUserId: body.externalUserId,
176
+ factoryProjectId: body.factoryProjectId
177
+ });
178
+ return c.json({ updated });
179
+ }
180
+ })
181
+ ];
182
+ }
183
+ //#endregion
184
+ export { createSlackConnectRoutes };
185
+
186
+ //# sourceMappingURL=connect-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect-route.js","names":[],"sources":["../../../src/integrations/slack/connect-route.ts"],"sourcesContent":["import { registerApiRoute } from '@mastra/core/server';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { RouteAuth } from '../../routes/route.js';\nimport type { StateSigner } from '../../state-signing.js';\nimport type { ChannelIdentityStorage } from '../../storage/domains/channel-identity/base.js';\nimport type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';\n\n/**\n * Payload shape for the Connections list: the platform sender key +\n * link time, without the tenant ids (the caller IS the tenant).\n */\ninterface ConnectedChannelAccountPayload {\n platform: string;\n externalTeamId: string;\n externalUserId: string;\n /** Display names captured at link time (OIDC profile claims); ids fall back. */\n externalTeamName?: string;\n externalUserName?: string;\n /** Which Factory project this link's channel runs route to (unset = not picked yet). */\n defaultFactoryProjectId?: string;\n linkedAt: string;\n}\n\n/**\n * Config for the web-initiated \"Sign in with Slack\" (OIDC) connect flow. All\n * values come from the env-supplied Slack app. Absent → the OIDC routes\n * respond with an error redirect and the list endpoint reports\n * `canConnect: false` so the UI hides its Connect button.\n */\ninterface SlackOidcConfig {\n clientId: string;\n clientSecret: string;\n /**\n * Public HTTPS origin the OIDC `redirect_uri` is built from (Slack rejects\n * plain-http redirect URLs, so locally this is the tunnel origin). Must also\n * be registered as a redirect URL on the Slack app.\n */\n redirectBaseUrl: string;\n /** Browser-facing origin post-connect redirects land on (the SPA host). */\n uiOrigin?: string;\n}\n\nconst SLACK_AUTHORIZE_URL = 'https://slack.com/openid/connect/authorize';\nconst SLACK_TOKEN_URL = 'https://slack.com/api/openid.connect.token';\nconst OIDC_CALLBACK_PATH = '/connect/slack/oidc/callback';\nconst SLACK_TOKEN_TIMEOUT_MS = 10_000;\n/** Matches the signer's own `state` lifetime — past it, `verify` rejects anyway. */\nconst STATE_REPLAY_WINDOW_MS = 10 * 60 * 1000;\n\n/**\n * Decode a JWT's payload WITHOUT signature verification. Safe here because the\n * `id_token` arrives directly from Slack's token endpoint over TLS in a\n * confidential-client code exchange — the transport authenticates the issuer,\n * which is the standard OIDC allowance for this flow. `iss`/`aud`/`exp` are\n * still checked by the caller.\n */\nfunction decodeJwtPayload(jwt: string): Record<string, unknown> | null {\n const payload = jwt.split('.')[1];\n if (!payload) return null;\n try {\n return JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\n/**\n * Slack account-linking routes.\n *\n * A link is only ever written by the OIDC flow below, where Slack itself\n * asserts the `(team, user)` pair in the id_token — so the web user has to\n * actually control the Slack account they bind. `/connect/slack` is just the\n * Slack-side entry point: it carries no identity and writes nothing, it only\n * sends the visitor to Connections, where the flow starts.\n */\nexport function createSlackConnectRoutes(deps: {\n auth: RouteAuth;\n accountLinks: ChannelIdentityStorage;\n /** Signs the OIDC `state`, binding the round-trip to the initiating tenant. */\n tenantStateSigner?: StateSigner;\n oidc?: SlackOidcConfig;\n /**\n * Factory projects domain, for validating (and listing to) the per-link\n * default factory. Unset → the default-factory PATCH route rejects.\n */\n projects?: FactoryProjectsStorage;\n}): ApiRoute[] {\n const { auth, accountLinks, tenantStateSigner, oidc, projects } = deps;\n const oidcEnabled = Boolean(oidc && tenantStateSigner);\n const uiOrigin = oidc?.uiOrigin?.replace(/\\/$/, '') ?? '';\n\n // A signed `state` stays valid for its whole 10-minute window, so on its own\n // it authorizes the binding repeatedly: anyone who captures a callback URL\n // can re-run the exchange with their own fresh Slack `code` and bind their\n // Slack account to the tenant that started the flow. Burning the nonce makes\n // the state single-use. Scope note: this is per-process, so it does not stop\n // a replay routed to a different replica — narrowing that further needs\n // shared storage, which is out of proportion to a 10-minute window here.\n const consumedNonces = new Map<string, number>();\n const consumeNonce = (nonce: string): boolean => {\n const now = Date.now();\n for (const [seen, expiresAt] of consumedNonces) {\n if (expiresAt <= now) consumedNonces.delete(seen);\n }\n if (consumedNonces.has(nonce)) return false;\n consumedNonces.set(nonce, now + STATE_REPLAY_WINDOW_MS);\n return true;\n };\n\n // mc-web can resolve a different hono version than @mastra/factory, so the\n // registerApiRoute handler `Context` and the factory `RouteAuth` `Context`\n // are structurally incompatible (private `[GET_MATCH_RESULT]` symbol). Cast\n // through `unknown` at the seam — same `loose()` workaround the factory\n // routes use internally.\n type RouteAuthContext = Parameters<RouteAuth['ensureUser']>[0];\n const loose = (c: unknown): RouteAuthContext => c as RouteAuthContext;\n\n return [\n registerApiRoute('/connect/slack', {\n method: 'GET',\n // No auth: nothing here reads a tenant or writes a link. The SPA route\n // this lands on requires a session and bounces through login itself.\n requiresAuth: false,\n handler: async c => c.redirect(`${uiOrigin}/settings/connections`),\n }),\n // Web-initiated connect: \"Sign in with Slack\" (OIDC). The only route that\n // writes a link, because it is the only one that proves the signed-in web\n // user actually CONTROLS the Slack account: Slack itself asserts the\n // (team, user) pair in the id_token.\n registerApiRoute('/connect/slack/oidc/start', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n if (!oidcEnabled) return c.redirect(`${uiOrigin}/?slack=error`);\n\n const factoryProjectId = c.req.query('factoryId')?.trim() || undefined;\n const startPath = factoryProjectId\n ? `/connect/slack/oidc/start?factoryId=${encodeURIComponent(factoryProjectId)}`\n : '/connect/slack/oidc/start';\n\n await auth.ensureUser(loose(c));\n const tenant = auth.tenant(loose(c));\n if (!tenant) {\n return c.redirect(`/auth/login?returnTo=${encodeURIComponent(startPath)}`);\n }\n\n const params = new URLSearchParams({\n response_type: 'code',\n // `profile` adds display-name claims (user name, team name) to the\n // id_token so Connections can show names instead of ids.\n scope: 'openid profile',\n client_id: oidc!.clientId,\n // Personal accounts have no org; the signer requires a string, so an\n // empty org round-trips and is mapped back to undefined on save.\n state: tenantStateSigner!.sign(tenant.orgId ?? '', tenant.userId, { factoryProjectId }),\n redirect_uri: `${oidc!.redirectBaseUrl.replace(/\\/$/, '')}${OIDC_CALLBACK_PATH}`,\n });\n return c.redirect(`${SLACK_AUTHORIZE_URL}?${params.toString()}`);\n },\n }),\n // OIDC callback. Authenticates via the signed `state` (which carries the\n // initiating tenant) rather than the session cookie — the callback may\n // arrive on a different origin (the public tunnel) where the SPA's\n // host-scoped cookie is not sent.\n registerApiRoute(OIDC_CALLBACK_PATH, {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n if (!oidcEnabled) return c.redirect(`${uiOrigin}/?slack=error`);\n\n const tenant = tenantStateSigner!.verify(c.req.query('state'));\n const code = c.req.query('code');\n if (!tenant || !code) return c.redirect(`${uiOrigin}/?slack=error`);\n // Burn the state before spending the code, not after saving: a replay\n // must be refused even if the first attempt fails partway through.\n if (!consumeNonce(tenant.nonce)) return c.redirect(`${uiOrigin}/?slack=error`);\n\n // Bound the exchange: without a deadline a slow token endpoint parks the\n // request until the platform's own timeout, and this route is reachable\n // by anyone holding a signed state.\n const tokenRes = await fetch(SLACK_TOKEN_URL, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n client_id: oidc!.clientId,\n client_secret: oidc!.clientSecret,\n code,\n redirect_uri: `${oidc!.redirectBaseUrl.replace(/\\/$/, '')}${OIDC_CALLBACK_PATH}`,\n }),\n signal: AbortSignal.timeout(SLACK_TOKEN_TIMEOUT_MS),\n }).catch(() => null);\n const token = (await tokenRes?.json().catch(() => null)) as { ok?: boolean; id_token?: string } | null;\n if (!tokenRes?.ok || !token?.ok || typeof token.id_token !== 'string') {\n return c.redirect(`${uiOrigin}/?slack=error`);\n }\n\n const claims = decodeJwtPayload(token.id_token);\n const teamId = claims?.['https://slack.com/team_id'];\n const slackUserId = claims?.['https://slack.com/user_id'];\n // Display-only claims from the `profile` scope — absent claims just\n // mean the settings list falls back to ids.\n const teamName = claims?.['https://slack.com/team_name'];\n const userName = claims?.name;\n // `exp` is seconds since the epoch. A missing or past `exp` means the\n // assertion is not currently valid, so it cannot bind an account.\n const expiresAt = typeof claims?.exp === 'number' ? claims.exp * 1000 : null;\n if (\n !claims ||\n claims.iss !== 'https://slack.com' ||\n claims.aud !== oidc!.clientId ||\n expiresAt === null ||\n expiresAt <= Date.now() ||\n typeof teamId !== 'string' ||\n typeof slackUserId !== 'string'\n ) {\n return c.redirect(`${uiOrigin}/?slack=error`);\n }\n\n await accountLinks.saveAccountLink({\n platform: 'slack',\n externalTeamId: teamId,\n externalUserId: slackUserId,\n orgId: tenant.orgId || undefined,\n userId: tenant.userId,\n externalTeamName: typeof teamName === 'string' ? teamName : undefined,\n externalUserName: typeof userName === 'string' ? userName : undefined,\n });\n\n const successPath = tenant.factoryProjectId\n ? `/factories/${encodeURIComponent(tenant.factoryProjectId)}/settings/connections/slack`\n : '/settings/connections';\n return c.redirect(`${uiOrigin}${successPath}?slack=connected`);\n },\n }),\n // The caller's own linked channel accounts for Connections. Tenant-scoped:\n // you only ever see your own links.\n registerApiRoute('/web/channel-accounts', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n await auth.ensureUser(loose(c));\n const tenant = auth.tenant(loose(c));\n if (!tenant) return c.json({ error: 'unauthorized' }, 401);\n\n const links = await accountLinks.listAccountLinksForUser(tenant.userId);\n const accounts: ConnectedChannelAccountPayload[] = links.map(link => ({\n platform: link.platform,\n externalTeamId: link.externalTeamId,\n externalUserId: link.externalUserId,\n externalTeamName: link.externalTeamName,\n externalUserName: link.externalUserName,\n defaultFactoryProjectId: link.defaultFactoryProjectId,\n linkedAt: link.linkedAt.toISOString(),\n }));\n // `canConnect` tells the settings UI whether the web-initiated OIDC\n // connect flow is configured (vs. Slack-side Connect card only).\n return c.json({ accounts, canConnect: oidcEnabled });\n },\n }),\n // Self-service disconnect. The storage delete is guarded by the caller's\n // tenant userId, so a known sender key alone can never sever someone\n // else's link.\n registerApiRoute('/web/channel-accounts', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async c => {\n await auth.ensureUser(loose(c));\n const tenant = auth.tenant(loose(c));\n if (!tenant) return c.json({ error: 'unauthorized' }, 401);\n\n const body = (await c.req.json().catch(() => null)) as {\n platform?: string;\n externalTeamId?: string;\n externalUserId?: string;\n } | null;\n if (!body?.platform || !body.externalTeamId || !body.externalUserId) {\n return c.json({ error: 'platform, externalTeamId and externalUserId are required' }, 400);\n }\n\n const deleted = await accountLinks.deleteAccountLinkForUser({\n userId: tenant.userId,\n platform: body.platform,\n externalTeamId: body.externalTeamId,\n externalUserId: body.externalUserId,\n });\n return c.json({ deleted });\n },\n }),\n // Set (or clear, with `factoryProjectId: null`) which Factory project a\n // link's channel runs route to. The storage write is guarded by the\n // caller's tenant userId — a known sender key alone can never repoint\n // someone else's link — and a non-null factory must exist in the caller's\n // org.\n registerApiRoute('/web/channel-accounts/default-factory', {\n method: 'PATCH',\n requiresAuth: false,\n handler: async c => {\n await auth.ensureUser(loose(c));\n const tenant = auth.tenant(loose(c));\n if (!tenant) return c.json({ error: 'unauthorized' }, 401);\n\n const body = (await c.req.json().catch(() => null)) as {\n platform?: string;\n externalTeamId?: string;\n externalUserId?: string;\n factoryProjectId?: string | null;\n } | null;\n if (!body?.platform || !body.externalTeamId || !body.externalUserId || body.factoryProjectId === undefined) {\n return c.json(\n { error: 'platform, externalTeamId, externalUserId and factoryProjectId (nullable) are required' },\n 400,\n );\n }\n\n if (body.factoryProjectId !== null) {\n if (!projects) return c.json({ error: 'factory routing is not configured' }, 400);\n const factory = await projects.get({ orgId: tenant.orgId ?? '', id: body.factoryProjectId });\n if (!factory) return c.json({ error: 'unknown factory' }, 400);\n }\n\n const updated = await accountLinks.setDefaultFactory({\n userId: tenant.userId,\n platform: body.platform,\n externalTeamId: body.externalTeamId,\n externalUserId: body.externalUserId,\n factoryProjectId: body.factoryProjectId,\n });\n return c.json({ updated });\n },\n }),\n ];\n}\n"],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * `SlackIntegration` — Slack as a `FactoryIntegration`.
3
+ *
4
+ * Slack contributes two things to a factory: the chat channels that carry
5
+ * inbound messages into agent runs (`channels()`), and the browser-facing
6
+ * account-link routes that bind a Slack sender to a Mastra tenant (`routes()`).
7
+ * Both used to be assembled by hand in the deploy entry, which had to reach
8
+ * into the prepared agent controller by string key, resolve storage domains
9
+ * itself, mint its own state signer, and splice routes onto the factory's
10
+ * assembled server config. Implementing the integration interface instead means
11
+ * the factory does all of that the same way it already does for GitHub and
12
+ * Linear, and the entry's job shrinks to reading Slack's env vars once.
13
+ *
14
+ * Slack ships as a factory built-in alongside GitHub and Linear (it originally
15
+ * lived in `mastracode/web` to keep `@mastra/slack`/`chat` out of this package;
16
+ * the team reversed that so `create-factory` consumers get Slack channels out
17
+ * of the box). The integration interface remains open — third parties still add
18
+ * capabilities by implementing `FactoryIntegration` from outside the package.
19
+ */
20
+ import type { ApiRoute } from '@mastra/core/server';
21
+ import type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext } from '../base.js';
22
+ /**
23
+ * Slack app credentials, read from env ONCE by the deploy entry. `signingSecret`
24
+ * is required because the Slack adapter validates it at construction — an
25
+ * integration constructed without it would throw during `prepare()` rather than
26
+ * reporting itself unconfigured.
27
+ */
28
+ export interface SlackIntegrationConfig {
29
+ /** Verifies inbound Slack request signatures. Required. */
30
+ signingSecret: string;
31
+ /** Bot token used to post replies and ephemeral cards. */
32
+ botToken?: string;
33
+ /**
34
+ * OAuth client credentials. Required for account linking: "Sign in with
35
+ * Slack" (OIDC) is the only flow that writes a link, so without these the
36
+ * settings surface reports `canConnect: false` and no sender can link.
37
+ */
38
+ clientId?: string;
39
+ clientSecret?: string;
40
+ /**
41
+ * HTTPS origin Slack redirects back to. Slack requires HTTPS, so locally this
42
+ * is the tunnel origin rather than the app's own public URL.
43
+ */
44
+ oidcRedirectBaseUrl?: string;
45
+ /** SPA origin the post-connect redirect returns to. */
46
+ uiOrigin?: string;
47
+ }
48
+ export declare class SlackIntegration implements FactoryIntegration {
49
+ #private;
50
+ readonly id = "slack";
51
+ /**
52
+ * The OIDC connect flow round-trips a signed `state` through Slack, so the
53
+ * replica handling the callback must be able to verify a state a different
54
+ * replica signed.
55
+ */
56
+ readonly requiresStableStateSigner = true;
57
+ constructor(config: SlackIntegrationConfig);
58
+ channels(ctx: IntegrationContext): FactoryChannelsConfig;
59
+ routes(ctx: IntegrationContext): ApiRoute[];
60
+ diagnostics(): Record<string, unknown>;
61
+ }
62
+ //# sourceMappingURL=integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAKhG;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,gBAAiB,YAAW,kBAAkB;;IACzD,QAAQ,CAAC,EAAE,WAAW;IACtB;;;;OAIG;IACH,QAAQ,CAAC,yBAAyB,QAAQ;gBAU9B,MAAM,EAAE,sBAAsB;IAS1C,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IAoBxD,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAc3C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CASvC"}
@@ -0,0 +1,68 @@
1
+ import { createSlackConnectRoutes } from "./connect-route.js";
2
+ import { createSlackChannelsConfig } from "./slack.js";
3
+ //#region src/integrations/slack/integration.ts
4
+ var SlackIntegration = class {
5
+ id = "slack";
6
+ /**
7
+ * The OIDC connect flow round-trips a signed `state` through Slack, so the
8
+ * replica handling the callback must be able to verify a state a different
9
+ * replica signed.
10
+ */
11
+ requiresStableStateSigner = true;
12
+ #config;
13
+ /**
14
+ * Whether `channels()` found a source-control owner on the context and wired
15
+ * repo-backed sessions. Set at the channels() attach path, which runs once at
16
+ * boot before diagnostics are served.
17
+ */
18
+ #repoBackedSessions = false;
19
+ constructor(config) {
20
+ if (!config.signingSecret) throw new Error("SlackIntegration: 'signingSecret' is required — Slack cannot verify inbound requests without it.");
21
+ this.#config = config;
22
+ }
23
+ channels(ctx) {
24
+ const sourceControlOwner = ctx.storage.sourceControlOwner;
25
+ this.#repoBackedSessions = Boolean(sourceControlOwner);
26
+ return createSlackChannelsConfig({
27
+ slack: {
28
+ clientId: this.#config.clientId,
29
+ clientSecret: this.#config.clientSecret,
30
+ signingSecret: this.#config.signingSecret,
31
+ botToken: this.#config.botToken
32
+ },
33
+ accountLinks: ctx.storage.channelIdentity,
34
+ projects: ctx.storage.projects,
35
+ sourceControl: sourceControlOwner,
36
+ memorySettings: ctx.storage.memorySettings,
37
+ workItems: ctx.rules?.workItems
38
+ });
39
+ }
40
+ routes(ctx) {
41
+ const { clientId, clientSecret, oidcRedirectBaseUrl, uiOrigin } = this.#config;
42
+ return createSlackConnectRoutes({
43
+ auth: ctx.auth,
44
+ accountLinks: ctx.storage.channelIdentity,
45
+ tenantStateSigner: ctx.stateSigner,
46
+ oidc: clientId && clientSecret && oidcRedirectBaseUrl ? {
47
+ clientId,
48
+ clientSecret,
49
+ redirectBaseUrl: oidcRedirectBaseUrl,
50
+ uiOrigin
51
+ } : void 0,
52
+ projects: ctx.storage.projects
53
+ });
54
+ }
55
+ diagnostics() {
56
+ const { clientId, clientSecret, botToken, oidcRedirectBaseUrl } = this.#config;
57
+ return {
58
+ configured: true,
59
+ botTokenConfigured: Boolean(botToken),
60
+ oidcConfigured: Boolean(clientId && clientSecret && oidcRedirectBaseUrl),
61
+ repoBackedSessions: this.#repoBackedSessions
62
+ };
63
+ }
64
+ };
65
+ //#endregion
66
+ export { SlackIntegration };
67
+
68
+ //# sourceMappingURL=integration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.js","names":["#config","#repoBackedSessions"],"sources":["../../../src/integrations/slack/integration.ts"],"sourcesContent":["/**\n * `SlackIntegration` — Slack as a `FactoryIntegration`.\n *\n * Slack contributes two things to a factory: the chat channels that carry\n * inbound messages into agent runs (`channels()`), and the browser-facing\n * account-link routes that bind a Slack sender to a Mastra tenant (`routes()`).\n * Both used to be assembled by hand in the deploy entry, which had to reach\n * into the prepared agent controller by string key, resolve storage domains\n * itself, mint its own state signer, and splice routes onto the factory's\n * assembled server config. Implementing the integration interface instead means\n * the factory does all of that the same way it already does for GitHub and\n * Linear, and the entry's job shrinks to reading Slack's env vars once.\n *\n * Slack ships as a factory built-in alongside GitHub and Linear (it originally\n * lived in `mastracode/web` to keep `@mastra/slack`/`chat` out of this package;\n * the team reversed that so `create-factory` consumers get Slack channels out\n * of the box). The integration interface remains open — third parties still add\n * capabilities by implementing `FactoryIntegration` from outside the package.\n */\n\nimport type { ApiRoute } from '@mastra/core/server';\n\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext } from '../base.js';\n\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { createSlackChannelsConfig } from './slack.js';\n\n/**\n * Slack app credentials, read from env ONCE by the deploy entry. `signingSecret`\n * is required because the Slack adapter validates it at construction — an\n * integration constructed without it would throw during `prepare()` rather than\n * reporting itself unconfigured.\n */\nexport interface SlackIntegrationConfig {\n /** Verifies inbound Slack request signatures. Required. */\n signingSecret: string;\n /** Bot token used to post replies and ephemeral cards. */\n botToken?: string;\n /**\n * OAuth client credentials. Required for account linking: \"Sign in with\n * Slack\" (OIDC) is the only flow that writes a link, so without these the\n * settings surface reports `canConnect: false` and no sender can link.\n */\n clientId?: string;\n clientSecret?: string;\n /**\n * HTTPS origin Slack redirects back to. Slack requires HTTPS, so locally this\n * is the tunnel origin rather than the app's own public URL.\n */\n oidcRedirectBaseUrl?: string;\n /** SPA origin the post-connect redirect returns to. */\n uiOrigin?: string;\n}\n\nexport class SlackIntegration implements FactoryIntegration {\n readonly id = 'slack';\n /**\n * The OIDC connect flow round-trips a signed `state` through Slack, so the\n * replica handling the callback must be able to verify a state a different\n * replica signed.\n */\n readonly requiresStableStateSigner = true;\n\n readonly #config: SlackIntegrationConfig;\n /**\n * Whether `channels()` found a source-control owner on the context and wired\n * repo-backed sessions. Set at the channels() attach path, which runs once at\n * boot before diagnostics are served.\n */\n #repoBackedSessions = false;\n\n constructor(config: SlackIntegrationConfig) {\n if (!config.signingSecret) {\n throw new Error(\n \"SlackIntegration: 'signingSecret' is required — Slack cannot verify inbound requests without it.\",\n );\n }\n this.#config = config;\n }\n\n channels(ctx: IntegrationContext): FactoryChannelsConfig {\n // Repo-backed sessions come from the factory's source-control owner\n // (GitHub, when registered) — no config-level wiring by the entry.\n const sourceControlOwner = ctx.storage.sourceControlOwner;\n this.#repoBackedSessions = Boolean(sourceControlOwner);\n return createSlackChannelsConfig({\n slack: {\n clientId: this.#config.clientId,\n clientSecret: this.#config.clientSecret,\n signingSecret: this.#config.signingSecret,\n botToken: this.#config.botToken,\n },\n accountLinks: ctx.storage.channelIdentity,\n projects: ctx.storage.projects,\n sourceControl: sourceControlOwner,\n memorySettings: ctx.storage.memorySettings,\n workItems: ctx.rules?.workItems,\n });\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n const { clientId, clientSecret, oidcRedirectBaseUrl, uiOrigin } = this.#config;\n return createSlackConnectRoutes({\n auth: ctx.auth,\n accountLinks: ctx.storage.channelIdentity,\n tenantStateSigner: ctx.stateSigner,\n oidc:\n clientId && clientSecret && oidcRedirectBaseUrl\n ? { clientId, clientSecret, redirectBaseUrl: oidcRedirectBaseUrl, uiOrigin }\n : undefined,\n projects: ctx.storage.projects,\n });\n }\n\n diagnostics(): Record<string, unknown> {\n const { clientId, clientSecret, botToken, oidcRedirectBaseUrl } = this.#config;\n return {\n configured: true,\n botTokenConfigured: Boolean(botToken),\n oidcConfigured: Boolean(clientId && clientSecret && oidcRedirectBaseUrl),\n repoBackedSessions: this.#repoBackedSessions,\n };\n }\n}\n"],"mappings":";;;AAsDA,IAAa,mBAAb,MAA4D;CAC1D,KAAc;;;;;;CAMd,4BAAqC;CAErC;;;;;;CAMA,sBAAsB;CAEtB,YAAY,QAAgC;EAC1C,IAAI,CAAC,OAAO,eACV,MAAM,IAAI,MACR,kGACF;EAEF,KAAKA,UAAU;CACjB;CAEA,SAAS,KAAgD;EAGvD,MAAM,qBAAqB,IAAI,QAAQ;EACvC,KAAKC,sBAAsB,QAAQ,kBAAkB;EACrD,OAAO,0BAA0B;GAC/B,OAAO;IACL,UAAU,KAAKD,QAAQ;IACvB,cAAc,KAAKA,QAAQ;IAC3B,eAAe,KAAKA,QAAQ;IAC5B,UAAU,KAAKA,QAAQ;GACzB;GACA,cAAc,IAAI,QAAQ;GAC1B,UAAU,IAAI,QAAQ;GACtB,eAAe;GACf,gBAAgB,IAAI,QAAQ;GAC5B,WAAW,IAAI,OAAO;EACxB,CAAC;CACH;CAEA,OAAO,KAAqC;EAC1C,MAAM,EAAE,UAAU,cAAc,qBAAqB,aAAa,KAAKA;EACvE,OAAO,yBAAyB;GAC9B,MAAM,IAAI;GACV,cAAc,IAAI,QAAQ;GAC1B,mBAAmB,IAAI;GACvB,MACE,YAAY,gBAAgB,sBACxB;IAAE;IAAU;IAAc,iBAAiB;IAAqB;GAAS,IACzE,KAAA;GACN,UAAU,IAAI,QAAQ;EACxB,CAAC;CACH;CAEA,cAAuC;EACrC,MAAM,EAAE,UAAU,cAAc,UAAU,wBAAwB,KAAKA;EACvE,OAAO;GACL,YAAY;GACZ,oBAAoB,QAAQ,QAAQ;GACpC,gBAAgB,QAAQ,YAAY,gBAAgB,mBAAmB;GACvE,oBAAoB,KAAKC;EAC3B;CACF;AACF"}
@@ -0,0 +1,200 @@
1
+ import type { ChannelHandler, ChannelHandlers, ChannelSessionStart, ResolveResourceId, ResolveThreadId } from '@mastra/core/channels';
2
+ import type { ChannelAccountLink, ChannelAccountLinkKey, ChannelIdentityStorage } from '../../storage/domains/channel-identity/base.js';
3
+ import type { MemorySettingsStorage } from '../../storage/domains/memory-settings/base.js';
4
+ import type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';
5
+ import type { SourceControlStorageHandle } from '../../storage/domains/source-control/base.js';
6
+ import type { WorkItemsStorage } from '../../storage/domains/work-items/base.js';
7
+ import type { FactoryChannelsConfig } from '../base.js';
8
+ type HandlerThread = Parameters<ChannelHandler>[0];
9
+ type HandlerMessage = Parameters<ChannelHandler>[1];
10
+ /** Dependencies the Slack channel handlers close over, injected from the web entry. */
11
+ interface SlackChannelDeps {
12
+ /**
13
+ * The factory's reverse-index store mapping a Slack sender to a Mastra
14
+ * tenant. When provided, inbound messages from an unlinked sender are not
15
+ * dispatched — the run only proceeds (with the sender's tenant stamped on
16
+ * the request context) once they've linked their account. Unlinked senders
17
+ * get an ephemeral "connect your account" card instead.
18
+ */
19
+ accountLinks?: ChannelIdentityStorage;
20
+ /**
21
+ * Factory projects domain. When provided (alongside `accountLinks`), a
22
+ * linked sender's run must also resolve to a Factory project before it
23
+ * dispatches: their link's default factory, else their tenant's only
24
+ * factory (stamped back onto the link), else an ephemeral "pick a default
25
+ * factory" card and no run. Unset → no factory routing (runs dispatch as
26
+ * before).
27
+ */
28
+ projects?: FactoryProjectsStorage;
29
+ /**
30
+ * Storage handle of the integration that owns source control
31
+ * (`IntegrationContext.storage.sourceControlOwner`). Used to make new Slack
32
+ * threads repo-backed: when the sender is linked and their factory has a
33
+ * repository, the thread's resourceId becomes a Factory user-session id (repo
34
+ * cloned on a `slack/{threadTs}` branch) instead of the chat-only
35
+ * `channel:...` id. It also lets a started session read back the project it
36
+ * belongs to. Nothing here is provider-specific — the connection is matched
37
+ * by the handle's own `integrationId`. Absent (no source-control integration
38
+ * registered) → chat-only sessions as before.
39
+ */
40
+ sourceControl?: SourceControlStorageHandle;
41
+ /**
42
+ * Observational-memory settings domain. When provided, a repo-backed session
43
+ * adopts its owner's memory settings on start, matching the web kickoff.
44
+ */
45
+ memorySettings?: MemorySettingsStorage;
46
+ /**
47
+ * Factory work-items domain. When provided, a dispatched new-session thread
48
+ * (DM or mention) upserts a Work-board card in Building (`execute`) carrying
49
+ * the Slack thread as its external source and binding the repo-backed
50
+ * session. Best-effort — a failure never blocks the run. Unset → no card.
51
+ */
52
+ workItems?: WorkItemsStorage;
53
+ }
54
+ /** Outcome of the sender-link gate for one inbound message. */
55
+ type LinkedSenderResult =
56
+ /** Gating not configured — dispatch as before account linking existed. */
57
+ {
58
+ status: 'ungated';
59
+ }
60
+ /** Sender unlinked — Connect card posted (when possible), do not dispatch. */
61
+ | {
62
+ status: 'blocked';
63
+ }
64
+ /** Sender linked — their tenant plus the sender key the link lives under. */
65
+ | {
66
+ status: 'linked';
67
+ link: ChannelAccountLink;
68
+ key: ChannelAccountLinkKey;
69
+ };
70
+ /**
71
+ * Resolve the sender's account link, posting an ephemeral "connect your
72
+ * account" card (visible only to the sender) linking into the web UI's
73
+ * Slack-connect flow when they're unlinked.
74
+ */
75
+ export declare function resolveLinkedSender({ thread, message, accountLinks, }: {
76
+ thread: HandlerThread;
77
+ message: HandlerMessage;
78
+ accountLinks?: ChannelIdentityStorage;
79
+ }): Promise<LinkedSenderResult>;
80
+ /** Outcome of factory routing for one linked sender's inbound message. */
81
+ type FactoryRouteResult =
82
+ /** Factory routing not configured — dispatch without a factory. */
83
+ {
84
+ status: 'ungated';
85
+ }
86
+ /** No factory resolved — prompt card posted (when possible), do not dispatch. */
87
+ | {
88
+ status: 'blocked';
89
+ }
90
+ /** The Factory project this sender's runs route to. */
91
+ | {
92
+ status: 'resolved';
93
+ factoryProjectId: string;
94
+ slackWorkItemsEnabled: boolean;
95
+ };
96
+ /**
97
+ * Decide which Factory project a linked sender's run belongs to:
98
+ *
99
+ * 1. The link's `defaultFactoryProjectId`, when it still exists (a stale id —
100
+ * deleted factory — falls through as if unset).
101
+ * 2. Else, the tenant's only factory, stamped back onto the link so it shows
102
+ * up (and stays editable) in Connected Accounts settings.
103
+ * 3. Else — zero or several factories — an ephemeral "pick a default factory"
104
+ * card deep-linking to settings, and the run is blocked.
105
+ */
106
+ export declare function resolveFactoryForLink({ thread, message, link, key, accountLinks, projects, }: {
107
+ thread: HandlerThread;
108
+ message: HandlerMessage;
109
+ link: ChannelAccountLink;
110
+ key: ChannelAccountLinkKey;
111
+ accountLinks: ChannelIdentityStorage;
112
+ projects?: FactoryProjectsStorage;
113
+ }): Promise<FactoryRouteResult>;
114
+ /**
115
+ * Resolve the resourceId for a NEW Slack channel thread. A linked sender whose
116
+ * factory has a repository gets a Factory user-session id — the controller
117
+ * session then materializes the repo sandbox via the factory's dynamic
118
+ * workspace (clone + PAT), the session shows up in the web Sessions list, and
119
+ * View Session deep-links land on the normal workspace route. Everything else
120
+ * (unlinked, unrouted, repo-less, or no source control) keeps the chat-only
121
+ * `defaultResourceId`.
122
+ *
123
+ * Pure lookups only — cards for unlinked/unrouted senders are the dispatch
124
+ * gate's job; this hook must never post.
125
+ */
126
+ export declare function createChannelResourceIdResolver(deps: SlackChannelDeps): ResolveResourceId;
127
+ /**
128
+ * Thread id for a NEW Slack channel thread. Repo-backed threads take the
129
+ * user-session id AS their thread id, matching the web convention
130
+ * (FactoryStartCoordinator seeds threads with threadId = sessionId) so
131
+ * `/workspaces/{sessionId}/threads/{sessionId}` resolves Slack-created
132
+ * sessions exactly like web-created ones — no `?resourceId=` override needed.
133
+ * Chat-only threads keep the default random id: their `channel:...`
134
+ * resourceId is a memory key, not a unique thread id.
135
+ */
136
+ export declare const resolveChannelThreadId: ResolveThreadId;
137
+ /**
138
+ * Apply the factory's configuration to a Slack-created session the first time
139
+ * its thread reaches the controller.
140
+ *
141
+ * Without this a Slack session runs on the SDK's built-in mode default
142
+ * (`openai/gpt-5.5`), so a factory configured for any other provider fails every
143
+ * message with a missing-credentials error. The web kickoff has always applied
144
+ * the factory default; this brings Slack to the same footing.
145
+ *
146
+ * Only repo-backed threads are configured. Their resourceId IS the Factory
147
+ * session id, which the source-control rows turn back into a project — a
148
+ * chat-only `channel:...` id names no project, so there is nothing to read.
149
+ *
150
+ * Skips a session whose mode already has a model persisted on the thread. That
151
+ * is the durable record of a deliberate choice — either an earlier start or a
152
+ * user's own switch — and re-applying the factory default over it would undo
153
+ * the user's selection every time the process restarts.
154
+ */
155
+ export declare function createChannelSessionStartHook(deps: SlackChannelDeps): ChannelSessionStart;
156
+ /**
157
+ * Upsert the Work-board card for a dispatched Slack-thread run. Keyed on the
158
+ * thread via `externalSource` — the work-items domain's unique
159
+ * `(factory_project_id, source_key)` index makes repeat messages reuse the
160
+ * same card, and `reuseMode: 'preserve'` keeps a card a human already dragged
161
+ * across stages untouched. The card lands in Building (`execute`) for every
162
+ * dispatched thread (DM or mention) — there is deliberately no per-origin
163
+ * stage split; smart routing is a follow-up.
164
+ *
165
+ * The session id / branch / threadId and the workspace deep-link are resolved
166
+ * by the caller (which already looked up the internal thread), so this helper
167
+ * just shapes and writes. Best-effort: the run is already dispatched, so a
168
+ * failure logs instead of throwing — work-item creation must never abort a Slack run.
169
+ */
170
+ export declare function upsertThreadWorkItem({ workItems, thread, message, link, factoryProjectId, session, url, }: {
171
+ workItems: WorkItemsStorage;
172
+ thread: HandlerThread;
173
+ message: HandlerMessage;
174
+ link: ChannelAccountLink;
175
+ factoryProjectId: string;
176
+ /**
177
+ * The repo-backed Factory session to bind under the `chat` role, or
178
+ * `undefined` for a chat-only thread (no Factory session to bind).
179
+ */
180
+ session?: {
181
+ sessionId: string;
182
+ branch: string;
183
+ threadId: string;
184
+ };
185
+ /** Workspace deep-link to the running session; omitted when no public URL. */
186
+ url?: string;
187
+ }): Promise<void>;
188
+ export declare const createHandlers: (deps: SlackChannelDeps) => ChannelHandlers;
189
+ /** Slack app credentials, passed in explicitly rather than read from env here. */
190
+ interface SlackCredentials {
191
+ clientId?: string;
192
+ clientSecret?: string;
193
+ signingSecret: string;
194
+ botToken?: string;
195
+ }
196
+ export declare function createSlackChannelsConfig(deps: SlackChannelDeps & {
197
+ slack: SlackCredentials;
198
+ }): FactoryChannelsConfig;
199
+ export {};
200
+ //# sourceMappingURL=slack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/slack.ts"],"names":[],"mappings":"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"}