@mastra/factory 0.0.0-fix-thread-subscription-multi-instance-redis-streams-20260730204150

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 (313) hide show
  1. package/CHANGELOG.md +695 -0
  2. package/LICENSE.md +30 -0
  3. package/README.md +27 -0
  4. package/dist/auth.d.ts +181 -0
  5. package/dist/auth.d.ts.map +1 -0
  6. package/dist/auth.js +505 -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 +140 -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 +245 -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 +156 -0
  18. package/dist/factory.d.ts.map +1 -0
  19. package/dist/factory.js +483 -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 +210 -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/factory-session.d.ts +12 -0
  32. package/dist/integrations/github/factory-session.d.ts.map +1 -0
  33. package/dist/integrations/github/factory-session.js +37 -0
  34. package/dist/integrations/github/factory-session.js.map +1 -0
  35. package/dist/integrations/github/integration.d.ts +191 -0
  36. package/dist/integrations/github/integration.d.ts.map +1 -0
  37. package/dist/integrations/github/integration.js +1077 -0
  38. package/dist/integrations/github/integration.js.map +1 -0
  39. package/dist/integrations/github/issue-triage.d.ts +27 -0
  40. package/dist/integrations/github/issue-triage.d.ts.map +1 -0
  41. package/dist/integrations/github/issue-triage.js +82 -0
  42. package/dist/integrations/github/issue-triage.js.map +1 -0
  43. package/dist/integrations/github/pat.d.ts +38 -0
  44. package/dist/integrations/github/pat.d.ts.map +1 -0
  45. package/dist/integrations/github/pat.js +59 -0
  46. package/dist/integrations/github/pat.js.map +1 -0
  47. package/dist/integrations/github/provenance.d.ts +27 -0
  48. package/dist/integrations/github/provenance.d.ts.map +1 -0
  49. package/dist/integrations/github/provenance.js +75 -0
  50. package/dist/integrations/github/provenance.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 +1324 -0
  54. package/dist/integrations/github/routes.js.map +1 -0
  55. package/dist/integrations/github/rules.d.ts +78 -0
  56. package/dist/integrations/github/rules.d.ts.map +1 -0
  57. package/dist/integrations/github/rules.js +362 -0
  58. package/dist/integrations/github/rules.js.map +1 -0
  59. package/dist/integrations/github/sandbox-release.d.ts +38 -0
  60. package/dist/integrations/github/sandbox-release.d.ts.map +1 -0
  61. package/dist/integrations/github/sandbox-release.js +74 -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 +87 -0
  80. package/dist/integrations/github/webhook.d.ts.map +1 -0
  81. package/dist/integrations/github/webhook.js +397 -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 +233 -0
  88. package/dist/integrations/linear/integration.d.ts.map +1 -0
  89. package/dist/integrations/linear/integration.js +672 -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 +239 -0
  94. package/dist/integrations/linear/routes.js.map +1 -0
  95. package/dist/integrations/linear/rules.d.ts +42 -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 +49 -0
  107. package/dist/integrations/platform/github/event-worker.d.ts.map +1 -0
  108. package/dist/integrations/platform/github/event-worker.js +348 -0
  109. package/dist/integrations/platform/github/event-worker.js.map +1 -0
  110. package/dist/integrations/platform/github/integration.d.ts +50 -0
  111. package/dist/integrations/platform/github/integration.d.ts.map +1 -0
  112. package/dist/integrations/platform/github/integration.js +961 -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/workos/integration.d.ts +39 -0
  119. package/dist/integrations/workos/integration.d.ts.map +1 -0
  120. package/dist/integrations/workos/integration.js +91 -0
  121. package/dist/integrations/workos/integration.js.map +1 -0
  122. package/dist/routes/config.d.ts +235 -0
  123. package/dist/routes/config.d.ts.map +1 -0
  124. package/dist/routes/config.js +972 -0
  125. package/dist/routes/config.js.map +1 -0
  126. package/dist/routes/custom-provider-source.d.ts +52 -0
  127. package/dist/routes/custom-provider-source.d.ts.map +1 -0
  128. package/dist/routes/custom-provider-source.js +124 -0
  129. package/dist/routes/custom-provider-source.js.map +1 -0
  130. package/dist/routes/fs.d.ts +151 -0
  131. package/dist/routes/fs.d.ts.map +1 -0
  132. package/dist/routes/fs.js +708 -0
  133. package/dist/routes/fs.js.map +1 -0
  134. package/dist/routes/intake.d.ts +23 -0
  135. package/dist/routes/intake.d.ts.map +1 -0
  136. package/dist/routes/intake.js +179 -0
  137. package/dist/routes/intake.js.map +1 -0
  138. package/dist/routes/oauth.d.ts +43 -0
  139. package/dist/routes/oauth.d.ts.map +1 -0
  140. package/dist/routes/oauth.js +463 -0
  141. package/dist/routes/oauth.js.map +1 -0
  142. package/dist/routes/projects.d.ts +19 -0
  143. package/dist/routes/projects.d.ts.map +1 -0
  144. package/dist/routes/projects.js +461 -0
  145. package/dist/routes/projects.js.map +1 -0
  146. package/dist/routes/provider-credentials.d.ts +81 -0
  147. package/dist/routes/provider-credentials.d.ts.map +1 -0
  148. package/dist/routes/provider-credentials.js +86 -0
  149. package/dist/routes/provider-credentials.js.map +1 -0
  150. package/dist/routes/route.d.ts +48 -0
  151. package/dist/routes/route.d.ts.map +1 -0
  152. package/dist/routes/route.js +15 -0
  153. package/dist/routes/route.js.map +1 -0
  154. package/dist/routes/skills.d.ts +30 -0
  155. package/dist/routes/skills.d.ts.map +1 -0
  156. package/dist/routes/skills.js +168 -0
  157. package/dist/routes/skills.js.map +1 -0
  158. package/dist/routes/surface.d.ts +91 -0
  159. package/dist/routes/surface.d.ts.map +1 -0
  160. package/dist/routes/surface.js +260 -0
  161. package/dist/routes/surface.js.map +1 -0
  162. package/dist/routes/tenant-credentials.d.ts +74 -0
  163. package/dist/routes/tenant-credentials.d.ts.map +1 -0
  164. package/dist/routes/tenant-credentials.js +156 -0
  165. package/dist/routes/tenant-credentials.js.map +1 -0
  166. package/dist/routes/work-items.d.ts +40 -0
  167. package/dist/routes/work-items.d.ts.map +1 -0
  168. package/dist/routes/work-items.js +695 -0
  169. package/dist/routes/work-items.js.map +1 -0
  170. package/dist/rules/binding-context.d.ts +5 -0
  171. package/dist/rules/binding-context.d.ts.map +1 -0
  172. package/dist/rules/binding-context.js +28 -0
  173. package/dist/rules/binding-context.js.map +1 -0
  174. package/dist/rules/defaults.d.ts +9 -0
  175. package/dist/rules/defaults.d.ts.map +1 -0
  176. package/dist/rules/defaults.js +260 -0
  177. package/dist/rules/defaults.js.map +1 -0
  178. package/dist/rules/dispatcher.d.ts +42 -0
  179. package/dist/rules/dispatcher.d.ts.map +1 -0
  180. package/dist/rules/dispatcher.js +518 -0
  181. package/dist/rules/dispatcher.js.map +1 -0
  182. package/dist/rules/index.d.ts +7 -0
  183. package/dist/rules/index.d.ts.map +1 -0
  184. package/dist/rules/index.js +5 -0
  185. package/dist/rules/processor.d.ts +54 -0
  186. package/dist/rules/processor.d.ts.map +1 -0
  187. package/dist/rules/processor.js +378 -0
  188. package/dist/rules/processor.js.map +1 -0
  189. package/dist/rules/resolve.d.ts +17 -0
  190. package/dist/rules/resolve.d.ts.map +1 -0
  191. package/dist/rules/resolve.js +30 -0
  192. package/dist/rules/resolve.js.map +1 -0
  193. package/dist/rules/start-coordinator.d.ts +60 -0
  194. package/dist/rules/start-coordinator.d.ts.map +1 -0
  195. package/dist/rules/start-coordinator.js +194 -0
  196. package/dist/rules/start-coordinator.js.map +1 -0
  197. package/dist/rules/tools.d.ts +10 -0
  198. package/dist/rules/tools.d.ts.map +1 -0
  199. package/dist/rules/tools.js +58 -0
  200. package/dist/rules/tools.js.map +1 -0
  201. package/dist/rules/transition-service.d.ts +49 -0
  202. package/dist/rules/transition-service.d.ts.map +1 -0
  203. package/dist/rules/transition-service.js +232 -0
  204. package/dist/rules/transition-service.js.map +1 -0
  205. package/dist/rules/types.d.ts +234 -0
  206. package/dist/rules/types.d.ts.map +1 -0
  207. package/dist/rules/types.js +38 -0
  208. package/dist/rules/types.js.map +1 -0
  209. package/dist/rules/validation.d.ts +11 -0
  210. package/dist/rules/validation.d.ts.map +1 -0
  211. package/dist/rules/validation.js +308 -0
  212. package/dist/rules/validation.js.map +1 -0
  213. package/dist/sandbox/fleet.d.ts +222 -0
  214. package/dist/sandbox/fleet.d.ts.map +1 -0
  215. package/dist/sandbox/fleet.js +322 -0
  216. package/dist/sandbox/fleet.js.map +1 -0
  217. package/dist/sandbox/reattach.d.ts +3 -0
  218. package/dist/sandbox/reattach.d.ts.map +1 -0
  219. package/dist/sandbox/reattach.js +16 -0
  220. package/dist/sandbox/reattach.js.map +1 -0
  221. package/dist/server-error.d.ts +12 -0
  222. package/dist/server-error.d.ts.map +1 -0
  223. package/dist/server-error.js +16 -0
  224. package/dist/server-error.js.map +1 -0
  225. package/dist/skills/service.d.ts +48 -0
  226. package/dist/skills/service.d.ts.map +1 -0
  227. package/dist/skills/service.js +40 -0
  228. package/dist/skills/service.js.map +1 -0
  229. package/dist/spa-static.d.ts +33 -0
  230. package/dist/spa-static.d.ts.map +1 -0
  231. package/dist/spa-static.js +104 -0
  232. package/dist/spa-static.js.map +1 -0
  233. package/dist/state-signing.d.ts +59 -0
  234. package/dist/state-signing.d.ts.map +1 -0
  235. package/dist/state-signing.js +83 -0
  236. package/dist/state-signing.js.map +1 -0
  237. package/dist/storage/domains/audit/agent-audit.d.ts +27 -0
  238. package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -0
  239. package/dist/storage/domains/audit/agent-audit.js +66 -0
  240. package/dist/storage/domains/audit/agent-audit.js.map +1 -0
  241. package/dist/storage/domains/audit/base.d.ts +140 -0
  242. package/dist/storage/domains/audit/base.d.ts.map +1 -0
  243. package/dist/storage/domains/audit/base.js +184 -0
  244. package/dist/storage/domains/audit/base.js.map +1 -0
  245. package/dist/storage/domains/audit/domain.d.ts +69 -0
  246. package/dist/storage/domains/audit/domain.d.ts.map +1 -0
  247. package/dist/storage/domains/audit/domain.js +167 -0
  248. package/dist/storage/domains/audit/domain.js.map +1 -0
  249. package/dist/storage/domains/channel-identity/base.d.ts +96 -0
  250. package/dist/storage/domains/channel-identity/base.d.ts.map +1 -0
  251. package/dist/storage/domains/channel-identity/base.js +149 -0
  252. package/dist/storage/domains/channel-identity/base.js.map +1 -0
  253. package/dist/storage/domains/credentials/base.d.ts +134 -0
  254. package/dist/storage/domains/credentials/base.d.ts.map +1 -0
  255. package/dist/storage/domains/credentials/base.js +273 -0
  256. package/dist/storage/domains/credentials/base.js.map +1 -0
  257. package/dist/storage/domains/custom-providers/base.d.ts +57 -0
  258. package/dist/storage/domains/custom-providers/base.d.ts.map +1 -0
  259. package/dist/storage/domains/custom-providers/base.js +145 -0
  260. package/dist/storage/domains/custom-providers/base.js.map +1 -0
  261. package/dist/storage/domains/intake/base.d.ts +38 -0
  262. package/dist/storage/domains/intake/base.d.ts.map +1 -0
  263. package/dist/storage/domains/intake/base.js +80 -0
  264. package/dist/storage/domains/intake/base.js.map +1 -0
  265. package/dist/storage/domains/integrations/base.d.ts +126 -0
  266. package/dist/storage/domains/integrations/base.d.ts.map +1 -0
  267. package/dist/storage/domains/integrations/base.js +317 -0
  268. package/dist/storage/domains/integrations/base.js.map +1 -0
  269. package/dist/storage/domains/memory-settings/base.d.ts +62 -0
  270. package/dist/storage/domains/memory-settings/base.d.ts.map +1 -0
  271. package/dist/storage/domains/memory-settings/base.js +126 -0
  272. package/dist/storage/domains/memory-settings/base.js.map +1 -0
  273. package/dist/storage/domains/model-packs/base.d.ts +49 -0
  274. package/dist/storage/domains/model-packs/base.d.ts.map +1 -0
  275. package/dist/storage/domains/model-packs/base.js +96 -0
  276. package/dist/storage/domains/model-packs/base.js.map +1 -0
  277. package/dist/storage/domains/projects/base.d.ts +58 -0
  278. package/dist/storage/domains/projects/base.d.ts.map +1 -0
  279. package/dist/storage/domains/projects/base.js +111 -0
  280. package/dist/storage/domains/projects/base.js.map +1 -0
  281. package/dist/storage/domains/queue-health/base.d.ts +52 -0
  282. package/dist/storage/domains/queue-health/base.d.ts.map +1 -0
  283. package/dist/storage/domains/queue-health/base.js +122 -0
  284. package/dist/storage/domains/queue-health/base.js.map +1 -0
  285. package/dist/storage/domains/source-control/base.d.ts +358 -0
  286. package/dist/storage/domains/source-control/base.d.ts.map +1 -0
  287. package/dist/storage/domains/source-control/base.js +908 -0
  288. package/dist/storage/domains/source-control/base.js.map +1 -0
  289. package/dist/storage/domains/source-control/inmemory.d.ts +175 -0
  290. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -0
  291. package/dist/storage/domains/source-control/inmemory.js +377 -0
  292. package/dist/storage/domains/source-control/inmemory.js.map +1 -0
  293. package/dist/storage/domains/work-items/base.d.ts +412 -0
  294. package/dist/storage/domains/work-items/base.d.ts.map +1 -0
  295. package/dist/storage/domains/work-items/base.js +1309 -0
  296. package/dist/storage/domains/work-items/base.js.map +1 -0
  297. package/dist/storage/domains/work-items/metrics.d.ts +101 -0
  298. package/dist/storage/domains/work-items/metrics.d.ts.map +1 -0
  299. package/dist/storage/domains/work-items/metrics.js +221 -0
  300. package/dist/storage/domains/work-items/metrics.js.map +1 -0
  301. package/dist/timing.d.ts +15 -0
  302. package/dist/timing.d.ts.map +1 -0
  303. package/dist/timing.js +34 -0
  304. package/dist/timing.js.map +1 -0
  305. package/dist/workspace.d.ts +24 -0
  306. package/dist/workspace.d.ts.map +1 -0
  307. package/dist/workspace.js +286 -0
  308. package/dist/workspace.js.map +1 -0
  309. package/factory-skills/configure-factory-rules/SKILL.md +67 -0
  310. package/factory-skills/factory-plan/SKILL.md +61 -0
  311. package/factory-skills/factory-review/SKILL.md +155 -0
  312. package/factory-skills/factory-triage/SKILL.md +77 -0
  313. package/package.json +81 -0
