@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,483 @@
1
+ import { PlatformApiClient, PlatformApiError, logPlatformInfo, logPlatformWarn, platformApiClientConfigFromEnv } from "../api-client.js";
2
+ import { buildLinearRoutes } from "../../linear/routes.js";
3
+ import { attachLinearRules } from "../../linear/rules.js";
4
+ import { buildLinearAgentTools } from "../../linear/agent-tools.js";
5
+ import { registerApiRoute } from "@mastra/core/server";
6
+ //#region src/integrations/platform/linear/integration.ts
7
+ const API_PREFIX = "/v1/server/linear";
8
+ const PAGE_SIZE = 30;
9
+ const MAX_REFERENCE_PAGES = 20;
10
+ const MAX_COMMENT_PAGES = 20;
11
+ const PLATFORM_MANAGED_CONNECTION_TOKEN = "platform-managed";
12
+ function loose(c) {
13
+ return c;
14
+ }
15
+ function routeBaseUrl(ctx, requestUrl) {
16
+ return (ctx.baseUrl || new URL(requestUrl).origin).replace(/\/+$/, "");
17
+ }
18
+ var PlatformLinearIntegration = class {
19
+ id = "linear";
20
+ #client;
21
+ #endpointHost;
22
+ #projects;
23
+ #auth;
24
+ /**
25
+ * Per-instance workflow-state cache keyed by `${workspaceId}:${teamId}`. Scoped to the process
26
+ * lifetime; not shared across requests intentionally to keep failure modes simple (stale states
27
+ * clear on process restart). Populated lazily on first `updateIssue` per team.
28
+ */
29
+ #workflowStatesByTeam = /* @__PURE__ */ new Map();
30
+ intake = {
31
+ resolveIntakeDispatch: (input) => this.#resolveIntakeDispatch(input),
32
+ listSources: async () => {
33
+ return (await this.#listProjectSources()).map(({ workspace, project }) => ({
34
+ id: encodeSourceId(workspace.linearWorkspaceId, project.id),
35
+ name: project.name,
36
+ type: "project",
37
+ metadata: {
38
+ workspaceId: workspace.linearWorkspaceId,
39
+ workspaceName: workspace.linearWorkspaceName,
40
+ workspaceUrlKey: workspace.urlKey,
41
+ state: project.state,
42
+ teams: project.teams
43
+ }
44
+ }));
45
+ },
46
+ listItems: async ({ sourceIds, cursor }) => {
47
+ const result = await this.#listIssues(sourceIds, cursor);
48
+ return {
49
+ items: result.issues.map(({ issue, source }) => ({
50
+ source: {
51
+ type: "issue",
52
+ externalId: issue.id,
53
+ url: issue.url
54
+ },
55
+ sourceId: encodeSourceId(source.workspace.linearWorkspaceId, source.project.id),
56
+ title: issue.title,
57
+ status: issue.state.name,
58
+ labels: issue.labels.map((label) => label.name),
59
+ assignee: issue.assignee?.displayName ?? issue.assignee?.name ?? null,
60
+ createdAt: issue.createdAt,
61
+ updatedAt: issue.updatedAt,
62
+ metadata: {
63
+ identifier: issue.identifier,
64
+ workspaceId: source.workspace.linearWorkspaceId,
65
+ workspaceName: source.workspace.linearWorkspaceName,
66
+ projectId: source.project.id,
67
+ projectName: source.project.name,
68
+ team: issue.team.key,
69
+ priority: issue.priorityLabel
70
+ }
71
+ })),
72
+ nextCursor: result.nextCursor
73
+ };
74
+ },
75
+ listIssues: async ({ connection, sourceIds, labels, cursor }) => {
76
+ requireLinearConnection(connection);
77
+ const result = await this.#listIssues(sourceIds, cursor, labels);
78
+ return {
79
+ issues: result.issues.map(({ issue }) => parseIssue(issue)),
80
+ nextCursor: result.nextCursor
81
+ };
82
+ },
83
+ getIssue: async ({ connection, sourceId, issueId }) => {
84
+ requireLinearConnection(connection);
85
+ const located = await this.#findIssue(sourceId, issueId);
86
+ if (!located) return null;
87
+ const comments = await this.#loadComments(located.workspaceId, issueId, located.issue.comments);
88
+ return parseIssueDetail(located.issue, comments);
89
+ },
90
+ createComment: async ({ connection, sourceId, issueId, body }) => {
91
+ requireLinearConnection(connection);
92
+ const workspaceId = await this.#resolveWorkspaceForIssue(sourceId, issueId);
93
+ if (!workspaceId) return null;
94
+ try {
95
+ const comment = await this.#client.request("POST", `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}/comments`, { body });
96
+ return {
97
+ id: comment.id,
98
+ url: comment.url
99
+ };
100
+ } catch (error) {
101
+ if (isNotFound(error)) return null;
102
+ throw error;
103
+ }
104
+ },
105
+ updateIssue: async (input) => {
106
+ requireLinearConnection(input.connection);
107
+ const located = await this.#findIssue(input.sourceId, input.issueId);
108
+ if (!located) return null;
109
+ const { workspaceId, issue } = located;
110
+ const target = input.state;
111
+ if (target.kind === "byType" && issue.state.type === target.stateType) return parseIssue(issue);
112
+ if (target.kind === "byName" && issue.state.name.toLowerCase() === target.name.toLowerCase()) return parseIssue(issue);
113
+ let states;
114
+ try {
115
+ states = await this.#listWorkflowStates(workspaceId, issue.team.id);
116
+ } catch (error) {
117
+ if (isNotFound(error)) {
118
+ logPlatformWarn("Platform Linear: workflow-states endpoint not available; skipping updateIssue.", {
119
+ workspaceId,
120
+ teamId: issue.team.id
121
+ });
122
+ return null;
123
+ }
124
+ throw error;
125
+ }
126
+ let matched;
127
+ if (target.kind === "byType") matched = states.slice().sort((a, b) => a.position - b.position).find((s) => s.type === target.stateType);
128
+ else {
129
+ const wanted = target.name.toLowerCase();
130
+ matched = states.find((s) => s.name.toLowerCase() === wanted);
131
+ }
132
+ if (!matched) {
133
+ logPlatformWarn("Platform Linear: no workflow state matched target; skipping updateIssue.", {
134
+ workspaceId,
135
+ teamId: issue.team.id,
136
+ target
137
+ });
138
+ return null;
139
+ }
140
+ try {
141
+ await this.#client.request("PATCH", `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(input.issueId)}`, { stateId: matched.id });
142
+ } catch (error) {
143
+ if (isNotFound(error)) return null;
144
+ throw error;
145
+ }
146
+ const refreshed = await this.#findIssue(input.sourceId, input.issueId);
147
+ return refreshed ? parseIssue(refreshed.issue) : null;
148
+ }
149
+ };
150
+ constructor() {
151
+ const config = platformApiClientConfigFromEnv();
152
+ this.#client = new PlatformApiClient(config);
153
+ this.#endpointHost = new URL(config.baseUrl).host;
154
+ }
155
+ get storage() {
156
+ const now = /* @__PURE__ */ new Date();
157
+ return {
158
+ integrationId: this.id,
159
+ connections: { get: async (orgId) => ({
160
+ id: `platform-linear:${orgId}`,
161
+ orgId,
162
+ userId: null,
163
+ data: {
164
+ accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN,
165
+ refreshToken: null,
166
+ expiresAtMs: null,
167
+ scope: "read,comments:create",
168
+ workspaceName: null,
169
+ workspaceUrlKey: null
170
+ },
171
+ metadata: {},
172
+ createdAt: now,
173
+ updatedAt: now
174
+ }) }
175
+ };
176
+ }
177
+ get projects() {
178
+ if (!this.#projects) throw new Error("PlatformLinearIntegration projects storage has not been initialized.");
179
+ return this.#projects;
180
+ }
181
+ initialize({ projects, auth }) {
182
+ this.#projects = projects;
183
+ this.#auth = auth;
184
+ logPlatformInfo("Platform Linear integration initialized", { endpointHost: this.#endpointHost });
185
+ }
186
+ get authEnabled() {
187
+ return this.#auth?.enabled() ?? false;
188
+ }
189
+ async resolveOrgId(resourceId) {
190
+ try {
191
+ return (await this.projects.getById({ id: resourceId }))?.orgId ?? null;
192
+ } catch {
193
+ return null;
194
+ }
195
+ }
196
+ async loadConnection(orgId) {
197
+ const workspace = (await this.#listWorkspaces())[0];
198
+ if (!workspace) return null;
199
+ const now = /* @__PURE__ */ new Date();
200
+ return {
201
+ id: `platform-linear:${orgId}`,
202
+ orgId,
203
+ userId: null,
204
+ accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN,
205
+ scope: "read,comments:create",
206
+ refreshToken: null,
207
+ expiresAt: null,
208
+ workspaceName: workspace.linearWorkspaceName,
209
+ workspaceUrlKey: workspace.urlKey,
210
+ createdAt: now,
211
+ updatedAt: now
212
+ };
213
+ }
214
+ async getFreshAccessToken(_connection) {
215
+ return PLATFORM_MANAGED_CONNECTION_TOKEN;
216
+ }
217
+ /**
218
+ * Background-dispatch context: platform-managed connection token when the
219
+ * org has a connected workspace. Linear work items store the issue UUID
220
+ * directly as `externalId`.
221
+ */
222
+ async #resolveIntakeDispatch({ orgId, externalSource }) {
223
+ if (externalSource.type !== "issue") return null;
224
+ if (!await this.loadConnection(orgId)) return null;
225
+ return {
226
+ connection: {
227
+ type: "oauth",
228
+ accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN
229
+ },
230
+ issueId: externalSource.externalId
231
+ };
232
+ }
233
+ canPostComments(connection) {
234
+ return (connection.scope ?? "").split(/[\s,]+/).filter(Boolean).some((scope) => scope === "comments:create" || scope === "write" || scope === "admin");
235
+ }
236
+ async checkConnection(orgId) {
237
+ const connection = await this.loadConnection(orgId);
238
+ return {
239
+ connected: connection !== null,
240
+ canComment: connection !== null && this.canPostComments(connection),
241
+ checkedAt: Date.now()
242
+ };
243
+ }
244
+ routes(ctx) {
245
+ return [this.#connectRoute(ctx), ...buildLinearRoutes({
246
+ auth: ctx.auth,
247
+ linear: this,
248
+ stateSigner: ctx.stateSigner,
249
+ baseUrl: ctx.baseUrl,
250
+ intake: ctx.storage.intake,
251
+ ingestFactoryIssues: attachLinearRules(ctx)
252
+ }).filter((route) => !route.path.startsWith("/auth/linear/"))];
253
+ }
254
+ #connectRoute(ctx) {
255
+ return registerApiRoute("/auth/linear/connect", {
256
+ method: "GET",
257
+ requiresAuth: false,
258
+ handler: async (c) => {
259
+ await ctx.auth.ensureUser(loose(c));
260
+ const tenant = ctx.auth.tenant(loose(c));
261
+ if (!tenant?.orgId) return c.json({ error: "unauthorized" }, 401);
262
+ const returnTo = c.req.query("return_to") || "/";
263
+ const originator = routeBaseUrl(ctx, c.req.url);
264
+ logPlatformInfo("Starting Platform Linear connect flow", {
265
+ orgId: tenant.orgId,
266
+ returnTo,
267
+ originator
268
+ });
269
+ const query = new URLSearchParams({
270
+ return_to: returnTo,
271
+ originator
272
+ });
273
+ const location = await this.#client.requestRedirect("GET", `${API_PREFIX}/authorize?${query}`);
274
+ return c.redirect(location);
275
+ }
276
+ });
277
+ }
278
+ async agentTools({ requestContext }) {
279
+ return buildLinearAgentTools({
280
+ requestContext,
281
+ linear: this
282
+ });
283
+ }
284
+ diagnostics() {
285
+ return {
286
+ mode: "platform",
287
+ endpointHost: this.#endpointHost
288
+ };
289
+ }
290
+ async listProjects() {
291
+ return (await this.#listProjectSources()).map(({ workspace, project }) => ({
292
+ ...project,
293
+ id: encodeSourceId(workspace.linearWorkspaceId, project.id),
294
+ workspaceId: workspace.linearWorkspaceId
295
+ }));
296
+ }
297
+ async #listProjectSources() {
298
+ const workspaces = await this.#listWorkspaces();
299
+ return (await Promise.all(workspaces.map(async (workspace) => {
300
+ const projects = [];
301
+ let after;
302
+ for (let page = 0; page < MAX_REFERENCE_PAGES; page += 1) {
303
+ const query = new URLSearchParams({ first: "200" });
304
+ if (after) query.set("after", after);
305
+ const result = await this.#client.request("GET", `${API_PREFIX}/workspaces/${encodeURIComponent(workspace.linearWorkspaceId)}/projects?${query}`);
306
+ projects.push(...result.projects);
307
+ if (!result.pageInfo.hasNextPage || !result.pageInfo.endCursor) break;
308
+ after = result.pageInfo.endCursor;
309
+ }
310
+ return projects.map((project) => ({
311
+ workspace,
312
+ project
313
+ }));
314
+ }))).flat();
315
+ }
316
+ async #listWorkspaces() {
317
+ return (await this.#client.request("GET", `${API_PREFIX}/workspaces`)).workspaces.filter((workspace) => workspace.connected);
318
+ }
319
+ async #listIssues(sourceIds, cursor, labels) {
320
+ if (sourceIds.length === 0) return {
321
+ issues: [],
322
+ nextCursor: null
323
+ };
324
+ const sources = await this.#listProjectSources();
325
+ const sourceMap = new Map(sources.map((source) => [encodeSourceId(source.workspace.linearWorkspaceId, source.project.id), source]));
326
+ const selected = sourceIds.map((sourceId) => sourceMap.get(sourceId)).filter((source) => !!source);
327
+ const cursors = decodeCursor(cursor, sourceIds);
328
+ const normalizedLabels = normalizeLabels(labels);
329
+ const nextState = {};
330
+ let hasNextPage = false;
331
+ return {
332
+ issues: (await Promise.all(selected.map(async (source) => {
333
+ const sourceId = encodeSourceId(source.workspace.linearWorkspaceId, source.project.id);
334
+ if (cursors[sourceId] === null) {
335
+ nextState[sourceId] = null;
336
+ return [];
337
+ }
338
+ const query = new URLSearchParams({
339
+ first: String(PAGE_SIZE),
340
+ projectIds: source.project.id,
341
+ stateType: "triage,backlog,unstarted,started",
342
+ orderBy: "updatedAt"
343
+ });
344
+ const after = cursors[sourceId];
345
+ if (after) query.set("after", after);
346
+ const result = await this.#client.request("GET", `${API_PREFIX}/workspaces/${encodeURIComponent(source.workspace.linearWorkspaceId)}/issues?${query}`);
347
+ const next = result.pageInfo.hasNextPage ? result.pageInfo.endCursor : null;
348
+ nextState[sourceId] = next;
349
+ hasNextPage ||= next !== null;
350
+ return result.issues.filter((issue) => normalizedLabels.length === 0 || issue.labels.some((label) => normalizedLabels.includes(label.name))).map((issue) => ({
351
+ issue,
352
+ source
353
+ }));
354
+ }))).flat(),
355
+ nextCursor: hasNextPage ? encodeCursor(nextState, sourceIds) : null
356
+ };
357
+ }
358
+ async #findIssue(sourceId, issueId) {
359
+ const workspaceIds = await this.#candidateWorkspaceIds(sourceId);
360
+ for (const workspaceId of workspaceIds) try {
361
+ return {
362
+ workspaceId,
363
+ issue: await this.#client.request("GET", `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}?include=comments`)
364
+ };
365
+ } catch (error) {
366
+ if (!isNotFound(error)) throw error;
367
+ }
368
+ return null;
369
+ }
370
+ async #resolveWorkspaceForIssue(sourceId, issueId) {
371
+ const workspaceIds = await this.#candidateWorkspaceIds(sourceId);
372
+ if (workspaceIds.length === 1) return workspaceIds[0];
373
+ for (const workspaceId of workspaceIds) try {
374
+ await this.#client.request("GET", `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}`);
375
+ return workspaceId;
376
+ } catch (error) {
377
+ if (!isNotFound(error)) throw error;
378
+ }
379
+ return null;
380
+ }
381
+ async #listWorkflowStates(workspaceId, teamId) {
382
+ const cacheKey = `${workspaceId}:${teamId}`;
383
+ const cached = this.#workflowStatesByTeam.get(cacheKey);
384
+ if (cached) return cached;
385
+ const result = await this.#client.request("GET", `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/workflow-states?teamId=${encodeURIComponent(teamId)}&first=100`);
386
+ this.#workflowStatesByTeam.set(cacheKey, result.workflowStates);
387
+ return result.workflowStates;
388
+ }
389
+ async #candidateWorkspaceIds(sourceId) {
390
+ if (sourceId) return [decodeSourceId(sourceId).workspaceId];
391
+ return (await this.#listWorkspaces()).map((workspace) => workspace.linearWorkspaceId);
392
+ }
393
+ async #loadComments(workspaceId, issueId, embedded) {
394
+ const comments = [...embedded?.nodes ?? []];
395
+ let pageInfo = embedded?.pageInfo;
396
+ let page = 0;
397
+ while (pageInfo?.hasNextPage && pageInfo.endCursor && page < MAX_COMMENT_PAGES) {
398
+ const result = await this.#client.request("GET", `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}/comments?first=200&after=${encodeURIComponent(pageInfo.endCursor)}`);
399
+ comments.push(...result.comments);
400
+ pageInfo = result.pageInfo;
401
+ page += 1;
402
+ }
403
+ return comments;
404
+ }
405
+ };
406
+ function parseIssue(issue) {
407
+ return {
408
+ id: issue.id,
409
+ identifier: issue.identifier,
410
+ title: issue.title,
411
+ url: issue.url,
412
+ author: issue.creator?.displayName ?? issue.creator?.name ?? null,
413
+ state: issue.state.name,
414
+ stateType: issue.state.type,
415
+ priority: issue.priorityLabel,
416
+ assignee: issue.assignee?.displayName ?? issue.assignee?.name ?? null,
417
+ source: issue.team.key,
418
+ labels: issue.labels.map((label) => label.name),
419
+ commentCount: null,
420
+ createdAt: issue.createdAt,
421
+ updatedAt: issue.updatedAt
422
+ };
423
+ }
424
+ function parseIssueDetail(issue, comments) {
425
+ return {
426
+ ...parseIssue(issue),
427
+ commentCount: comments.length,
428
+ description: issue.description?.trim() ? issue.description : null,
429
+ comments: comments.map((comment) => ({
430
+ author: comment.user?.displayName ?? comment.user?.name ?? null,
431
+ body: comment.body,
432
+ createdAt: comment.createdAt
433
+ }))
434
+ };
435
+ }
436
+ function encodeSourceId(workspaceId, projectId) {
437
+ return `linear-project:${Buffer.from(JSON.stringify({
438
+ workspaceId,
439
+ projectId
440
+ })).toString("base64url")}`;
441
+ }
442
+ function decodeSourceId(sourceId) {
443
+ if (!sourceId.startsWith("linear-project:")) throw new Error("Linear project source id is invalid.");
444
+ try {
445
+ const parsed = JSON.parse(Buffer.from(sourceId.slice(15), "base64url").toString("utf8"));
446
+ if (typeof parsed.workspaceId !== "string" || !parsed.workspaceId) throw new Error();
447
+ if (typeof parsed.projectId !== "string" || !parsed.projectId) throw new Error();
448
+ return {
449
+ workspaceId: parsed.workspaceId,
450
+ projectId: parsed.projectId
451
+ };
452
+ } catch {
453
+ throw new Error("Linear project source id is invalid.");
454
+ }
455
+ }
456
+ function normalizeLabels(labels) {
457
+ return [...new Set((labels ?? []).map((label) => label.trim()).filter(Boolean))];
458
+ }
459
+ function decodeCursor(cursor, sourceIds) {
460
+ if (!cursor) return {};
461
+ if (sourceIds.length === 1) return { [sourceIds[0]]: cursor };
462
+ try {
463
+ const parsed = JSON.parse(cursor);
464
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error();
465
+ return parsed;
466
+ } catch {
467
+ throw new Error("Linear cursor is invalid.");
468
+ }
469
+ }
470
+ function encodeCursor(state, sourceIds) {
471
+ if (sourceIds.length === 1) return state[sourceIds[0]];
472
+ return JSON.stringify(state);
473
+ }
474
+ function requireLinearConnection(connection) {
475
+ if (connection.type !== "oauth") throw new Error("Linear capabilities require an OAuth connection.");
476
+ }
477
+ function isNotFound(error) {
478
+ return error instanceof PlatformApiError && error.status === 404;
479
+ }
480
+ //#endregion
481
+ export { PlatformLinearIntegration };
482
+
483
+ //# sourceMappingURL=integration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.js","names":["#client","#endpointHost","#workflowStatesByTeam","#resolveIntakeDispatch","#listProjectSources","#listIssues","#findIssue","#loadComments","#resolveWorkspaceForIssue","#listWorkflowStates","#projects","#auth","#listWorkspaces","#connectRoute","#candidateWorkspaceIds"],"sources":["../../../../src/integrations/platform/linear/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { IntegrationConnection } from '../../../capabilities/connection.js';\nimport type { Intake, IntakeIssue, IntakeIssueDetail, UpdateIntakeIssueInput } from '../../../capabilities/intake.js';\nimport type { RouteAuth } from '../../../routes/route.js';\nimport type { FactoryProjectsStorage } from '../../../storage/domains/projects/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../../base.js';\nimport { buildLinearAgentTools } from '../../linear/agent-tools.js';\nimport type { LinearConnectionCheck, LinearIntegration } from '../../linear/integration.js';\nimport { buildLinearRoutes } from '../../linear/routes.js';\nimport { attachLinearRules } from '../../linear/rules.js';\nimport type { LinearConnectionData, LinearConnectionRow, LinearStorageHandle } from '../../linear/storage.js';\nimport {\n logPlatformInfo,\n logPlatformWarn,\n PlatformApiClient,\n PlatformApiError,\n platformApiClientConfigFromEnv,\n} from '../api-client.js';\n\ntype PageInfo = { hasNextPage: boolean; endCursor: string | null };\ntype LinearUser = {\n id: string;\n name: string;\n displayName: string;\n email: string | null;\n avatarUrl: string | null;\n};\ntype LinearIssue = {\n id: string;\n identifier: string;\n number: number;\n title: string;\n description: string | null;\n url: string;\n priority: number;\n priorityLabel: string;\n labels: Array<{ id: string; name: string }>;\n state: { id: string; name: string; type: string };\n team: { id: string; key: string; name: string };\n assignee: LinearUser | null;\n creator: LinearUser | null;\n createdAt: string;\n updatedAt: string;\n archivedAt: string | null;\n};\ntype LinearComment = {\n id: string;\n body: string;\n url: string;\n issue: { id: string; identifier: string };\n user: LinearUser | null;\n parent: { id: string } | null;\n createdAt: string;\n updatedAt: string;\n};\ntype LinearWorkspace = {\n linearWorkspaceId: string;\n linearWorkspaceName: string;\n urlKey: string | null;\n connected: boolean;\n};\ntype LinearProject = {\n id: string;\n name: string;\n state: string;\n teams: Array<{ id: string; key: string; name: string }>;\n};\ntype ProjectSource = { workspace: LinearWorkspace; project: LinearProject };\ntype LinearWorkflowState = {\n id: string;\n name: string;\n type: string;\n position: number;\n teamId: string;\n};\n\nconst API_PREFIX = '/v1/server/linear';\nconst PAGE_SIZE = 30;\nconst MAX_REFERENCE_PAGES = 20;\nconst MAX_COMMENT_PAGES = 20;\nconst PLATFORM_MANAGED_CONNECTION_TOKEN = 'platform-managed';\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nfunction routeBaseUrl(ctx: IntegrationContext, requestUrl: string): string {\n return (ctx.baseUrl || new URL(requestUrl).origin).replace(/\\/+$/, '');\n}\n\nexport class PlatformLinearIntegration implements FactoryIntegration {\n readonly id = 'linear';\n readonly #client: PlatformApiClient;\n readonly #endpointHost: string;\n #projects: FactoryProjectsStorage | undefined;\n #auth: RouteAuth | undefined;\n /**\n * Per-instance workflow-state cache keyed by `${workspaceId}:${teamId}`. Scoped to the process\n * lifetime; not shared across requests intentionally to keep failure modes simple (stale states\n * clear on process restart). Populated lazily on first `updateIssue` per team.\n */\n readonly #workflowStatesByTeam = new Map<string, LinearWorkflowState[]>();\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: async () => {\n const sources = await this.#listProjectSources();\n return sources.map(({ workspace, project }) => ({\n id: encodeSourceId(workspace.linearWorkspaceId, project.id),\n name: project.name,\n type: 'project',\n metadata: {\n workspaceId: workspace.linearWorkspaceId,\n workspaceName: workspace.linearWorkspaceName,\n workspaceUrlKey: workspace.urlKey,\n state: project.state,\n teams: project.teams,\n },\n }));\n },\n listItems: async ({ sourceIds, cursor }) => {\n const result = await this.#listIssues(sourceIds, cursor);\n return {\n items: result.issues.map(({ issue, source }) => ({\n source: { type: 'issue', externalId: issue.id, url: issue.url },\n sourceId: encodeSourceId(source.workspace.linearWorkspaceId, source.project.id),\n title: issue.title,\n status: issue.state.name,\n labels: issue.labels.map(label => label.name),\n assignee: issue.assignee?.displayName ?? issue.assignee?.name ?? null,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n metadata: {\n identifier: issue.identifier,\n workspaceId: source.workspace.linearWorkspaceId,\n workspaceName: source.workspace.linearWorkspaceName,\n projectId: source.project.id,\n projectName: source.project.name,\n team: issue.team.key,\n priority: issue.priorityLabel,\n },\n })),\n nextCursor: result.nextCursor,\n };\n },\n listIssues: async ({ connection, sourceIds, labels, cursor }) => {\n requireLinearConnection(connection);\n const result = await this.#listIssues(sourceIds, cursor, labels);\n return {\n issues: result.issues.map(({ issue }) => parseIssue(issue)),\n nextCursor: result.nextCursor,\n };\n },\n getIssue: async ({ connection, sourceId, issueId }) => {\n requireLinearConnection(connection);\n const located = await this.#findIssue(sourceId, issueId);\n if (!located) return null;\n const comments = await this.#loadComments(located.workspaceId, issueId, located.issue.comments);\n return parseIssueDetail(located.issue, comments);\n },\n createComment: async ({ connection, sourceId, issueId, body }) => {\n requireLinearConnection(connection);\n const workspaceId = await this.#resolveWorkspaceForIssue(sourceId, issueId);\n if (!workspaceId) return null;\n try {\n const comment = await this.#client.request<LinearComment>(\n 'POST',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}/comments`,\n { body },\n );\n return { id: comment.id, url: comment.url };\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n },\n updateIssue: async (input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> => {\n requireLinearConnection(input.connection);\n const located = await this.#findIssue(input.sourceId, input.issueId);\n if (!located) return null;\n const { workspaceId, issue } = located;\n\n const target = input.state;\n // Idempotency: skip the write entirely if the issue is already at the target state.\n if (target.kind === 'byType' && issue.state.type === target.stateType) {\n return parseIssue(issue);\n }\n if (target.kind === 'byName' && issue.state.name.toLowerCase() === target.name.toLowerCase()) {\n return parseIssue(issue);\n }\n\n let states: LinearWorkflowState[];\n try {\n states = await this.#listWorkflowStates(workspaceId, issue.team.id);\n } catch (error) {\n if (isNotFound(error)) {\n // Platform companion endpoint not deployed yet — degrade to policy skip so PR 1\n // is standalone-mergeable ahead of the platform-side workflow-states route landing.\n logPlatformWarn('Platform Linear: workflow-states endpoint not available; skipping updateIssue.', {\n workspaceId,\n teamId: issue.team.id,\n });\n return null;\n }\n throw error;\n }\n let matched: LinearWorkflowState | undefined;\n if (target.kind === 'byType') {\n matched = states\n .slice()\n .sort((a, b) => a.position - b.position)\n .find(s => s.type === target.stateType);\n } else {\n const wanted = target.name.toLowerCase();\n matched = states.find(s => s.name.toLowerCase() === wanted);\n }\n if (!matched) {\n logPlatformWarn('Platform Linear: no workflow state matched target; skipping updateIssue.', {\n workspaceId,\n teamId: issue.team.id,\n target,\n });\n return null;\n }\n\n try {\n await this.#client.request<LinearIssue>(\n 'PATCH',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(input.issueId)}`,\n { stateId: matched.id },\n );\n } catch (error) {\n if (isNotFound(error)) return null;\n throw error;\n }\n\n const refreshed = await this.#findIssue(input.sourceId, input.issueId);\n return refreshed ? parseIssue(refreshed.issue) : null;\n },\n };\n\n constructor() {\n const config = platformApiClientConfigFromEnv();\n this.#client = new PlatformApiClient(config);\n this.#endpointHost = new URL(config.baseUrl).host;\n }\n\n get storage(): LinearStorageHandle {\n const now = new Date();\n return {\n integrationId: this.id,\n connections: {\n get: async (orgId: string) => ({\n id: `platform-linear:${orgId}`,\n orgId,\n userId: null,\n data: {\n accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN,\n refreshToken: null,\n expiresAtMs: null,\n scope: 'read,comments:create',\n workspaceName: null,\n workspaceUrlKey: null,\n } satisfies LinearConnectionData,\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }),\n },\n } as unknown as LinearStorageHandle;\n }\n\n get projects(): FactoryProjectsStorage {\n if (!this.#projects) throw new Error('PlatformLinearIntegration projects storage has not been initialized.');\n return this.#projects;\n }\n\n initialize({ projects, auth }: { projects: FactoryProjectsStorage; auth?: RouteAuth }): void {\n this.#projects = projects;\n this.#auth = auth;\n logPlatformInfo('Platform Linear integration initialized', { endpointHost: this.#endpointHost });\n }\n\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n async resolveOrgId(resourceId: string): Promise<string | null> {\n try {\n const project = await this.projects.getById({ id: resourceId });\n return project?.orgId ?? null;\n } catch {\n return null;\n }\n }\n\n async loadConnection(orgId: string): Promise<LinearConnectionRow | null> {\n const workspace = (await this.#listWorkspaces())[0];\n if (!workspace) return null;\n const now = new Date();\n return {\n id: `platform-linear:${orgId}`,\n orgId,\n userId: null,\n accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN,\n scope: 'read,comments:create',\n refreshToken: null,\n expiresAt: null,\n workspaceName: workspace.linearWorkspaceName,\n workspaceUrlKey: workspace.urlKey,\n createdAt: now,\n updatedAt: now,\n };\n }\n\n async getFreshAccessToken(_connection: LinearConnectionRow): Promise<string> {\n return PLATFORM_MANAGED_CONNECTION_TOKEN;\n }\n\n /**\n * Background-dispatch context: platform-managed connection token when the\n * org has a connected workspace. Linear work items store the issue UUID\n * directly as `externalId`.\n */\n async #resolveIntakeDispatch({\n orgId,\n externalSource,\n }: {\n orgId: string;\n externalSource: { type: string; externalId: string };\n }): Promise<{ connection: IntegrationConnection; issueId: string } | null> {\n if (externalSource.type !== 'issue') return null;\n const connection = await this.loadConnection(orgId);\n if (!connection) return null;\n return {\n connection: { type: 'oauth', accessToken: PLATFORM_MANAGED_CONNECTION_TOKEN },\n issueId: externalSource.externalId,\n };\n }\n\n canPostComments(connection: LinearConnectionRow): boolean {\n const scopes = (connection.scope ?? '').split(/[\\s,]+/).filter(Boolean);\n return scopes.some(scope => scope === 'comments:create' || scope === 'write' || scope === 'admin');\n }\n\n async checkConnection(orgId: string): Promise<LinearConnectionCheck> {\n const connection = await this.loadConnection(orgId);\n return {\n connected: connection !== null,\n canComment: connection !== null && this.canPostComments(connection),\n checkedAt: Date.now(),\n };\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n return [\n this.#connectRoute(ctx),\n ...buildLinearRoutes({\n auth: ctx.auth,\n linear: this as unknown as LinearIntegration,\n stateSigner: ctx.stateSigner,\n baseUrl: ctx.baseUrl,\n intake: ctx.storage.intake,\n ingestFactoryIssues: attachLinearRules(ctx),\n }).filter(route => !route.path.startsWith('/auth/linear/')),\n ];\n }\n\n #connectRoute(ctx: IntegrationContext): ApiRoute {\n return registerApiRoute('/auth/linear/connect', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n await ctx.auth.ensureUser(loose(c));\n const tenant = ctx.auth.tenant(loose(c));\n if (!tenant?.orgId) return c.json({ error: 'unauthorized' }, 401);\n\n const returnTo = c.req.query('return_to') || '/';\n const originator = routeBaseUrl(ctx, c.req.url);\n logPlatformInfo('Starting Platform Linear connect flow', {\n orgId: tenant.orgId,\n returnTo,\n originator,\n });\n const query = new URLSearchParams({ return_to: returnTo, originator });\n const location = await this.#client.requestRedirect('GET', `${API_PREFIX}/authorize?${query}`);\n return c.redirect(location);\n },\n });\n }\n\n async agentTools({ requestContext }: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildLinearAgentTools({ requestContext, linear: this as unknown as LinearIntegration });\n }\n\n diagnostics(): Record<string, unknown> {\n return { mode: 'platform', endpointHost: this.#endpointHost };\n }\n\n async listProjects(): Promise<Array<LinearProject & { workspaceId: string }>> {\n return (await this.#listProjectSources()).map(({ workspace, project }) => ({\n ...project,\n id: encodeSourceId(workspace.linearWorkspaceId, project.id),\n workspaceId: workspace.linearWorkspaceId,\n }));\n }\n\n async #listProjectSources(): Promise<ProjectSource[]> {\n const workspaces = await this.#listWorkspaces();\n const projectGroups = await Promise.all(\n workspaces.map(async workspace => {\n const projects: LinearProject[] = [];\n let after: string | undefined;\n for (let page = 0; page < MAX_REFERENCE_PAGES; page += 1) {\n const query = new URLSearchParams({ first: '200' });\n if (after) query.set('after', after);\n const result = await this.#client.request<{ projects: LinearProject[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspace.linearWorkspaceId)}/projects?${query}`,\n );\n projects.push(...result.projects);\n if (!result.pageInfo.hasNextPage || !result.pageInfo.endCursor) break;\n after = result.pageInfo.endCursor;\n }\n return projects.map(project => ({ workspace, project }));\n }),\n );\n return projectGroups.flat();\n }\n\n async #listWorkspaces(): Promise<LinearWorkspace[]> {\n const result = await this.#client.request<{ workspaces: LinearWorkspace[] }>('GET', `${API_PREFIX}/workspaces`);\n return result.workspaces.filter(workspace => workspace.connected);\n }\n\n async #listIssues(sourceIds: string[], cursor?: string, labels?: string[]) {\n if (sourceIds.length === 0)\n return { issues: [] as Array<{ issue: LinearIssue; source: ProjectSource }>, nextCursor: null };\n const sources = await this.#listProjectSources();\n const sourceMap = new Map(\n sources.map(source => [encodeSourceId(source.workspace.linearWorkspaceId, source.project.id), source]),\n );\n const selected = sourceIds\n .map(sourceId => sourceMap.get(sourceId))\n .filter((source): source is ProjectSource => !!source);\n const cursors = decodeCursor(cursor, sourceIds);\n const normalizedLabels = normalizeLabels(labels);\n const nextState: Record<string, string | null> = {};\n let hasNextPage = false;\n const pages = await Promise.all(\n selected.map(async source => {\n const sourceId = encodeSourceId(source.workspace.linearWorkspaceId, source.project.id);\n if (cursors[sourceId] === null) {\n nextState[sourceId] = null;\n return [] as Array<{ issue: LinearIssue; source: ProjectSource }>;\n }\n const query = new URLSearchParams({\n first: String(PAGE_SIZE),\n projectIds: source.project.id,\n stateType: 'triage,backlog,unstarted,started',\n orderBy: 'updatedAt',\n });\n const after = cursors[sourceId];\n if (after) query.set('after', after);\n const result = await this.#client.request<{ issues: LinearIssue[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(source.workspace.linearWorkspaceId)}/issues?${query}`,\n );\n const next = result.pageInfo.hasNextPage ? result.pageInfo.endCursor : null;\n nextState[sourceId] = next;\n hasNextPage ||= next !== null;\n return result.issues\n .filter(\n issue => normalizedLabels.length === 0 || issue.labels.some(label => normalizedLabels.includes(label.name)),\n )\n .map(issue => ({ issue, source }));\n }),\n );\n return {\n issues: pages.flat(),\n nextCursor: hasNextPage ? encodeCursor(nextState, sourceIds) : null,\n };\n }\n\n async #findIssue(\n sourceId: string | undefined,\n issueId: string,\n ): Promise<{\n workspaceId: string;\n issue: LinearIssue & { comments?: { nodes: LinearComment[]; pageInfo: PageInfo } };\n } | null> {\n const workspaceIds = await this.#candidateWorkspaceIds(sourceId);\n for (const workspaceId of workspaceIds) {\n try {\n const issue = await this.#client.request<\n LinearIssue & { comments?: { nodes: LinearComment[]; pageInfo: PageInfo } }\n >(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}?include=comments`,\n );\n return { workspaceId, issue };\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n }\n return null;\n }\n\n async #resolveWorkspaceForIssue(sourceId: string | undefined, issueId: string): Promise<string | null> {\n const workspaceIds = await this.#candidateWorkspaceIds(sourceId);\n if (workspaceIds.length === 1) return workspaceIds[0]!;\n for (const workspaceId of workspaceIds) {\n try {\n await this.#client.request<LinearIssue>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}`,\n );\n return workspaceId;\n } catch (error) {\n if (!isNotFound(error)) throw error;\n }\n }\n return null;\n }\n\n async #listWorkflowStates(workspaceId: string, teamId: string): Promise<LinearWorkflowState[]> {\n const cacheKey = `${workspaceId}:${teamId}`;\n const cached = this.#workflowStatesByTeam.get(cacheKey);\n if (cached) return cached;\n const result = await this.#client.request<{ workflowStates: LinearWorkflowState[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/workflow-states?teamId=${encodeURIComponent(teamId)}&first=100`,\n );\n this.#workflowStatesByTeam.set(cacheKey, result.workflowStates);\n return result.workflowStates;\n }\n\n async #candidateWorkspaceIds(sourceId: string | undefined): Promise<string[]> {\n if (sourceId) return [decodeSourceId(sourceId).workspaceId];\n return (await this.#listWorkspaces()).map(workspace => workspace.linearWorkspaceId);\n }\n\n async #loadComments(\n workspaceId: string,\n issueId: string,\n embedded: { nodes: LinearComment[]; pageInfo: PageInfo } | undefined,\n ): Promise<LinearComment[]> {\n const comments = [...(embedded?.nodes ?? [])];\n let pageInfo = embedded?.pageInfo;\n let page = 0;\n while (pageInfo?.hasNextPage && pageInfo.endCursor && page < MAX_COMMENT_PAGES) {\n const result = await this.#client.request<{ comments: LinearComment[]; pageInfo: PageInfo }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(workspaceId)}/issues/${encodeURIComponent(issueId)}/comments?first=200&after=${encodeURIComponent(pageInfo.endCursor)}`,\n );\n comments.push(...result.comments);\n pageInfo = result.pageInfo;\n page += 1;\n }\n return comments;\n }\n}\n\nfunction parseIssue(issue: LinearIssue): IntakeIssue {\n return {\n id: issue.id,\n identifier: issue.identifier,\n title: issue.title,\n url: issue.url,\n author: issue.creator?.displayName ?? issue.creator?.name ?? null,\n state: issue.state.name,\n stateType: issue.state.type,\n priority: issue.priorityLabel,\n assignee: issue.assignee?.displayName ?? issue.assignee?.name ?? null,\n source: issue.team.key,\n labels: issue.labels.map(label => label.name),\n commentCount: null,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n };\n}\n\nfunction parseIssueDetail(issue: LinearIssue, comments: LinearComment[]): IntakeIssueDetail {\n return {\n ...parseIssue(issue),\n commentCount: comments.length,\n description: issue.description?.trim() ? issue.description : null,\n comments: comments.map(comment => ({\n author: comment.user?.displayName ?? comment.user?.name ?? null,\n body: comment.body,\n createdAt: comment.createdAt,\n })),\n };\n}\n\nfunction encodeSourceId(workspaceId: string, projectId: string): string {\n return `linear-project:${Buffer.from(JSON.stringify({ workspaceId, projectId })).toString('base64url')}`;\n}\n\nfunction decodeSourceId(sourceId: string): { workspaceId: string; projectId: string } {\n if (!sourceId.startsWith('linear-project:')) throw new Error('Linear project source id is invalid.');\n try {\n const parsed = JSON.parse(Buffer.from(sourceId.slice('linear-project:'.length), 'base64url').toString('utf8')) as {\n workspaceId?: unknown;\n projectId?: unknown;\n };\n if (typeof parsed.workspaceId !== 'string' || !parsed.workspaceId) throw new Error();\n if (typeof parsed.projectId !== 'string' || !parsed.projectId) throw new Error();\n return { workspaceId: parsed.workspaceId, projectId: parsed.projectId };\n } catch {\n throw new Error('Linear project source id is invalid.');\n }\n}\n\nfunction normalizeLabels(labels: string[] | undefined): string[] {\n return [...new Set((labels ?? []).map(label => label.trim()).filter(Boolean))];\n}\n\nfunction decodeCursor(cursor: string | undefined, sourceIds: string[]): Record<string, string | null | undefined> {\n if (!cursor) return {};\n if (sourceIds.length === 1) return { [sourceIds[0]!]: cursor };\n try {\n const parsed = JSON.parse(cursor) as unknown;\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error();\n return parsed as Record<string, string | null>;\n } catch {\n throw new Error('Linear cursor is invalid.');\n }\n}\n\nfunction encodeCursor(state: Record<string, string | null>, sourceIds: string[]): string {\n if (sourceIds.length === 1) return state[sourceIds[0]!]!;\n return JSON.stringify(state);\n}\n\nfunction requireLinearConnection(connection: IntegrationConnection): void {\n if (connection.type !== 'oauth') {\n throw new Error('Linear capabilities require an OAuth connection.');\n }\n}\n\nfunction isNotFound(error: unknown): boolean {\n return error instanceof PlatformApiError && error.status === 404;\n}\n"],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { WorkOSAdminPortal } from '@mastra/auth-workos';
2
+ import type { ApiRoute } from '@mastra/core/server';
3
+ import type { AuditEventRow } from '../../storage/domains/audit/base.js';
4
+ import type { FactoryIntegration, IntegrationContext } from '../base.js';
5
+ type WorkOSClient = ConstructorParameters<typeof WorkOSAdminPortal>[0];
6
+ export declare function toWorkOSEvent(event: AuditEventRow): {
7
+ action: string;
8
+ occurredAt: Date;
9
+ actor: {
10
+ type: string;
11
+ id: string;
12
+ };
13
+ targets: Array<{
14
+ type: string;
15
+ id: string;
16
+ name?: string;
17
+ }>;
18
+ context: {
19
+ location: string;
20
+ userAgent?: string;
21
+ };
22
+ metadata: Record<string, string | number | boolean>;
23
+ };
24
+ /** Optional WorkOS mirror and Admin Portal route, independent of the auth adapter. */
25
+ export declare class WorkOSAuditIntegration implements FactoryIntegration {
26
+ #private;
27
+ readonly id = "workos";
28
+ constructor({ client, returnUrl }: {
29
+ client: WorkOSClient;
30
+ returnUrl: string;
31
+ });
32
+ audit({ event }: {
33
+ event: AuditEventRow;
34
+ }): Promise<void>;
35
+ routes(ctx: IntegrationContext): ApiRoute[];
36
+ diagnostics(): Record<string, unknown>;
37
+ }
38
+ export {};
39
+ //# sourceMappingURL=integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/workos/integration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEzE,KAAK,YAAY,GAAG,qBAAqB,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;AAyBvE,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,IAAI,CAAC;IACjB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CACrD,CAgBA;AAED,sFAAsF;AACtF,qBAAa,sBAAuB,YAAW,kBAAkB;;IAC/D,QAAQ,CAAC,EAAE,YAAY;gBAIX,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE;QAAE,MAAM,EAAE,YAAY,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAKxE,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE;QAAE,KAAK,EAAE,aAAa,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/D,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAgC3C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAGvC"}