@@ -0,0 +1,463 @@
1
+ import { getAuthProviderId, resolveCredentialContext } from "./provider-credentials.js";
2
+ import { Route } from "./route.js";
3
+ import { ModelCredentialsStorage } from "../storage/domains/credentials/base.js";
4
+ import { registerApiRoute } from "@mastra/core/server";
5
+ import { randomUUID } from "crypto";
6
+ import { nextPollDelayMs } from "@mastra/code-sdk/auth/device-code";
7
+ import { completeAnthropicLogin, startAnthropicLogin } from "@mastra/code-sdk/auth/providers/anthropic";
8
+ import { copilotNextPollDelayMs, pollGitHubCopilotDeviceLogin, startGitHubCopilotDeviceLogin } from "@mastra/code-sdk/auth/providers/github-copilot";
9
+ import { pollCodexDeviceLogin, startCodexDeviceLogin } from "@mastra/code-sdk/auth/providers/openai-codex";
10
+ import { pollXAIDeviceLogin, startXAIDeviceLogin } from "@mastra/code-sdk/auth/providers/xai";
11
+ //#region src/routes/oauth.ts
12
+ /**
13
+ * Web OAuth sign-in routes for model providers (Settings › Providers).
14
+ *
15
+ * Wraps the SDK's step-based OAuth primitives (start/complete for Anthropic's
16
+ * paste-code PKCE flow, start/poll for the Codex/Copilot/xAI device flows) in
17
+ * HTTP routes. Flow state lives in login sessions — the `model-credentials`
18
+ * domain's `oauth_login_sessions` table in tenant mode (any replica can
19
+ * complete/poll), an in-memory store in local mode — so a flow can span
20
+ * requests. Completed credentials are always **user-scoped** (plan tokens are
21
+ * personal subscriptions) in tenant mode, or written to the file-backed
22
+ * `AuthStorage` in local mode.
23
+ *
24
+ * Tokens never leave the server; responses only carry flow metadata (URLs,
25
+ * user codes, poll delays).
26
+ */
27
+ /** Lifetime of a paste-code session (Anthropic gives no explicit expiry). */
28
+ const PASTE_CODE_TTL_MS = 600 * 1e3;
29
+ /** Web OAuth flows keyed by *catalog* provider id (mirrors {@link WEB_OAUTH_FLOW_KINDS}). */
30
+ const OAUTH_FLOWS = {
31
+ anthropic: {
32
+ kind: "paste-code",
33
+ start: async () => {
34
+ const { url, verifier } = await startAnthropicLogin();
35
+ return {
36
+ url,
37
+ instructions: "Open the link, authorize, then paste the code shown on the final page.",
38
+ expiresAt: Date.now() + PASTE_CODE_TTL_MS,
39
+ pending: { verifier }
40
+ };
41
+ },
42
+ complete: (pending, code) => completeAnthropicLogin(code, String(pending.verifier ?? ""))
43
+ },
44
+ openai: {
45
+ kind: "device-code",
46
+ start: async () => {
47
+ const p = await startCodexDeviceLogin();
48
+ return {
49
+ url: p.url,
50
+ userCode: p.userCode,
51
+ instructions: p.instructions,
52
+ expiresAt: p.deadlineAt,
53
+ nextPollMs: p.intervalMs,
54
+ pending: { ...p }
55
+ };
56
+ },
57
+ poll: async (pending) => {
58
+ const r = await pollCodexDeviceLogin(pending);
59
+ if (r.status === "complete") return {
60
+ status: "complete",
61
+ credentials: r.credentials
62
+ };
63
+ if (r.status === "pending") return {
64
+ status: "pending",
65
+ nextPollMs: r.nextPollMs
66
+ };
67
+ return {
68
+ status: "failed",
69
+ error: r.error
70
+ };
71
+ }
72
+ },
73
+ "github-copilot": {
74
+ kind: "device-code",
75
+ start: async () => {
76
+ const p = await startGitHubCopilotDeviceLogin();
77
+ return {
78
+ url: p.url,
79
+ userCode: p.userCode,
80
+ instructions: p.instructions,
81
+ expiresAt: p.deadlineAt,
82
+ nextPollMs: copilotNextPollDelayMs(p),
83
+ pending: { ...p }
84
+ };
85
+ },
86
+ poll: async (pending) => {
87
+ const p = pending;
88
+ if (p.domain !== "github.com" || p.enterpriseDomain !== void 0) return {
89
+ status: "failed",
90
+ error: "Unsupported GitHub host"
91
+ };
92
+ const r = await pollGitHubCopilotDeviceLogin(p);
93
+ if (r.status === "complete") return {
94
+ status: "complete",
95
+ credentials: r.credentials
96
+ };
97
+ if (r.status === "pending") return {
98
+ status: "pending",
99
+ nextPollMs: r.nextPollMs,
100
+ pending: { ...r.pending }
101
+ };
102
+ return {
103
+ status: "failed",
104
+ error: r.error
105
+ };
106
+ }
107
+ },
108
+ xai: {
109
+ kind: "device-code",
110
+ start: async () => {
111
+ const p = await startXAIDeviceLogin();
112
+ return {
113
+ url: p.url,
114
+ userCode: p.userCode,
115
+ instructions: p.instructions,
116
+ expiresAt: p.state.deadlineAt,
117
+ nextPollMs: nextPollDelayMs(p.state),
118
+ pending: { ...p }
119
+ };
120
+ },
121
+ poll: async (pending) => {
122
+ const r = await pollXAIDeviceLogin(pending);
123
+ if (r.status === "complete") return {
124
+ status: "complete",
125
+ credentials: r.credentials
126
+ };
127
+ if (r.status === "pending") return {
128
+ status: "pending",
129
+ nextPollMs: r.nextPollMs,
130
+ pending: { ...r.pending }
131
+ };
132
+ return {
133
+ status: "failed",
134
+ error: r.error
135
+ };
136
+ }
137
+ }
138
+ };
139
+ function loose(c) {
140
+ return c;
141
+ }
142
+ /**
143
+ * Local-mode login sessions. Flows in local mode are single-process, so a
144
+ * process-local libsql `:memory:` database (which already handles TTL
145
+ * cleanup) is sufficient.
146
+ */
147
+ let localSessionsPromise;
148
+ function localSessions() {
149
+ localSessionsPromise ??= (async () => {
150
+ const { LibSQLFactoryStorage } = await import("@mastra/libsql");
151
+ const storage = new LibSQLFactoryStorage({
152
+ id: "local-oauth-sessions",
153
+ url: ":memory:"
154
+ });
155
+ const store = storage.registerDomain(new ModelCredentialsStorage());
156
+ await storage.init();
157
+ return store;
158
+ })();
159
+ return localSessionsPromise;
160
+ }
161
+ const LOCAL_TENANT = {
162
+ orgId: "local",
163
+ userId: "local"
164
+ };
165
+ async function sessionStore(ctx) {
166
+ return ctx.mode === "tenant" ? ctx.storage : localSessions();
167
+ }
168
+ function sessionTenant(ctx) {
169
+ return ctx.mode === "tenant" ? {
170
+ orgId: ctx.orgId,
171
+ userId: ctx.userId
172
+ } : LOCAL_TENANT;
173
+ }
174
+ /** Load a session and verify it belongs to the caller + provider (else undefined). */
175
+ async function loadOwnedSession({ ctx, provider, sessionId }) {
176
+ const session = await (await sessionStore(ctx)).getLoginSession(sessionId);
177
+ if (!session) return void 0;
178
+ const tenant = sessionTenant(ctx);
179
+ if (session.orgId !== tenant.orgId || session.userId !== tenant.userId) return void 0;
180
+ if (session.provider !== provider) return void 0;
181
+ return session;
182
+ }
183
+ /** Persist completed OAuth credentials — always user-scoped in tenant mode. */
184
+ async function persistOAuthCredential({ ctx, provider, credentials, authStorage, onCredentialsChanged }) {
185
+ const authProviderId = getAuthProviderId(provider);
186
+ if (ctx.mode === "tenant") {
187
+ await ctx.storage.setCredential({
188
+ orgId: ctx.orgId,
189
+ userId: ctx.userId
190
+ }, authProviderId, {
191
+ type: "oauth",
192
+ ...credentials
193
+ });
194
+ onCredentialsChanged({
195
+ orgId: ctx.orgId,
196
+ userId: ctx.userId
197
+ });
198
+ return;
199
+ }
200
+ if (!authStorage) throw new Error("Credential storage is not available");
201
+ authStorage.set(authProviderId, {
202
+ type: "oauth",
203
+ ...credentials
204
+ });
205
+ }
206
+ async function readJsonBody(c) {
207
+ try {
208
+ const body = await c.req.json();
209
+ return body && typeof body === "object" ? body : {};
210
+ } catch {
211
+ return {};
212
+ }
213
+ }
214
+ /**
215
+ * Provider OAuth sign-in routes as Mastra `apiRoutes`:
216
+ * - `POST /web/config/providers/:provider/oauth/start` — begin a sign-in flow
217
+ * - `POST /web/config/providers/:provider/oauth/complete` — paste-code exchange
218
+ * - `POST /web/config/providers/:provider/oauth/poll` — one device-code poll
219
+ * - `DELETE /web/config/providers/:provider/oauth/session/:sessionId` — cancel a flow
220
+ * - `DELETE /web/config/providers/:provider/oauth` — sign out (caller only)
221
+ */
222
+ var OAuthRoutes = class extends Route {
223
+ routes() {
224
+ const { auth, authStorage, modelCredentials } = this.deps;
225
+ const onCredentialsChanged = this.deps.onCredentialsChanged ?? (() => {});
226
+ return [
227
+ registerApiRoute("/web/config/providers/:provider/oauth/start", {
228
+ method: "POST",
229
+ requiresAuth: false,
230
+ handler: async (c) => {
231
+ const ctx = await resolveCredentialContext({
232
+ c: loose(c),
233
+ auth,
234
+ credentials: modelCredentials
235
+ });
236
+ if ("response" in ctx) return ctx.response;
237
+ const provider = c.req.param("provider");
238
+ const flow = OAUTH_FLOWS[provider];
239
+ if (!flow) return c.json({
240
+ error: "oauth_not_supported",
241
+ message: `Provider does not support web sign-in`
242
+ }, 404);
243
+ const body = await readJsonBody(loose(c));
244
+ if (typeof body.mode === "string" && body.mode !== flow.kind) return c.json({
245
+ error: "invalid_mode",
246
+ message: `Unsupported mode for ${provider}: ${body.mode}`
247
+ }, 400);
248
+ let started;
249
+ try {
250
+ started = await flow.start();
251
+ } catch (error) {
252
+ return c.json({ error: error instanceof Error ? error.message : String(error) }, 502);
253
+ }
254
+ const sessionId = randomUUID();
255
+ const tenant = sessionTenant(ctx);
256
+ await (await sessionStore(ctx)).createLoginSession({
257
+ sessionId,
258
+ orgId: tenant.orgId,
259
+ userId: tenant.userId,
260
+ provider,
261
+ kind: flow.kind,
262
+ pending: started.pending,
263
+ expiresAt: new Date(started.expiresAt),
264
+ nextPollAt: started.nextPollMs != null ? new Date(Date.now() + started.nextPollMs) : null
265
+ });
266
+ return c.json({
267
+ sessionId,
268
+ kind: flow.kind,
269
+ url: started.url,
270
+ ...started.userCode ? { userCode: started.userCode } : {},
271
+ instructions: started.instructions,
272
+ expiresAt: started.expiresAt,
273
+ ...started.nextPollMs != null ? { nextPollMs: started.nextPollMs } : {}
274
+ });
275
+ }
276
+ }),
277
+ registerApiRoute("/web/config/providers/:provider/oauth/complete", {
278
+ method: "POST",
279
+ requiresAuth: false,
280
+ handler: async (c) => {
281
+ const ctx = await resolveCredentialContext({
282
+ c: loose(c),
283
+ auth,
284
+ credentials: modelCredentials
285
+ });
286
+ if ("response" in ctx) return ctx.response;
287
+ const provider = c.req.param("provider");
288
+ const flow = OAUTH_FLOWS[provider];
289
+ if (!flow?.complete) return c.json({ error: "oauth_not_supported" }, 404);
290
+ const body = await readJsonBody(loose(c));
291
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
292
+ const code = typeof body.code === "string" ? body.code.trim() : "";
293
+ if (!sessionId || !code) return c.json({ error: "Missing required fields: sessionId, code" }, 400);
294
+ const session = await loadOwnedSession({
295
+ ctx,
296
+ provider,
297
+ sessionId
298
+ });
299
+ if (!session) return c.json({ error: "session_not_found" }, 404);
300
+ if (session.kind !== "paste-code") return c.json({ error: "wrong_session_kind" }, 400);
301
+ const claimed = await (await sessionStore(ctx)).claimLoginSession(sessionId, {
302
+ orgId: session.orgId,
303
+ userId: session.userId,
304
+ provider,
305
+ kind: "paste-code"
306
+ });
307
+ if (!claimed) return c.json({ error: "oauth_in_progress" }, 409);
308
+ let credentials;
309
+ try {
310
+ credentials = await flow.complete(claimed.pending, code);
311
+ } catch (error) {
312
+ await (await sessionStore(ctx)).touchLoginSession(sessionId, { nextPollAt: null });
313
+ return c.json({ error: error instanceof Error ? error.message : String(error) }, 400);
314
+ }
315
+ await persistOAuthCredential({
316
+ ctx,
317
+ provider,
318
+ credentials,
319
+ authStorage,
320
+ onCredentialsChanged
321
+ });
322
+ await (await sessionStore(ctx)).deleteLoginSession(sessionId);
323
+ return c.json({
324
+ status: "complete",
325
+ ok: true
326
+ });
327
+ }
328
+ }),
329
+ registerApiRoute("/web/config/providers/:provider/oauth/poll", {
330
+ method: "POST",
331
+ requiresAuth: false,
332
+ handler: async (c) => {
333
+ const ctx = await resolveCredentialContext({
334
+ c: loose(c),
335
+ auth,
336
+ credentials: modelCredentials
337
+ });
338
+ if ("response" in ctx) return ctx.response;
339
+ const provider = c.req.param("provider");
340
+ const flow = OAUTH_FLOWS[provider];
341
+ if (!flow?.poll) return c.json({ error: "oauth_not_supported" }, 404);
342
+ const body = await readJsonBody(loose(c));
343
+ const sessionId = typeof body.sessionId === "string" ? body.sessionId : "";
344
+ if (!sessionId) return c.json({ error: "Missing required field: sessionId" }, 400);
345
+ const session = await loadOwnedSession({
346
+ ctx,
347
+ provider,
348
+ sessionId
349
+ });
350
+ if (!session) return c.json({ error: "session_not_found" }, 404);
351
+ if (session.kind !== "device-code") return c.json({ error: "wrong_session_kind" }, 400);
352
+ const now = Date.now();
353
+ const nextPollAt = session.nextPollAt?.getTime();
354
+ if (nextPollAt != null && now < nextPollAt) return c.json({
355
+ status: "pending",
356
+ nextPollMs: nextPollAt - now
357
+ });
358
+ const claimed = await (await sessionStore(ctx)).claimLoginSession(sessionId, {
359
+ orgId: session.orgId,
360
+ userId: session.userId,
361
+ provider,
362
+ kind: "device-code"
363
+ });
364
+ if (!claimed) return c.json({
365
+ status: "pending",
366
+ nextPollMs: 250
367
+ });
368
+ let result;
369
+ try {
370
+ result = await flow.poll(claimed.pending);
371
+ } catch (error) {
372
+ await (await sessionStore(ctx)).touchLoginSession(sessionId, { nextPollAt: null });
373
+ throw error;
374
+ }
375
+ if (result.status === "complete") {
376
+ await persistOAuthCredential({
377
+ ctx,
378
+ provider,
379
+ credentials: result.credentials,
380
+ authStorage,
381
+ onCredentialsChanged
382
+ });
383
+ await (await sessionStore(ctx)).deleteLoginSession(sessionId);
384
+ return c.json({
385
+ status: "complete",
386
+ ok: true
387
+ });
388
+ }
389
+ if (result.status === "failed") {
390
+ await (await sessionStore(ctx)).deleteLoginSession(sessionId);
391
+ return c.json({
392
+ status: "failed",
393
+ error: result.error
394
+ });
395
+ }
396
+ await (await sessionStore(ctx)).touchLoginSession(sessionId, {
397
+ ...result.pending ? { pending: result.pending } : {},
398
+ nextPollAt: new Date(now + result.nextPollMs)
399
+ });
400
+ return c.json({
401
+ status: "pending",
402
+ nextPollMs: result.nextPollMs
403
+ });
404
+ }
405
+ }),
406
+ registerApiRoute("/web/config/providers/:provider/oauth/session/:sessionId", {
407
+ method: "DELETE",
408
+ requiresAuth: false,
409
+ handler: async (c) => {
410
+ const ctx = await resolveCredentialContext({
411
+ c: loose(c),
412
+ auth,
413
+ credentials: modelCredentials
414
+ });
415
+ if ("response" in ctx) return ctx.response;
416
+ const provider = c.req.param("provider");
417
+ const sessionId = c.req.param("sessionId");
418
+ if (await loadOwnedSession({
419
+ ctx,
420
+ provider,
421
+ sessionId
422
+ })) await (await sessionStore(ctx)).deleteLoginSession(sessionId);
423
+ return c.json({ ok: true });
424
+ }
425
+ }),
426
+ registerApiRoute("/web/config/providers/:provider/oauth", {
427
+ method: "DELETE",
428
+ requiresAuth: false,
429
+ handler: async (c) => {
430
+ const ctx = await resolveCredentialContext({
431
+ c: loose(c),
432
+ auth,
433
+ credentials: modelCredentials
434
+ });
435
+ if ("response" in ctx) return ctx.response;
436
+ const authProviderId = getAuthProviderId(c.req.param("provider"));
437
+ try {
438
+ if (ctx.mode === "tenant") {
439
+ await ctx.storage.removeCredential({
440
+ orgId: ctx.orgId,
441
+ userId: ctx.userId
442
+ }, authProviderId);
443
+ onCredentialsChanged({
444
+ orgId: ctx.orgId,
445
+ userId: ctx.userId
446
+ });
447
+ } else {
448
+ if (!authStorage) return c.json({ error: "Credential storage is not available" }, 503);
449
+ authStorage.remove(authProviderId);
450
+ }
451
+ return c.json({ ok: true });
452
+ } catch (error) {
453
+ return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
454
+ }
455
+ }
456
+ })
457
+ ];
458
+ }
459
+ };
460
+ //#endregion
461
+ export { OAuthRoutes };
462
+
463
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","names":[],"sources":["../../src/routes/oauth.ts"],"sourcesContent":["/**\n * Web OAuth sign-in routes for model providers (Settings › Providers).\n *\n * Wraps the SDK's step-based OAuth primitives (start/complete for Anthropic's\n * paste-code PKCE flow, start/poll for the Codex/Copilot/xAI device flows) in\n * HTTP routes. Flow state lives in login sessions — the `model-credentials`\n * domain's `oauth_login_sessions` table in tenant mode (any replica can\n * complete/poll), an in-memory store in local mode — so a flow can span\n * requests. Completed credentials are always **user-scoped** (plan tokens are\n * personal subscriptions) in tenant mode, or written to the file-backed\n * `AuthStorage` in local mode.\n *\n * Tokens never leave the server; responses only carry flow metadata (URLs,\n * user codes, poll delays).\n */\n\nimport { randomUUID } from 'node:crypto';\n\nimport { nextPollDelayMs } from '@mastra/code-sdk/auth/device-code';\nimport { completeAnthropicLogin, startAnthropicLogin } from '@mastra/code-sdk/auth/providers/anthropic';\nimport {\n copilotNextPollDelayMs,\n pollGitHubCopilotDeviceLogin,\n startGitHubCopilotDeviceLogin,\n} from '@mastra/code-sdk/auth/providers/github-copilot';\nimport type { CopilotDeviceLoginPending } from '@mastra/code-sdk/auth/providers/github-copilot';\nimport { pollCodexDeviceLogin, startCodexDeviceLogin } from '@mastra/code-sdk/auth/providers/openai-codex';\nimport type { CodexDeviceLoginPending } from '@mastra/code-sdk/auth/providers/openai-codex';\nimport { pollXAIDeviceLogin, startXAIDeviceLogin } from '@mastra/code-sdk/auth/providers/xai';\nimport type { XAIDeviceLoginPending } from '@mastra/code-sdk/auth/providers/xai';\nimport type { AuthStorage } from '@mastra/code-sdk/auth/storage';\nimport type { OAuthCredentials } from '@mastra/code-sdk/auth/types';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport { ModelCredentialsStorage } from '../storage/domains/credentials/base.js';\nimport type { LoginSessionKind, LoginSessionRow } from '../storage/domains/credentials/base.js';\nimport { getAuthProviderId, resolveCredentialContext } from './provider-credentials.js';\nimport type { CredentialContext } from './provider-credentials.js';\nimport { Route } from './route.js';\nimport type { RouteDependencies } from './route.js';\n\n/** Lifetime of a paste-code session (Anthropic gives no explicit expiry). */\nconst PASTE_CODE_TTL_MS = 10 * 60 * 1000;\n\ninterface OAuthFlowStart {\n url: string;\n userCode?: string;\n instructions: string;\n /** ms epoch after which the flow expires. */\n expiresAt: number;\n /** Delay before the first upstream poll (device-code flows only). */\n nextPollMs?: number;\n /** Serializable flow state persisted in the login session. */\n pending: Record<string, unknown>;\n}\n\ntype OAuthFlowPoll =\n | { status: 'complete'; credentials: OAuthCredentials }\n | { status: 'pending'; nextPollMs: number; pending?: Record<string, unknown> }\n | { status: 'failed'; error: string };\n\ninterface OAuthFlow {\n kind: LoginSessionKind;\n start(): Promise<OAuthFlowStart>;\n /** Paste-code flows: exchange the pasted code for credentials. Throws on bad input. */\n complete?(pending: Record<string, unknown>, code: string): Promise<OAuthCredentials>;\n /** Device-code flows: perform exactly one upstream poll. */\n poll?(pending: Record<string, unknown>): Promise<OAuthFlowPoll>;\n}\n\n/** Web OAuth flows keyed by *catalog* provider id (mirrors {@link WEB_OAUTH_FLOW_KINDS}). */\nconst OAUTH_FLOWS: Record<string, OAuthFlow> = {\n anthropic: {\n kind: 'paste-code',\n start: async () => {\n const { url, verifier } = await startAnthropicLogin();\n return {\n url,\n instructions: 'Open the link, authorize, then paste the code shown on the final page.',\n expiresAt: Date.now() + PASTE_CODE_TTL_MS,\n pending: { verifier },\n };\n },\n complete: (pending, code) => completeAnthropicLogin(code, String(pending.verifier ?? '')),\n },\n openai: {\n kind: 'device-code',\n start: async () => {\n const p = await startCodexDeviceLogin();\n return {\n url: p.url,\n userCode: p.userCode,\n instructions: p.instructions,\n expiresAt: p.deadlineAt,\n nextPollMs: p.intervalMs,\n pending: { ...p },\n };\n },\n poll: async pending => {\n const r = await pollCodexDeviceLogin(pending as unknown as CodexDeviceLoginPending);\n if (r.status === 'complete') return { status: 'complete', credentials: r.credentials };\n if (r.status === 'pending') return { status: 'pending', nextPollMs: r.nextPollMs };\n return { status: 'failed', error: r.error };\n },\n },\n 'github-copilot': {\n kind: 'device-code',\n start: async () => {\n const p = await startGitHubCopilotDeviceLogin();\n return {\n url: p.url,\n userCode: p.userCode,\n instructions: p.instructions,\n expiresAt: p.deadlineAt,\n nextPollMs: copilotNextPollDelayMs(p),\n pending: { ...p },\n };\n },\n poll: async pending => {\n const p = pending as unknown as CopilotDeviceLoginPending;\n // Web flows are always started against github.com (no Enterprise input).\n // Never let deserialized session state redirect server-side polling to\n // an arbitrary hostname.\n if (p.domain !== 'github.com' || p.enterpriseDomain !== undefined) {\n return { status: 'failed', error: 'Unsupported GitHub host' };\n }\n const r = await pollGitHubCopilotDeviceLogin(p);\n if (r.status === 'complete') return { status: 'complete', credentials: r.credentials };\n if (r.status === 'pending') return { status: 'pending', nextPollMs: r.nextPollMs, pending: { ...r.pending } };\n return { status: 'failed', error: r.error };\n },\n },\n xai: {\n kind: 'device-code',\n start: async () => {\n const p = await startXAIDeviceLogin();\n return {\n url: p.url,\n userCode: p.userCode,\n instructions: p.instructions,\n expiresAt: p.state.deadlineAt,\n nextPollMs: nextPollDelayMs(p.state),\n pending: { ...p },\n };\n },\n poll: async pending => {\n const r = await pollXAIDeviceLogin(pending as unknown as XAIDeviceLoginPending);\n if (r.status === 'complete') return { status: 'complete', credentials: r.credentials };\n if (r.status === 'pending') return { status: 'pending', nextPollMs: r.nextPollMs, pending: { ...r.pending } };\n return { status: 'failed', error: r.error };\n },\n },\n};\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\n/**\n * Local-mode login sessions. Flows in local mode are single-process, so a\n * process-local libsql `:memory:` database (which already handles TTL\n * cleanup) is sufficient.\n */\nlet localSessionsPromise: Promise<ModelCredentialsStorage> | undefined;\nfunction localSessions(): Promise<ModelCredentialsStorage> {\n localSessionsPromise ??= (async () => {\n const { LibSQLFactoryStorage } = await import('@mastra/libsql');\n const storage = new LibSQLFactoryStorage({ id: 'local-oauth-sessions', url: ':memory:' });\n const store = storage.registerDomain(new ModelCredentialsStorage());\n await storage.init();\n return store;\n })();\n return localSessionsPromise;\n}\nconst LOCAL_TENANT = { orgId: 'local', userId: 'local' } as const;\n\nasync function sessionStore(ctx: CredentialContext): Promise<ModelCredentialsStorage> {\n return ctx.mode === 'tenant' ? ctx.storage : localSessions();\n}\n\nfunction sessionTenant(ctx: CredentialContext): { orgId: string; userId: string } {\n return ctx.mode === 'tenant' ? { orgId: ctx.orgId, userId: ctx.userId } : LOCAL_TENANT;\n}\n\n/** Load a session and verify it belongs to the caller + provider (else undefined). */\nasync function loadOwnedSession({\n ctx,\n provider,\n sessionId,\n}: {\n ctx: CredentialContext;\n provider: string;\n sessionId: string;\n}): Promise<LoginSessionRow | undefined> {\n const session = await (await sessionStore(ctx)).getLoginSession(sessionId);\n if (!session) return undefined;\n const tenant = sessionTenant(ctx);\n if (session.orgId !== tenant.orgId || session.userId !== tenant.userId) return undefined;\n if (session.provider !== provider) return undefined;\n return session;\n}\n\n/** Persist completed OAuth credentials — always user-scoped in tenant mode. */\nasync function persistOAuthCredential({\n ctx,\n provider,\n credentials,\n authStorage,\n onCredentialsChanged,\n}: {\n ctx: CredentialContext;\n provider: string;\n credentials: OAuthCredentials;\n authStorage: AuthStorage | undefined;\n onCredentialsChanged: (tenant: { orgId: string; userId?: string }) => void;\n}): Promise<void> {\n const authProviderId = getAuthProviderId(provider);\n if (ctx.mode === 'tenant') {\n await ctx.storage.setCredential({ orgId: ctx.orgId, userId: ctx.userId }, authProviderId, {\n type: 'oauth',\n ...credentials,\n });\n onCredentialsChanged({ orgId: ctx.orgId, userId: ctx.userId });\n return;\n }\n if (!authStorage) throw new Error('Credential storage is not available');\n authStorage.set(authProviderId, { type: 'oauth', ...credentials });\n}\n\nasync function readJsonBody(c: Context): Promise<Record<string, unknown>> {\n try {\n const body = (await c.req.json()) as unknown;\n return body && typeof body === 'object' ? (body as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n}\n\nexport interface OAuthRoutesDeps extends RouteDependencies {\n /** File-backed credential store; used in local (no-auth) mode. */\n authStorage?: AuthStorage;\n /** Tenant credential domain handle; absent in local (no-DB) mode. */\n modelCredentials?: ModelCredentialsStorage;\n /** Notifies the host after tenant credentials change so caches can be dropped. */\n onCredentialsChanged?: (tenant: { orgId: string; userId?: string }) => void;\n}\n\n/**\n * Provider OAuth sign-in routes as Mastra `apiRoutes`:\n * - `POST /web/config/providers/:provider/oauth/start` — begin a sign-in flow\n * - `POST /web/config/providers/:provider/oauth/complete` — paste-code exchange\n * - `POST /web/config/providers/:provider/oauth/poll` — one device-code poll\n * - `DELETE /web/config/providers/:provider/oauth/session/:sessionId` — cancel a flow\n * - `DELETE /web/config/providers/:provider/oauth` — sign out (caller only)\n */\nexport class OAuthRoutes extends Route<OAuthRoutesDeps> {\n routes(): ApiRoute[] {\n const { auth, authStorage, modelCredentials } = this.deps;\n const onCredentialsChanged = this.deps.onCredentialsChanged ?? (() => {});\n\n return [\n registerApiRoute('/web/config/providers/:provider/oauth/start', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const ctx = await resolveCredentialContext({ c: loose(c), auth, credentials: modelCredentials });\n if ('response' in ctx) return ctx.response;\n\n const provider = c.req.param('provider');\n const flow = OAUTH_FLOWS[provider];\n if (!flow) {\n return c.json({ error: 'oauth_not_supported', message: `Provider does not support web sign-in` }, 404);\n }\n // Body may carry `{ mode }` for future multi-mode providers; each web\n // flow currently has exactly one mode, so an unknown mode is rejected.\n const body = await readJsonBody(loose(c));\n if (typeof body.mode === 'string' && body.mode !== flow.kind) {\n return c.json({ error: 'invalid_mode', message: `Unsupported mode for ${provider}: ${body.mode}` }, 400);\n }\n\n let started: OAuthFlowStart;\n try {\n started = await flow.start();\n } catch (error) {\n return c.json({ error: error instanceof Error ? error.message : String(error) }, 502);\n }\n\n const sessionId = randomUUID();\n const tenant = sessionTenant(ctx);\n await (\n await sessionStore(ctx)\n ).createLoginSession({\n sessionId,\n orgId: tenant.orgId,\n userId: tenant.userId,\n provider,\n kind: flow.kind,\n pending: started.pending,\n expiresAt: new Date(started.expiresAt),\n nextPollAt: started.nextPollMs != null ? new Date(Date.now() + started.nextPollMs) : null,\n });\n\n return c.json({\n sessionId,\n kind: flow.kind,\n url: started.url,\n ...(started.userCode ? { userCode: started.userCode } : {}),\n instructions: started.instructions,\n expiresAt: started.expiresAt,\n ...(started.nextPollMs != null ? { nextPollMs: started.nextPollMs } : {}),\n });\n },\n }),\n\n registerApiRoute('/web/config/providers/:provider/oauth/complete', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const ctx = await resolveCredentialContext({ c: loose(c), auth, credentials: modelCredentials });\n if ('response' in ctx) return ctx.response;\n\n const provider = c.req.param('provider');\n const flow = OAUTH_FLOWS[provider];\n if (!flow?.complete) return c.json({ error: 'oauth_not_supported' }, 404);\n\n const body = await readJsonBody(loose(c));\n const sessionId = typeof body.sessionId === 'string' ? body.sessionId : '';\n const code = typeof body.code === 'string' ? body.code.trim() : '';\n if (!sessionId || !code) return c.json({ error: 'Missing required fields: sessionId, code' }, 400);\n\n const session = await loadOwnedSession({ ctx, provider, sessionId });\n if (!session) return c.json({ error: 'session_not_found' }, 404);\n if (session.kind !== 'paste-code') return c.json({ error: 'wrong_session_kind' }, 400);\n\n const claimed = await (\n await sessionStore(ctx)\n ).claimLoginSession(sessionId, {\n orgId: session.orgId,\n userId: session.userId,\n provider,\n kind: 'paste-code',\n });\n if (!claimed) return c.json({ error: 'oauth_in_progress' }, 409);\n\n let credentials: OAuthCredentials;\n try {\n credentials = await flow.complete(claimed.pending, code);\n } catch (error) {\n await (await sessionStore(ctx)).touchLoginSession(sessionId, { nextPollAt: null });\n return c.json({ error: error instanceof Error ? error.message : String(error) }, 400);\n }\n\n await persistOAuthCredential({ ctx, provider, credentials, authStorage, onCredentialsChanged });\n await (await sessionStore(ctx)).deleteLoginSession(sessionId);\n return c.json({ status: 'complete', ok: true });\n },\n }),\n\n registerApiRoute('/web/config/providers/:provider/oauth/poll', {\n method: 'POST',\n requiresAuth: false,\n handler: async c => {\n const ctx = await resolveCredentialContext({ c: loose(c), auth, credentials: modelCredentials });\n if ('response' in ctx) return ctx.response;\n\n const provider = c.req.param('provider');\n const flow = OAUTH_FLOWS[provider];\n if (!flow?.poll) return c.json({ error: 'oauth_not_supported' }, 404);\n\n const body = await readJsonBody(loose(c));\n const sessionId = typeof body.sessionId === 'string' ? body.sessionId : '';\n if (!sessionId) return c.json({ error: 'Missing required field: sessionId' }, 400);\n\n const session = await loadOwnedSession({ ctx, provider, sessionId });\n if (!session) return c.json({ error: 'session_not_found' }, 404);\n if (session.kind !== 'device-code') return c.json({ error: 'wrong_session_kind' }, 400);\n\n // Server-side rate limit: at most one upstream poll per interval,\n // regardless of how eagerly the client calls this route.\n const now = Date.now();\n const nextPollAt = session.nextPollAt?.getTime();\n if (nextPollAt != null && now < nextPollAt) {\n return c.json({ status: 'pending', nextPollMs: nextPollAt - now });\n }\n\n const claimed = await (\n await sessionStore(ctx)\n ).claimLoginSession(sessionId, {\n orgId: session.orgId,\n userId: session.userId,\n provider,\n kind: 'device-code',\n });\n if (!claimed) return c.json({ status: 'pending', nextPollMs: 250 });\n\n let result: OAuthFlowPoll;\n try {\n result = await flow.poll(claimed.pending);\n } catch (error) {\n await (await sessionStore(ctx)).touchLoginSession(sessionId, { nextPollAt: null });\n throw error;\n }\n\n if (result.status === 'complete') {\n await persistOAuthCredential({\n ctx,\n provider,\n credentials: result.credentials,\n authStorage,\n onCredentialsChanged,\n });\n await (await sessionStore(ctx)).deleteLoginSession(sessionId);\n return c.json({ status: 'complete', ok: true });\n }\n\n if (result.status === 'failed') {\n await (await sessionStore(ctx)).deleteLoginSession(sessionId);\n return c.json({ status: 'failed', error: result.error });\n }\n\n await (\n await sessionStore(ctx)\n ).touchLoginSession(sessionId, {\n ...(result.pending ? { pending: result.pending } : {}),\n nextPollAt: new Date(now + result.nextPollMs),\n });\n return c.json({ status: 'pending', nextPollMs: result.nextPollMs });\n },\n }),\n\n registerApiRoute('/web/config/providers/:provider/oauth/session/:sessionId', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async c => {\n const ctx = await resolveCredentialContext({ c: loose(c), auth, credentials: modelCredentials });\n if ('response' in ctx) return ctx.response;\n\n const provider = c.req.param('provider');\n const sessionId = c.req.param('sessionId');\n const session = await loadOwnedSession({ ctx, provider, sessionId });\n if (session) await (await sessionStore(ctx)).deleteLoginSession(sessionId);\n return c.json({ ok: true });\n },\n }),\n\n registerApiRoute('/web/config/providers/:provider/oauth', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async c => {\n const ctx = await resolveCredentialContext({ c: loose(c), auth, credentials: modelCredentials });\n if ('response' in ctx) return ctx.response;\n\n const provider = c.req.param('provider');\n const authProviderId = getAuthProviderId(provider);\n try {\n if (ctx.mode === 'tenant') {\n // Caller's credential only — never touches org rows or other users.\n await ctx.storage.removeCredential({ orgId: ctx.orgId, userId: ctx.userId }, authProviderId);\n onCredentialsChanged({ orgId: ctx.orgId, userId: ctx.userId });\n } else {\n if (!authStorage) return c.json({ error: 'Credential storage is not available' }, 503);\n authStorage.remove(authProviderId);\n }\n return c.json({ ok: true });\n } catch (error) {\n return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);\n }\n },\n }),\n ];\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { ApiRoute } from '@mastra/core/server';
2
+ import type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';
3
+ import type { SourceControlStorage } from '../storage/domains/source-control/base.js';
4
+ import type { RouteDependencies } from './route.js';
5
+ import { Route } from './route.js';
6
+ export interface ProjectRoutesDeps extends RouteDependencies {
7
+ /** Factory projects domain backing the CRUD surface. */
8
+ projects: FactoryProjectsStorage;
9
+ /** Source-control domain the connection/repository routes fan out over. */
10
+ sourceControl: SourceControlStorage;
11
+ /** Integration ids allowed as source-control connection targets. */
12
+ versionControlIntegrationIds?: string[];
13
+ }
14
+ export declare class ProjectRoutes extends Route<ProjectRoutesDeps> {
15
+ #private;
16
+ constructor(deps: ProjectRoutesDeps);
17
+ routes(): ApiRoute[];
18
+ }
19
+ //# sourceMappingURL=projects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/routes/projects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAEV,oBAAoB,EAGrB,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AA2InC,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D,wDAAwD;IACxD,QAAQ,EAAE,sBAAsB,CAAC;IACjC,2EAA2E;IAC3E,aAAa,EAAE,oBAAoB,CAAC;IACpC,oEAAoE;IACpE,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;CACzC;AAED,qBAAa,aAAc,SAAQ,KAAK,CAAC,iBAAiB,CAAC;;gBAG7C,IAAI,EAAE,iBAAiB;IA8DnC,MAAM,IAAI,QAAQ,EAAE;CAoOrB"}