@mastra/factory 0.24.0-alpha.9 → 0.24.0

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 (296) hide show
  1. package/README.md +4 -4
  2. package/dist/environment/build-runner.js +2 -1
  3. package/dist/environment/build-runner.js.map +1 -1
  4. package/dist/environment/build-workflow.js +1 -1
  5. package/dist/environment/build-workflow.js.map +1 -1
  6. package/dist/environment/build.js +16 -14
  7. package/dist/environment/build.js.map +1 -1
  8. package/dist/factory.d.ts.map +1 -1
  9. package/dist/factory.js +111 -19
  10. package/dist/factory.js.map +1 -1
  11. package/dist/integrations/base.d.ts +1 -1
  12. package/dist/integrations/base.d.ts.map +1 -1
  13. package/dist/integrations/github/app-identity.js.map +1 -1
  14. package/dist/integrations/github/integration.js +1 -1
  15. package/dist/integrations/github/integration.js.map +1 -1
  16. package/dist/integrations/github/reconcile-worker.js +1 -1
  17. package/dist/integrations/github/reconcile-worker.js.map +1 -1
  18. package/dist/integrations/github/routes.d.ts +1 -1
  19. package/dist/integrations/github/routes.d.ts.map +1 -1
  20. package/dist/integrations/github/routes.js +24 -13
  21. package/dist/integrations/github/routes.js.map +1 -1
  22. package/dist/integrations/github/rules.d.ts.map +1 -1
  23. package/dist/integrations/github/rules.js +53 -9
  24. package/dist/integrations/github/rules.js.map +1 -1
  25. package/dist/integrations/github/sandbox.js +2 -2
  26. package/dist/integrations/github/sandbox.js.map +1 -1
  27. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
  28. package/dist/integrations/github/session-subscriptions.js +1 -2
  29. package/dist/integrations/github/session-subscriptions.js.map +1 -1
  30. package/dist/integrations/github/subscriptions.d.ts +7 -4
  31. package/dist/integrations/github/subscriptions.d.ts.map +1 -1
  32. package/dist/integrations/github/subscriptions.js +5 -5
  33. package/dist/integrations/github/subscriptions.js.map +1 -1
  34. package/dist/integrations/gitlab/api.js.map +1 -1
  35. package/dist/integrations/gitlab/integration.js.map +1 -1
  36. package/dist/integrations/gitlab/reconciliation-config.js +2 -1
  37. package/dist/integrations/gitlab/reconciliation-config.js.map +1 -1
  38. package/dist/integrations/gitlab/routes.d.ts.map +1 -1
  39. package/dist/integrations/gitlab/routes.js +10 -6
  40. package/dist/integrations/gitlab/routes.js.map +1 -1
  41. package/dist/integrations/gitlab/rules.js.map +1 -1
  42. package/dist/integrations/gitlab/session-subscriptions.d.ts.map +1 -1
  43. package/dist/integrations/gitlab/session-subscriptions.js +1 -2
  44. package/dist/integrations/gitlab/session-subscriptions.js.map +1 -1
  45. package/dist/integrations/gitlab/subscriptions.d.ts +7 -4
  46. package/dist/integrations/gitlab/subscriptions.d.ts.map +1 -1
  47. package/dist/integrations/gitlab/subscriptions.js +5 -5
  48. package/dist/integrations/gitlab/subscriptions.js.map +1 -1
  49. package/dist/integrations/incidentio/api.js.map +1 -1
  50. package/dist/integrations/incidentio/integration.js.map +1 -1
  51. package/dist/integrations/incidentio/rules.js.map +1 -1
  52. package/dist/integrations/issue-reconcile-worker.js +1 -1
  53. package/dist/integrations/issue-reconcile-worker.js.map +1 -1
  54. package/dist/integrations/issue-reconciler.js +1 -1
  55. package/dist/integrations/issue-reconciler.js.map +1 -1
  56. package/dist/integrations/jira/api.js.map +1 -1
  57. package/dist/integrations/jira/integration.js.map +1 -1
  58. package/dist/integrations/jira/rules.js.map +1 -1
  59. package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
  60. package/dist/integrations/linear/agent-tools.js.map +1 -1
  61. package/dist/integrations/linear/integration.js.map +1 -1
  62. package/dist/integrations/linear/rules.js.map +1 -1
  63. package/dist/integrations/platform/api-client.js.map +1 -1
  64. package/dist/integrations/platform/github/event-worker.js +1 -1
  65. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  66. package/dist/integrations/platform/github/integration.js +2 -2
  67. package/dist/integrations/platform/github/integration.js.map +1 -1
  68. package/dist/integrations/platform/gitlab/event-worker.js.map +1 -1
  69. package/dist/integrations/platform/gitlab/integration.js.map +1 -1
  70. package/dist/integrations/platform/incidentio/integration.js +2 -1
  71. package/dist/integrations/platform/incidentio/integration.js.map +1 -1
  72. package/dist/integrations/platform/jira/integration.js.map +1 -1
  73. package/dist/integrations/platform/linear/event-worker.js +1 -1
  74. package/dist/integrations/platform/linear/event-worker.js.map +1 -1
  75. package/dist/integrations/platform/linear/integration.js.map +1 -1
  76. package/dist/integrations/slack/connect-route.js +1 -1
  77. package/dist/integrations/slack/connect-route.js.map +1 -1
  78. package/dist/integrations/slack/feed-publisher.js +1 -1
  79. package/dist/integrations/slack/feed-publisher.js.map +1 -1
  80. package/dist/integrations/slack/integration.js +1 -1
  81. package/dist/integrations/slack/integration.js.map +1 -1
  82. package/dist/integrations/slack/slack.d.ts +16 -5
  83. package/dist/integrations/slack/slack.d.ts.map +1 -1
  84. package/dist/integrations/slack/slack.js +87 -34
  85. package/dist/integrations/slack/slack.js.map +1 -1
  86. package/dist/integrations/subscription-session.d.ts +0 -1
  87. package/dist/integrations/subscription-session.d.ts.map +1 -1
  88. package/dist/integrations/subscription-session.js +1 -4
  89. package/dist/integrations/subscription-session.js.map +1 -1
  90. package/dist/integrations/workos/integration.js.map +1 -1
  91. package/dist/mcp/routes.d.ts.map +1 -1
  92. package/dist/mcp/routes.js +1 -1
  93. package/dist/mcp/routes.js.map +1 -1
  94. package/dist/packages/_internals/workspace/dist/index.js +18 -1
  95. package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
  96. package/dist/routes/attention-activity.js.map +1 -1
  97. package/dist/routes/attention-parked.js.map +1 -1
  98. package/dist/routes/attention-providers.js.map +1 -1
  99. package/dist/routes/automation-runs.js.map +1 -1
  100. package/dist/routes/config.d.ts +6 -7
  101. package/dist/routes/config.d.ts.map +1 -1
  102. package/dist/routes/config.js +20 -19
  103. package/dist/routes/config.js.map +1 -1
  104. package/dist/routes/contracts.js +1 -1
  105. package/dist/routes/contracts.js.map +1 -1
  106. package/dist/routes/custom-provider-source.js.map +1 -1
  107. package/dist/routes/fs.d.ts +31 -23
  108. package/dist/routes/fs.d.ts.map +1 -1
  109. package/dist/routes/fs.js +218 -64
  110. package/dist/routes/fs.js.map +1 -1
  111. package/dist/routes/intake.js +2 -1
  112. package/dist/routes/intake.js.map +1 -1
  113. package/dist/routes/knowledge.js +1 -1
  114. package/dist/routes/knowledge.js.map +1 -1
  115. package/dist/routes/oauth.js +3 -2
  116. package/dist/routes/oauth.js.map +1 -1
  117. package/dist/routes/projects.d.ts +1 -1
  118. package/dist/routes/projects.d.ts.map +1 -1
  119. package/dist/routes/projects.js +33 -31
  120. package/dist/routes/projects.js.map +1 -1
  121. package/dist/routes/provider-credentials.d.ts.map +1 -1
  122. package/dist/routes/provider-credentials.js +6 -1
  123. package/dist/routes/provider-credentials.js.map +1 -1
  124. package/dist/routes/session-access.d.ts +55 -0
  125. package/dist/routes/session-access.d.ts.map +1 -0
  126. package/dist/routes/session-access.js +165 -0
  127. package/dist/routes/session-access.js.map +1 -0
  128. package/dist/routes/skills.d.ts +2 -4
  129. package/dist/routes/skills.d.ts.map +1 -1
  130. package/dist/routes/skills.js +19 -65
  131. package/dist/routes/skills.js.map +1 -1
  132. package/dist/routes/source-control-sessions.d.ts +6 -1
  133. package/dist/routes/source-control-sessions.d.ts.map +1 -1
  134. package/dist/routes/source-control-sessions.js +95 -12
  135. package/dist/routes/source-control-sessions.js.map +1 -1
  136. package/dist/routes/surface.d.ts +5 -0
  137. package/dist/routes/surface.d.ts.map +1 -1
  138. package/dist/routes/surface.js +10 -4
  139. package/dist/routes/surface.js.map +1 -1
  140. package/dist/routes/tenant-credentials.js.map +1 -1
  141. package/dist/routes/work-items.js.map +1 -1
  142. package/dist/rules/binding-context.js +11 -9
  143. package/dist/rules/binding-context.js.map +1 -1
  144. package/dist/rules/dispatcher.d.ts +1 -1
  145. package/dist/rules/dispatcher.d.ts.map +1 -1
  146. package/dist/rules/dispatcher.js +10 -10
  147. package/dist/rules/dispatcher.js.map +1 -1
  148. package/dist/rules/start-coordinator.d.ts +4 -0
  149. package/dist/rules/start-coordinator.d.ts.map +1 -1
  150. package/dist/rules/start-coordinator.js +36 -4
  151. package/dist/rules/start-coordinator.js.map +1 -1
  152. package/dist/rules/tools.js +2 -2
  153. package/dist/rules/tools.js.map +1 -1
  154. package/dist/rules/transition-service.js.map +1 -1
  155. package/dist/sandbox/callback-factory-sandbox.js.map +1 -1
  156. package/dist/sandbox/local-factory-sandbox.js.map +1 -1
  157. package/dist/sandbox/session-retirement.js.map +1 -1
  158. package/dist/secret-encryption.d.ts.map +1 -1
  159. package/dist/secret-encryption.js +65 -25
  160. package/dist/secret-encryption.js.map +1 -1
  161. package/dist/session/channel-reply-processor.d.ts +7 -6
  162. package/dist/session/channel-reply-processor.d.ts.map +1 -1
  163. package/dist/session/channel-reply-processor.js +9 -9
  164. package/dist/session/channel-reply-processor.js.map +1 -1
  165. package/dist/session/channel-session-link-processor.js +1 -0
  166. package/dist/session/channel-session-link-processor.js.map +1 -1
  167. package/dist/session/credential-policy.d.ts.map +1 -1
  168. package/dist/session/credential-policy.js +7 -6
  169. package/dist/session/credential-policy.js.map +1 -1
  170. package/dist/session/default-model-hydration.d.ts +1 -0
  171. package/dist/session/default-model-hydration.d.ts.map +1 -1
  172. package/dist/session/default-model-hydration.js +3 -2
  173. package/dist/session/default-model-hydration.js.map +1 -1
  174. package/dist/session/environment-state-processor.js +1 -1
  175. package/dist/session/environment-state-processor.js.map +1 -1
  176. package/dist/session/filesystem-capture.d.ts +3 -3
  177. package/dist/session/filesystem-capture.d.ts.map +1 -1
  178. package/dist/session/filesystem-capture.js +16 -17
  179. package/dist/session/filesystem-capture.js.map +1 -1
  180. package/dist/session/first-exec-capture.d.ts +1 -1
  181. package/dist/session/first-exec-capture.d.ts.map +1 -1
  182. package/dist/session/first-exec-capture.js +1 -1
  183. package/dist/session/first-exec-capture.js.map +1 -1
  184. package/dist/session/first-message-capture.d.ts +1 -1
  185. package/dist/session/first-message-capture.d.ts.map +1 -1
  186. package/dist/session/first-message-capture.js +1 -1
  187. package/dist/session/first-message-capture.js.map +1 -1
  188. package/dist/session/live-sessions.d.ts +1 -1
  189. package/dist/session/live-sessions.js +1 -1
  190. package/dist/session/live-sessions.js.map +1 -1
  191. package/dist/session/memory-settings-hydration.d.ts +1 -1
  192. package/dist/session/memory-settings-hydration.d.ts.map +1 -1
  193. package/dist/session/memory-settings-hydration.js +2 -1
  194. package/dist/session/memory-settings-hydration.js.map +1 -1
  195. package/dist/session/resource-migration.d.ts +105 -0
  196. package/dist/session/resource-migration.d.ts.map +1 -0
  197. package/dist/session/resource-migration.js +195 -0
  198. package/dist/session/resource-migration.js.map +1 -0
  199. package/dist/session/session-threads.d.ts +58 -0
  200. package/dist/session/session-threads.d.ts.map +1 -0
  201. package/dist/session/session-threads.js +90 -0
  202. package/dist/session/session-threads.js.map +1 -0
  203. package/dist/session/source-control-tools.js +8 -8
  204. package/dist/session/source-control-tools.js.map +1 -1
  205. package/dist/session/thinking-default.d.ts.map +1 -1
  206. package/dist/session/thinking-default.js +3 -4
  207. package/dist/session/thinking-default.js.map +1 -1
  208. package/dist/session/thread-session-stamp.d.ts +21 -0
  209. package/dist/session/thread-session-stamp.d.ts.map +1 -0
  210. package/dist/session/thread-session-stamp.js +20 -0
  211. package/dist/session/thread-session-stamp.js.map +1 -0
  212. package/dist/session/thread-title-mirror.d.ts +1 -1
  213. package/dist/session/thread-title-mirror.d.ts.map +1 -1
  214. package/dist/session/thread-title-mirror.js +1 -1
  215. package/dist/session/thread-title-mirror.js.map +1 -1
  216. package/dist/skills/service.d.ts +6 -8
  217. package/dist/skills/service.d.ts.map +1 -1
  218. package/dist/skills/service.js +3 -3
  219. package/dist/skills/service.js.map +1 -1
  220. package/dist/spa-static.js.map +1 -1
  221. package/dist/state-signing.js +1 -1
  222. package/dist/state-signing.js.map +1 -1
  223. package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -1
  224. package/dist/storage/domains/audit/agent-audit.js +2 -2
  225. package/dist/storage/domains/audit/agent-audit.js.map +1 -1
  226. package/dist/storage/domains/audit/base.js.map +1 -1
  227. package/dist/storage/domains/audit/domain.js.map +1 -1
  228. package/dist/storage/domains/channel-identity/base.js.map +1 -1
  229. package/dist/storage/domains/comments/base.js.map +1 -1
  230. package/dist/storage/domains/comments/domain.js.map +1 -1
  231. package/dist/storage/domains/comments/feed-context.js.map +1 -1
  232. package/dist/storage/domains/credentials/base.js.map +1 -1
  233. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  234. package/dist/storage/domains/intake/base.js.map +1 -1
  235. package/dist/storage/domains/integrations/base.d.ts +8 -0
  236. package/dist/storage/domains/integrations/base.d.ts.map +1 -1
  237. package/dist/storage/domains/integrations/base.js +7 -0
  238. package/dist/storage/domains/integrations/base.js.map +1 -1
  239. package/dist/storage/domains/memory-settings/base.js.map +1 -1
  240. package/dist/storage/domains/model-defaults/base.js.map +1 -1
  241. package/dist/storage/domains/projects/base.js.map +1 -1
  242. package/dist/storage/domains/queue-health/base.js.map +1 -1
  243. package/dist/storage/domains/source-control/base.d.ts +19 -0
  244. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  245. package/dist/storage/domains/source-control/base.js +42 -0
  246. package/dist/storage/domains/source-control/base.js.map +1 -1
  247. package/dist/storage/domains/source-control/inmemory.d.ts +5 -1
  248. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  249. package/dist/storage/domains/source-control/inmemory.js +7 -0
  250. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  251. package/dist/storage/domains/work-items/base.d.ts +14 -0
  252. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  253. package/dist/storage/domains/work-items/base.js +25 -1
  254. package/dist/storage/domains/work-items/base.js.map +1 -1
  255. package/dist/storage/domains/work-items/board-version.js.map +1 -1
  256. package/dist/storage/domains/work-items/metrics.js.map +1 -1
  257. package/dist/tools/approval-tools.js +1 -1
  258. package/dist/tools/approval-tools.js.map +1 -1
  259. package/dist/tools/deliver.d.ts +8 -5
  260. package/dist/tools/deliver.d.ts.map +1 -1
  261. package/dist/tools/deliver.js +22 -8
  262. package/dist/tools/deliver.js.map +1 -1
  263. package/dist/tools/messaging-tools.d.ts +1 -1
  264. package/dist/tools/messaging-tools.d.ts.map +1 -1
  265. package/dist/tools/messaging-tools.js +203 -72
  266. package/dist/tools/messaging-tools.js.map +1 -1
  267. package/dist/tools/permissions.d.ts +1 -1
  268. package/dist/tools/permissions.d.ts.map +1 -1
  269. package/dist/tools/permissions.js +1 -1
  270. package/dist/tools/permissions.js.map +1 -1
  271. package/dist/tools/reports.d.ts.map +1 -1
  272. package/dist/tools/reports.js +10 -3
  273. package/dist/tools/reports.js.map +1 -1
  274. package/dist/tools/run-watch.d.ts +2 -0
  275. package/dist/tools/run-watch.d.ts.map +1 -1
  276. package/dist/tools/run-watch.js +42 -2
  277. package/dist/tools/run-watch.js.map +1 -1
  278. package/dist/tools/scope.d.ts +2 -1
  279. package/dist/tools/scope.d.ts.map +1 -1
  280. package/dist/tools/scope.js +3 -2
  281. package/dist/tools/scope.js.map +1 -1
  282. package/dist/tools/session-tools.d.ts +1 -1
  283. package/dist/tools/session-tools.d.ts.map +1 -1
  284. package/dist/tools/session-tools.js +33 -16
  285. package/dist/tools/session-tools.js.map +1 -1
  286. package/dist/tools/sessions-state-processor.js +2 -2
  287. package/dist/tools/sessions-state-processor.js.map +1 -1
  288. package/dist/tools/side-sessions.d.ts +1 -1
  289. package/dist/tools/side-sessions.d.ts.map +1 -1
  290. package/dist/tools/side-sessions.js +1 -1
  291. package/dist/tools/side-sessions.js.map +1 -1
  292. package/dist/workspace.d.ts +5 -2
  293. package/dist/workspace.d.ts.map +1 -1
  294. package/dist/workspace.js +28 -15
  295. package/dist/workspace.js.map +1 -1
  296. package/package.json +13 -13
@@ -1 +1 @@
1
- {"version":3,"file":"subscriptions.js","names":[],"sources":["../../../src/integrations/github/subscriptions.ts"],"sourcesContent":["import type { IntegrationStorageHandle, IntegrationSubscription } from '../../storage/domains/integrations/base.js';\n\nexport type GithubSignalSubscriptionSource = 'auto-gh-pr-create' | 'factory-pr-create' | 'explicit-tool';\nexport type GithubSignalSubscriptionStatus = 'open' | 'closed' | 'merged';\n\nexport interface GithubSignalSubscriptionData {\n installationExternalId: string;\n projectRepositoryId: string;\n repositoryExternalId: string;\n repositorySlug: string;\n changeRequestId: string;\n ownerId: string;\n source: GithubSignalSubscriptionSource;\n subscribedByUserId: string | null;\n}\n\nexport type GithubSignalSubscriptionRow = IntegrationSubscription<GithubSignalSubscriptionData>;\nexport type GithubSubscriptionStorage = IntegrationStorageHandle<\n Record<string, unknown>,\n Record<string, unknown>,\n GithubSignalSubscriptionData\n>;\n\nexport interface SubscribeToPullRequestInput {\n orgId: string;\n installationExternalId: string;\n projectRepositoryId: string;\n repositoryExternalId: string;\n repositorySlug: string;\n changeRequestId: string;\n sessionId: string;\n ownerId: string;\n resourceId: string;\n threadId: string;\n sessionScope?: string;\n source: GithubSignalSubscriptionSource;\n subscribedByUserId?: string;\n}\n\nexport interface ThreadSubscriptionTarget {\n orgId: string;\n resourceId: string;\n threadId: string;\n sessionScope?: string;\n}\n\nexport interface PullRequestSubscriptionTarget {\n orgId: string;\n installationExternalId: string;\n repositoryExternalId: string;\n changeRequestId: string;\n}\n\nexport type GithubWebhookPullRequestTarget = Omit<PullRequestSubscriptionTarget, 'orgId'>;\n\nexport function changeRequestTargetKey(input: GithubWebhookPullRequestTarget): string {\n return `change-request:${input.installationExternalId}:${input.repositoryExternalId}:${input.changeRequestId}`;\n}\n\nfunction sameSession(row: GithubSignalSubscriptionRow, input: SubscribeToPullRequestInput): boolean {\n return (\n row.orgId === input.orgId &&\n row.sessionId === input.sessionId &&\n row.resourceId === input.resourceId &&\n row.threadId === input.threadId &&\n (row.sessionScope ?? '') === (input.sessionScope ?? '')\n );\n}\n\nexport async function subscribeToPullRequest(\n input: SubscribeToPullRequestInput,\n storage: GithubSubscriptionStorage,\n): Promise<GithubSignalSubscriptionRow> {\n const targetKey = changeRequestTargetKey(input);\n const existing = (await storage.subscriptions.listByTarget(targetKey)).find(row => sameSession(row, input));\n if (existing) {\n if (existing.status !== 'open') await storage.subscriptions.updateStatus(existing.id, 'open');\n return { ...existing, status: 'open' };\n }\n\n return storage.subscriptions.create({\n orgId: input.orgId,\n targetKey,\n sessionId: input.sessionId,\n resourceId: input.resourceId,\n threadId: input.threadId,\n sessionScope: input.sessionScope ?? '',\n status: 'open',\n data: {\n installationExternalId: input.installationExternalId,\n projectRepositoryId: input.projectRepositoryId,\n repositoryExternalId: input.repositoryExternalId,\n repositorySlug: input.repositorySlug,\n changeRequestId: input.changeRequestId,\n ownerId: input.ownerId,\n source: input.source,\n subscribedByUserId: input.subscribedByUserId ?? null,\n },\n });\n}\n\nexport async function unsubscribeFromPullRequest(\n input: SubscribeToPullRequestInput,\n storage: GithubSubscriptionStorage,\n): Promise<void> {\n const rows = await storage.subscriptions.listByTarget(changeRequestTargetKey(input));\n await Promise.all(rows.filter(row => sameSession(row, input)).map(row => storage.subscriptions.delete(row.id)));\n}\n\nexport async function listPullRequestSubscriptionsForThread(\n input: ThreadSubscriptionTarget,\n storage: GithubSubscriptionStorage,\n): Promise<GithubSignalSubscriptionRow[]> {\n const rows = await storage.subscriptions.listByThread(input.resourceId, input.threadId);\n const matching = rows.filter(\n row =>\n row.orgId === input.orgId &&\n row.resourceId === input.resourceId &&\n row.threadId === input.threadId &&\n (row.sessionScope ?? '') === (input.sessionScope ?? ''),\n );\n if (matching.length > 0 || input.sessionScope) return matching;\n // A user session is addressed by its own id (its thread is its session), but\n // its subscriptions were recorded under the Factory project that owns the\n // repository. Answer those rows for the session that created them.\n const owned = await storage.subscriptions.listBySession(input.resourceId);\n return owned.filter(row => row.orgId === input.orgId && row.threadId === input.threadId && !row.sessionScope);\n}\n\nexport async function listPullRequestSubscriptions(\n input: PullRequestSubscriptionTarget,\n storage: GithubSubscriptionStorage,\n): Promise<GithubSignalSubscriptionRow[]> {\n const rows = await storage.subscriptions.listByTarget(changeRequestTargetKey(input));\n return rows.filter(row => row.orgId === input.orgId && row.status === 'open');\n}\n\nexport async function listPullRequestSubscriptionsForWebhook(\n input: GithubWebhookPullRequestTarget,\n options: { includeTerminal?: boolean } | undefined,\n storage: GithubSubscriptionStorage,\n): Promise<GithubSignalSubscriptionRow[]> {\n const rows = await storage.subscriptions.listByTarget(changeRequestTargetKey(input));\n return options?.includeTerminal ? rows : rows.filter(row => row.status === 'open');\n}\n\nexport function retirePullRequestSubscription(\n id: string,\n status: GithubSignalSubscriptionStatus,\n storage: GithubSubscriptionStorage,\n): Promise<void> {\n return storage.subscriptions.updateStatus(id, status);\n}\n\nexport async function retirePullRequestSubscriptions(\n input: PullRequestSubscriptionTarget,\n storage: GithubSubscriptionStorage,\n): Promise<void> {\n const rows = await listPullRequestSubscriptions(input, storage);\n await Promise.all(rows.map(row => storage.subscriptions.updateStatus(row.id, 'closed')));\n}\n"],"mappings":";AAuDA,SAAgB,uBAAuB,OAA+C;CACpF,OAAO,kBAAkB,MAAM,uBAAuB,GAAG,MAAM,qBAAqB,GAAG,MAAM;AAC/F;AAEA,SAAS,YAAY,KAAkC,OAA6C;CAClG,OACE,IAAI,UAAU,MAAM,SACpB,IAAI,cAAc,MAAM,aACxB,IAAI,eAAe,MAAM,cACzB,IAAI,aAAa,MAAM,aACtB,IAAI,gBAAgB,SAAS,MAAM,gBAAgB;AAExD;AAEA,eAAsB,uBACpB,OACA,SACsC;CACtC,MAAM,YAAY,uBAAuB,KAAK;CAC9C,MAAM,YAAY,MAAM,QAAQ,cAAc,aAAa,SAAS,EAAA,CAAG,MAAK,QAAO,YAAY,KAAK,KAAK,CAAC;CAC1G,IAAI,UAAU;EACZ,IAAI,SAAS,WAAW,QAAQ,MAAM,QAAQ,cAAc,aAAa,SAAS,IAAI,MAAM;EAC5F,OAAO;GAAE,GAAG;GAAU,QAAQ;EAAO;CACvC;CAEA,OAAO,QAAQ,cAAc,OAAO;EAClC,OAAO,MAAM;EACb;EACA,WAAW,MAAM;EACjB,YAAY,MAAM;EAClB,UAAU,MAAM;EAChB,cAAc,MAAM,gBAAgB;EACpC,QAAQ;EACR,MAAM;GACJ,wBAAwB,MAAM;GAC9B,qBAAqB,MAAM;GAC3B,sBAAsB,MAAM;GAC5B,gBAAgB,MAAM;GACtB,iBAAiB,MAAM;GACvB,SAAS,MAAM;GACf,QAAQ,MAAM;GACd,oBAAoB,MAAM,sBAAsB;EAClD;CACF,CAAC;AACH;AAEA,eAAsB,2BACpB,OACA,SACe;CACf,MAAM,OAAO,MAAM,QAAQ,cAAc,aAAa,uBAAuB,KAAK,CAAC;CACnF,MAAM,QAAQ,IAAI,KAAK,QAAO,QAAO,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,KAAI,QAAO,QAAQ,cAAc,OAAO,IAAI,EAAE,CAAC,CAAC;AAChH;AAEA,eAAsB,sCACpB,OACA,SACwC;CAExC,MAAM,YAAW,MADE,QAAQ,cAAc,aAAa,MAAM,YAAY,MAAM,QAAQ,EAAA,CAChE,QACpB,QACE,IAAI,UAAU,MAAM,SACpB,IAAI,eAAe,MAAM,cACzB,IAAI,aAAa,MAAM,aACtB,IAAI,gBAAgB,SAAS,MAAM,gBAAgB,GACxD;CACA,IAAI,SAAS,SAAS,KAAK,MAAM,cAAc,OAAO;CAKtD,QAAO,MADa,QAAQ,cAAc,cAAc,MAAM,UAAU,EAAA,CAC3D,QAAO,QAAO,IAAI,UAAU,MAAM,SAAS,IAAI,aAAa,MAAM,YAAY,CAAC,IAAI,YAAY;AAC9G;AAEA,eAAsB,6BACpB,OACA,SACwC;CAExC,QAAO,MADY,QAAQ,cAAc,aAAa,uBAAuB,KAAK,CAAC,EAAA,CACvE,QAAO,QAAO,IAAI,UAAU,MAAM,SAAS,IAAI,WAAW,MAAM;AAC9E;AAEA,eAAsB,uCACpB,OACA,SACA,SACwC;CACxC,MAAM,OAAO,MAAM,QAAQ,cAAc,aAAa,uBAAuB,KAAK,CAAC;CACnF,OAAO,SAAS,kBAAkB,OAAO,KAAK,QAAO,QAAO,IAAI,WAAW,MAAM;AACnF;AAEA,SAAgB,8BACd,IACA,QACA,SACe;CACf,OAAO,QAAQ,cAAc,aAAa,IAAI,MAAM;AACtD;AAEA,eAAsB,+BACpB,OACA,SACe;CACf,MAAM,OAAO,MAAM,6BAA6B,OAAO,OAAO;CAC9D,MAAM,QAAQ,IAAI,KAAK,KAAI,QAAO,QAAQ,cAAc,aAAa,IAAI,IAAI,QAAQ,CAAC,CAAC;AACzF"}
1
+ {"version":3,"file":"subscriptions.js","names":[],"sources":["../../../src/integrations/github/subscriptions.ts"],"sourcesContent":["import type { IntegrationStorageHandle, IntegrationSubscription } from '../../storage/domains/integrations/base.js';\n\nexport type GithubSignalSubscriptionSource = 'auto-gh-pr-create' | 'factory-pr-create' | 'explicit-tool';\nexport type GithubSignalSubscriptionStatus = 'open' | 'closed' | 'merged';\n\nexport interface GithubSignalSubscriptionData {\n installationExternalId: string;\n projectRepositoryId: string;\n repositoryExternalId: string;\n repositorySlug: string;\n changeRequestId: string;\n ownerId: string;\n source: GithubSignalSubscriptionSource;\n subscribedByUserId: string | null;\n}\n\nexport type GithubSignalSubscriptionRow = IntegrationSubscription<GithubSignalSubscriptionData>;\nexport type GithubSubscriptionStorage = IntegrationStorageHandle<\n Record<string, unknown>,\n Record<string, unknown>,\n GithubSignalSubscriptionData\n>;\n\nexport interface SubscribeToPullRequestInput {\n orgId: string;\n installationExternalId: string;\n projectRepositoryId: string;\n repositoryExternalId: string;\n repositorySlug: string;\n changeRequestId: string;\n sessionId: string;\n ownerId: string;\n resourceId: string;\n threadId: string;\n source: GithubSignalSubscriptionSource;\n subscribedByUserId?: string;\n}\n\n/** A thread's subscriptions, found by the session that owns it or (for older callers) by its resource. */\nexport type ThreadSubscriptionTarget =\n | { orgId: string; sessionId: string; threadId: string }\n | { orgId: string; resourceId: string; threadId: string };\n\nexport interface PullRequestSubscriptionTarget {\n orgId: string;\n installationExternalId: string;\n repositoryExternalId: string;\n changeRequestId: string;\n}\n\nexport type GithubWebhookPullRequestTarget = Omit<PullRequestSubscriptionTarget, 'orgId'>;\n\nexport function changeRequestTargetKey(input: GithubWebhookPullRequestTarget): string {\n return `change-request:${input.installationExternalId}:${input.repositoryExternalId}:${input.changeRequestId}`;\n}\n\nfunction sameSession(row: GithubSignalSubscriptionRow, input: SubscribeToPullRequestInput): boolean {\n return (\n row.orgId === input.orgId &&\n row.sessionId === input.sessionId &&\n row.resourceId === input.resourceId &&\n row.threadId === input.threadId\n );\n}\n\nexport async function subscribeToPullRequest(\n input: SubscribeToPullRequestInput,\n storage: GithubSubscriptionStorage,\n): Promise<GithubSignalSubscriptionRow> {\n const targetKey = changeRequestTargetKey(input);\n const existing = (await storage.subscriptions.listByTarget(targetKey)).find(row => sameSession(row, input));\n if (existing) {\n if (existing.status !== 'open') await storage.subscriptions.updateStatus(existing.id, 'open');\n return { ...existing, status: 'open' };\n }\n\n return storage.subscriptions.create({\n orgId: input.orgId,\n targetKey,\n sessionId: input.sessionId,\n resourceId: input.resourceId,\n threadId: input.threadId,\n status: 'open',\n data: {\n installationExternalId: input.installationExternalId,\n projectRepositoryId: input.projectRepositoryId,\n repositoryExternalId: input.repositoryExternalId,\n repositorySlug: input.repositorySlug,\n changeRequestId: input.changeRequestId,\n ownerId: input.ownerId,\n source: input.source,\n subscribedByUserId: input.subscribedByUserId ?? null,\n },\n });\n}\n\nexport async function unsubscribeFromPullRequest(\n input: SubscribeToPullRequestInput,\n storage: GithubSubscriptionStorage,\n): Promise<void> {\n const rows = await storage.subscriptions.listByTarget(changeRequestTargetKey(input));\n await Promise.all(rows.filter(row => sameSession(row, input)).map(row => storage.subscriptions.delete(row.id)));\n}\n\nexport async function listPullRequestSubscriptionsForThread(\n input: ThreadSubscriptionTarget,\n storage: GithubSubscriptionStorage,\n): Promise<GithubSignalSubscriptionRow[]> {\n if ('sessionId' in input) {\n const owned = await storage.subscriptions.listBySession(input.sessionId);\n return owned.filter(row => row.orgId === input.orgId && row.threadId === input.threadId);\n }\n const rows = await storage.subscriptions.listByThread(input.resourceId, input.threadId);\n const matching = rows.filter(\n row =>\n row.orgId === input.orgId &&\n row.resourceId === input.resourceId &&\n row.threadId === input.threadId,\n );\n if (matching.length > 0) return matching;\n // A user session is addressed by its own id (its thread is its session), but\n // its subscriptions were recorded under the Factory project that owns the\n // repository. Answer those rows for the session that created them.\n const owned = await storage.subscriptions.listBySession(input.resourceId);\n return owned.filter(row => row.orgId === input.orgId && row.threadId === input.threadId);\n}\n\nexport async function listPullRequestSubscriptions(\n input: PullRequestSubscriptionTarget,\n storage: GithubSubscriptionStorage,\n): Promise<GithubSignalSubscriptionRow[]> {\n const rows = await storage.subscriptions.listByTarget(changeRequestTargetKey(input));\n return rows.filter(row => row.orgId === input.orgId && row.status === 'open');\n}\n\nexport async function listPullRequestSubscriptionsForWebhook(\n input: GithubWebhookPullRequestTarget,\n options: { includeTerminal?: boolean } | undefined,\n storage: GithubSubscriptionStorage,\n): Promise<GithubSignalSubscriptionRow[]> {\n const rows = await storage.subscriptions.listByTarget(changeRequestTargetKey(input));\n return options?.includeTerminal ? rows : rows.filter(row => row.status === 'open');\n}\n\nexport function retirePullRequestSubscription(\n id: string,\n status: GithubSignalSubscriptionStatus,\n storage: GithubSubscriptionStorage,\n): Promise<void> {\n return storage.subscriptions.updateStatus(id, status);\n}\n\nexport async function retirePullRequestSubscriptions(\n input: PullRequestSubscriptionTarget,\n storage: GithubSubscriptionStorage,\n): Promise<void> {\n const rows = await listPullRequestSubscriptions(input, storage);\n await Promise.all(rows.map(row => storage.subscriptions.updateStatus(row.id, 'closed')));\n}\n"],"mappings":";AAoDA,SAAgB,uBAAuB,OAA+C;CACpF,OAAO,kBAAkB,MAAM,uBAAuB,GAAG,MAAM,qBAAqB,GAAG,MAAM;AAC/F;AAEA,SAAS,YAAY,KAAkC,OAA6C;CAClG,OACE,IAAI,UAAU,MAAM,SACpB,IAAI,cAAc,MAAM,aACxB,IAAI,eAAe,MAAM,cACzB,IAAI,aAAa,MAAM;AAE3B;AAEA,eAAsB,uBACpB,OACA,SACsC;CACtC,MAAM,YAAY,uBAAuB,KAAK;CAC9C,MAAM,YAAY,MAAM,QAAQ,cAAc,aAAa,SAAS,EAAA,CAAG,MAAK,QAAO,YAAY,KAAK,KAAK,CAAC;CAC1G,IAAI,UAAU;EACZ,IAAI,SAAS,WAAW,QAAQ,MAAM,QAAQ,cAAc,aAAa,SAAS,IAAI,MAAM;EAC5F,OAAO;GAAE,GAAG;GAAU,QAAQ;EAAO;CACvC;CAEA,OAAO,QAAQ,cAAc,OAAO;EAClC,OAAO,MAAM;EACb;EACA,WAAW,MAAM;EACjB,YAAY,MAAM;EAClB,UAAU,MAAM;EAChB,QAAQ;EACR,MAAM;GACJ,wBAAwB,MAAM;GAC9B,qBAAqB,MAAM;GAC3B,sBAAsB,MAAM;GAC5B,gBAAgB,MAAM;GACtB,iBAAiB,MAAM;GACvB,SAAS,MAAM;GACf,QAAQ,MAAM;GACd,oBAAoB,MAAM,sBAAsB;EAClD;CACF,CAAC;AACH;AAEA,eAAsB,2BACpB,OACA,SACe;CACf,MAAM,OAAO,MAAM,QAAQ,cAAc,aAAa,uBAAuB,KAAK,CAAC;CACnF,MAAM,QAAQ,IAAI,KAAK,QAAO,QAAO,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,KAAI,QAAO,QAAQ,cAAc,OAAO,IAAI,EAAE,CAAC,CAAC;AAChH;AAEA,eAAsB,sCACpB,OACA,SACwC;CACxC,IAAI,eAAe,OAEjB,QAAO,MADa,QAAQ,cAAc,cAAc,MAAM,SAAS,EAAA,CAC1D,QAAO,QAAO,IAAI,UAAU,MAAM,SAAS,IAAI,aAAa,MAAM,QAAQ;CAGzF,MAAM,YAAW,MADE,QAAQ,cAAc,aAAa,MAAM,YAAY,MAAM,QAAQ,EAAA,CAChE,QACpB,QACE,IAAI,UAAU,MAAM,SACpB,IAAI,eAAe,MAAM,cACzB,IAAI,aAAa,MAAM,QAC3B;CACA,IAAI,SAAS,SAAS,GAAG,OAAO;CAKhC,QAAO,MADa,QAAQ,cAAc,cAAc,MAAM,UAAU,EAAA,CAC3D,QAAO,QAAO,IAAI,UAAU,MAAM,SAAS,IAAI,aAAa,MAAM,QAAQ;AACzF;AAEA,eAAsB,6BACpB,OACA,SACwC;CAExC,QAAO,MADY,QAAQ,cAAc,aAAa,uBAAuB,KAAK,CAAC,EAAA,CACvE,QAAO,QAAO,IAAI,UAAU,MAAM,SAAS,IAAI,WAAW,MAAM;AAC9E;AAEA,eAAsB,uCACpB,OACA,SACA,SACwC;CACxC,MAAM,OAAO,MAAM,QAAQ,cAAc,aAAa,uBAAuB,KAAK,CAAC;CACnF,OAAO,SAAS,kBAAkB,OAAO,KAAK,QAAO,QAAO,IAAI,WAAW,MAAM;AACnF;AAEA,SAAgB,8BACd,IACA,QACA,SACe;CACf,OAAO,QAAQ,cAAc,aAAa,IAAI,MAAM;AACtD;AAEA,eAAsB,+BACpB,OACA,SACe;CACf,MAAM,OAAO,MAAM,6BAA6B,OAAO,OAAO;CAC9D,MAAM,QAAQ,IAAI,KAAK,KAAI,QAAO,QAAQ,cAAc,aAAa,IAAI,IAAI,QAAQ,CAAC,CAAC;AACzF"}
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","names":["#direct","#platform","#request"],"sources":["../../../src/integrations/gitlab/api.ts"],"sourcesContent":["import type { PlatformApiClient} from '../platform/api-client.js';\nimport { PlatformApiError } from '../platform/api-client.js';\n\nexport const GITLAB_PROJECTS_PAGE_SIZE = 100;\nexport const GITLAB_ISSUES_PAGE_SIZE = 30;\nexport const GITLAB_NOTES_PAGE_SIZE = 100;\nexport const GITLAB_MIN_ACCESS_LEVEL = 20;\nexport const GITLAB_MERGE_REQUESTS_PAGE_SIZE = 30;\nexport const GITLAB_DISCUSSIONS_PAGE_SIZE = 100;\n\nexport type GitLabApiErrorCode = 'gitlab_auth_failed' | 'gitlab_request_failed';\n\nexport class GitLabApiError extends Error {\n readonly status: number | null;\n readonly code: GitLabApiErrorCode;\n\n constructor(message: string, status: number | null) {\n super(message);\n this.name = 'GitLabApiError';\n this.status = status;\n this.code = status === 401 || status === 403 ? 'gitlab_auth_failed' : 'gitlab_request_failed';\n }\n}\n\nexport interface GitLabProject {\n id: number;\n name: string;\n path_with_namespace: string;\n web_url: string;\n default_branch?: string | null;\n}\n\nexport interface GitLabUser {\n id?: number;\n name?: string | null;\n username: string;\n}\n\nexport interface GitLabLabelDetail {\n name: string;\n color: string;\n text_color?: string;\n}\n\ntype GitLabIssueResponse = Omit<GitLabIssue, 'labels' | 'labelDetails'> & {\n labels?: Array<string | GitLabLabelDetail>;\n};\n\nexport interface GitLabIssue {\n id: number;\n iid: number;\n project_id: number;\n title: string;\n description?: string | null;\n state: 'opened' | 'closed';\n web_url: string;\n author?: GitLabUser | null;\n assignee?: GitLabUser | null;\n assignees?: GitLabUser[];\n labels?: string[];\n labelDetails?: GitLabLabelDetail[];\n weight?: number | null;\n user_notes_count?: number;\n created_at: string;\n updated_at: string;\n}\n\nexport interface GitLabNote {\n id: number;\n body: string;\n type?: string | null;\n author?: GitLabUser | null;\n created_at: string;\n updated_at?: string;\n system?: boolean;\n}\n\nexport interface GitLabDiffRefs {\n base_sha: string;\n start_sha: string;\n head_sha: string;\n}\n\nexport interface GitLabMergeRequest {\n id: number;\n iid: number;\n project_id: number;\n title: string;\n description?: string | null;\n state: 'opened' | 'closed' | 'merged' | 'locked';\n web_url: string;\n author?: GitLabUser | null;\n assignees?: GitLabUser[];\n reviewers?: GitLabUser[];\n labels?: string[];\n source_branch: string;\n target_branch: string;\n sha: string;\n merge_commit_sha?: string | null;\n squash_commit_sha?: string | null;\n diff_refs?: GitLabDiffRefs | null;\n merge_status?: string;\n detailed_merge_status?: string;\n draft?: boolean;\n work_in_progress?: boolean;\n merged_at?: string | null;\n user_notes_count?: number;\n created_at: string;\n updated_at: string;\n}\n\nexport interface GitLabMergeResult extends GitLabMergeRequest {\n message?: string;\n}\n\nexport interface GitLabDiscussionPosition {\n position_type: 'text';\n base_sha: string;\n start_sha: string;\n head_sha: string;\n old_path: string;\n new_path: string;\n old_line?: number | null;\n new_line?: number | null;\n line_range?: {\n start: GitLabDiscussionLine;\n end: GitLabDiscussionLine;\n };\n}\n\nexport interface GitLabDiscussionLine {\n line_code: string;\n type: 'old' | 'new';\n old_line?: number | null;\n new_line?: number | null;\n}\n\nexport interface GitLabDiscussionNote extends GitLabNote {\n position?: GitLabDiscussionPosition | null;\n resolvable?: boolean;\n resolved?: boolean;\n}\n\nexport interface GitLabDiscussion {\n id: string;\n individual_note?: boolean;\n notes: GitLabDiscussionNote[];\n}\n\nexport interface GitLabMergeRequestApprovals {\n approved: boolean;\n approvals_required?: number;\n approvals_left?: number;\n approved_by?: Array<{ user: GitLabUser }>;\n}\n\nexport interface GitLabMember {\n id: number;\n username: string;\n name?: string | null;\n state?: string;\n access_level?: number;\n}\n\nexport type GitLabCommitStatusState = 'pending' | 'running' | 'success' | 'failed' | 'canceled' | 'skipped';\n\nexport interface GitLabCommitStatus {\n id: number;\n sha: string;\n name: string;\n status: GitLabCommitStatusState;\n target_url: string | null;\n}\n\nexport interface GitLabApiClientConfig {\n baseUrl: string;\n accessToken: string;\n fetchImpl?: typeof fetch;\n}\n\nexport interface PlatformGitLabApiClientConfig {\n client: PlatformApiClient;\n connectionId: string;\n}\n\nexport class GitLabApiClient {\n readonly #direct: { baseUrl: string; accessToken: string; fetch: typeof fetch } | null;\n readonly #platform: PlatformGitLabApiClientConfig | null;\n\n constructor(config: GitLabApiClientConfig | PlatformGitLabApiClientConfig) {\n if ('client' in config) {\n if (!config.connectionId.trim()) throw new Error('GitLabApiClient is missing required config: connectionId.');\n this.#direct = null;\n this.#platform = config;\n return;\n }\n\n const accessToken = config.accessToken.trim();\n if (!accessToken) throw new Error('GitLabApiClient is missing required config: accessToken.');\n let url: URL;\n try {\n url = new URL(config.baseUrl);\n } catch {\n throw new Error('GitLabApiClient baseUrl must be an absolute HTTP(S) URL.');\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new Error('GitLabApiClient baseUrl must be an absolute HTTP(S) URL.');\n }\n if (url.username || url.password || url.search || url.hash) {\n throw new Error('GitLabApiClient baseUrl must not include credentials, query, or fragment.');\n }\n const hostname = url.hostname.replace(/^\\[|\\]$/g, '').toLowerCase();\n const isLoopback = hostname === 'localhost' || hostname === '::1' || /^127(?:\\.\\d{1,3}){3}$/.test(hostname);\n if (url.protocol === 'http:' && !isLoopback) {\n throw new Error('GitLabApiClient baseUrl must use HTTPS unless it targets a loopback host.');\n }\n this.#direct = {\n baseUrl: config.baseUrl.replace(/\\/+$/, ''),\n accessToken,\n fetch: config.fetchImpl ?? globalThis.fetch,\n };\n this.#platform = null;\n }\n\n async getCurrentUser(): Promise<GitLabUser> {\n return this.#request<GitLabUser>('GET', '/api/v4/user');\n }\n\n async getProject(projectId: string): Promise<GitLabProject> {\n return this.#request<GitLabProject>('GET', `/api/v4/projects/${encodeURIComponent(projectId)}`);\n }\n\n async listProjects(options: { page?: number } = {}): Promise<GitLabProject[]> {\n return this.#request<GitLabProject[]>('GET', '/api/v4/projects', {\n query: {\n membership: 'true',\n simple: 'true',\n with_issues_enabled: 'true',\n min_access_level: GITLAB_MIN_ACCESS_LEVEL,\n order_by: 'last_activity_at',\n sort: 'desc',\n page: options.page ?? 1,\n per_page: GITLAB_PROJECTS_PAGE_SIZE,\n },\n });\n }\n\n async listIssues(projectId: string, options: { page?: number; labels?: string[] } = {}): Promise<GitLabIssue[]> {\n const issues = await this.#request<GitLabIssueResponse[]>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/issues`,\n {\n query: {\n state: 'opened',\n scope: 'all',\n order_by: 'updated_at',\n sort: 'desc',\n page: options.page ?? 1,\n per_page: GITLAB_ISSUES_PAGE_SIZE,\n labels: options.labels?.length ? options.labels.join(',') : undefined,\n with_labels_details: 'true',\n },\n },\n );\n return issues.map(normalizeIssue);\n }\n\n async getIssue(projectId: string, issueIid: number): Promise<GitLabIssue> {\n // GitLab's single-issue endpoint returns label names even when\n // with_labels_details is requested. The IID-filtered list endpoint returns\n // the same issue with label colors, including for closed issues.\n const issues = await this.#request<GitLabIssueResponse[]>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/issues`,\n { query: { 'iids[]': issueIid, state: 'all', scope: 'all', with_labels_details: 'true' } },\n );\n const issue = issues.find(candidate => candidate.iid === issueIid);\n if (!issue) throw new GitLabApiError(`GitLab issue ${issueIid} was not found`, 404);\n return normalizeIssue(issue);\n }\n\n async listNotes(projectId: string, issueIid: number, options: { page?: number } = {}): Promise<GitLabNote[]> {\n return this.#request<GitLabNote[]>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/notes`,\n {\n query: { order_by: 'created_at', sort: 'asc', page: options.page ?? 1, per_page: GITLAB_NOTES_PAGE_SIZE },\n },\n );\n }\n\n async createNote(projectId: string, issueIid: number, body: string): Promise<GitLabNote> {\n return this.#request<GitLabNote>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/notes`,\n { body: { body } },\n );\n }\n\n async updateIssueState(projectId: string, issueIid: number, stateEvent: 'close' | 'reopen'): Promise<GitLabIssue> {\n return this.#request<GitLabIssue>('PUT', `/api/v4/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`, {\n body: { state_event: stateEvent },\n });\n }\n\n async listMergeRequests(\n projectId: string,\n options: { page?: number; state?: 'opened' | 'closed' | 'merged' | 'all' } = {},\n ): Promise<GitLabMergeRequest[]> {\n return this.#request<GitLabMergeRequest[]>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests`,\n {\n query: {\n state: options.state ?? 'opened',\n order_by: 'updated_at',\n sort: 'desc',\n page: options.page ?? 1,\n per_page: GITLAB_MERGE_REQUESTS_PAGE_SIZE,\n },\n },\n );\n }\n\n async getMergeRequest(projectId: string, mergeRequestIid: number): Promise<GitLabMergeRequest> {\n return this.#request<GitLabMergeRequest>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`,\n );\n }\n\n async createMergeRequest(\n projectId: string,\n input: {\n sourceBranch: string;\n targetBranch: string;\n title: string;\n description?: string;\n removeSourceBranch?: boolean;\n squash?: boolean;\n },\n ): Promise<GitLabMergeRequest> {\n return this.#request<GitLabMergeRequest>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests`,\n {\n body: {\n source_branch: input.sourceBranch,\n target_branch: input.targetBranch,\n title: input.title,\n description: input.description,\n remove_source_branch: input.removeSourceBranch,\n squash: input.squash,\n },\n },\n );\n }\n\n async updateMergeRequest(\n projectId: string,\n mergeRequestIid: number,\n input: {\n title?: string;\n description?: string;\n targetBranch?: string;\n stateEvent?: 'close' | 'reopen';\n removeSourceBranch?: boolean;\n squash?: boolean;\n reviewerIds?: number[];\n },\n ): Promise<GitLabMergeRequest> {\n return this.#request<GitLabMergeRequest>(\n 'PUT',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`,\n {\n body: {\n title: input.title,\n description: input.description,\n target_branch: input.targetBranch,\n state_event: input.stateEvent,\n remove_source_branch: input.removeSourceBranch,\n squash: input.squash,\n reviewer_ids: input.reviewerIds,\n },\n },\n );\n }\n\n async mergeMergeRequest(\n projectId: string,\n mergeRequestIid: number,\n options: {\n squash?: boolean;\n mergeCommitMessage?: string;\n squashCommitMessage?: string;\n shouldRemoveSourceBranch?: boolean;\n } = {},\n ): Promise<GitLabMergeResult> {\n return this.#request<GitLabMergeResult>(\n 'PUT',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/merge`,\n {\n body: {\n squash: options.squash,\n merge_commit_message: options.mergeCommitMessage,\n squash_commit_message: options.squashCommitMessage,\n should_remove_source_branch: options.shouldRemoveSourceBranch,\n },\n },\n );\n }\n\n async listMergeRequestNotes(\n projectId: string,\n mergeRequestIid: number,\n options: { page?: number } = {},\n ): Promise<GitLabNote[]> {\n return this.#request<GitLabNote[]>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/notes`,\n {\n query: {\n order_by: 'created_at',\n sort: 'asc',\n page: options.page ?? 1,\n per_page: GITLAB_NOTES_PAGE_SIZE,\n },\n },\n );\n }\n\n async createMergeRequestNote(projectId: string, mergeRequestIid: number, body: string): Promise<GitLabNote> {\n return this.#request<GitLabNote>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/notes`,\n { body: { body } },\n );\n }\n\n async getMergeRequestNote(projectId: string, mergeRequestIid: number, noteId: number): Promise<GitLabNote> {\n return this.#request<GitLabNote>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/notes/${noteId}`,\n );\n }\n\n async updateMergeRequestNote(\n projectId: string,\n mergeRequestIid: number,\n noteId: number,\n body: string,\n ): Promise<GitLabNote> {\n return this.#request<GitLabNote>(\n 'PUT',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/notes/${noteId}`,\n { body: { body } },\n );\n }\n\n async deleteMergeRequestNote(projectId: string, mergeRequestIid: number, noteId: number): Promise<void> {\n await this.#request<void>(\n 'DELETE',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/notes/${noteId}`,\n );\n }\n\n async listMergeRequestDiscussions(\n projectId: string,\n mergeRequestIid: number,\n options: { page?: number } = {},\n ): Promise<GitLabDiscussion[]> {\n return this.#request<GitLabDiscussion[]>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions`,\n { query: { page: options.page ?? 1, per_page: GITLAB_DISCUSSIONS_PAGE_SIZE } },\n );\n }\n\n async getMergeRequestDiscussion(\n projectId: string,\n mergeRequestIid: number,\n discussionId: string,\n ): Promise<GitLabDiscussion> {\n return this.#request<GitLabDiscussion>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${encodeURIComponent(discussionId)}`,\n );\n }\n\n async resolveMergeRequestDiscussion(\n projectId: string,\n mergeRequestIid: number,\n discussionId: string,\n resolved: boolean,\n ): Promise<GitLabDiscussion> {\n return this.#request<GitLabDiscussion>(\n 'PUT',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${encodeURIComponent(discussionId)}`,\n { body: { resolved } },\n );\n }\n\n async setCommitStatus(\n projectId: string,\n sha: string,\n status: { state: GitLabCommitStatusState; name: string; ref?: string; description?: string; targetUrl?: string },\n ): Promise<GitLabCommitStatus> {\n return this.#request<GitLabCommitStatus>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/statuses/${encodeURIComponent(sha)}`,\n {\n body: {\n state: status.state,\n name: status.name,\n ref: status.ref,\n description: status.description,\n target_url: status.targetUrl,\n },\n },\n );\n }\n\n async createMergeRequestDiscussion(\n projectId: string,\n mergeRequestIid: number,\n input: { body: string; commitId?: string; position?: GitLabDiscussionPosition },\n ): Promise<GitLabDiscussion> {\n return this.#request<GitLabDiscussion>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions`,\n { body: { body: input.body, commit_id: input.commitId, position: input.position } },\n );\n }\n\n async addMergeRequestDiscussionNote(\n projectId: string,\n mergeRequestIid: number,\n discussionId: string,\n body: string,\n ): Promise<GitLabDiscussionNote> {\n return this.#request<GitLabDiscussionNote>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${encodeURIComponent(discussionId)}/notes`,\n { body: { body } },\n );\n }\n\n async updateMergeRequestDiscussionNote(\n projectId: string,\n mergeRequestIid: number,\n discussionId: string,\n noteId: number,\n body: string,\n ): Promise<GitLabDiscussionNote> {\n return this.#request<GitLabDiscussionNote>(\n 'PUT',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${encodeURIComponent(discussionId)}/notes/${noteId}`,\n { body: { body } },\n );\n }\n\n async deleteMergeRequestDiscussionNote(\n projectId: string,\n mergeRequestIid: number,\n discussionId: string,\n noteId: number,\n ): Promise<void> {\n await this.#request<void>(\n 'DELETE',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${encodeURIComponent(discussionId)}/notes/${noteId}`,\n );\n }\n\n async getMergeRequestApprovals(projectId: string, mergeRequestIid: number): Promise<GitLabMergeRequestApprovals> {\n return this.#request<GitLabMergeRequestApprovals>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/approvals`,\n );\n }\n\n async approveMergeRequest(projectId: string, mergeRequestIid: number, sha?: string): Promise<GitLabMergeRequest> {\n return this.#request<GitLabMergeRequest>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/approve`,\n sha ? { body: { sha } } : undefined,\n );\n }\n\n async unapproveMergeRequest(projectId: string, mergeRequestIid: number): Promise<GitLabMergeRequest> {\n return this.#request<GitLabMergeRequest>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/unapprove`,\n );\n }\n\n async setMergeRequestReviewers(\n projectId: string,\n mergeRequestIid: number,\n reviewerIds: number[],\n ): Promise<GitLabMergeRequest> {\n return this.#request<GitLabMergeRequest>(\n 'PUT',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`,\n { body: { reviewer_ids: reviewerIds } },\n );\n }\n\n async listProjectMembers(\n projectId: string,\n options: { query?: string; page?: number } = {},\n ): Promise<GitLabMember[]> {\n return this.#request<GitLabMember[]>('GET', `/api/v4/projects/${encodeURIComponent(projectId)}/members/all`, {\n query: {\n query: options.query,\n page: options.page ?? 1,\n per_page: GITLAB_DISCUSSIONS_PAGE_SIZE,\n },\n });\n }\n\n async #request<T>(\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n options: { query?: Record<string, string | number | undefined>; body?: unknown } = {},\n ): Promise<T> {\n const query = new URLSearchParams();\n for (const [key, value] of Object.entries(options.query ?? {})) {\n if (value !== undefined) query.set(key, String(value));\n }\n const suffix = query.size > 0 ? `?${query.toString()}` : '';\n // Repository targets carry both the immutable numeric ID for GitLab API\n // requests and the path for browser links. A Platform proxy decodes %2F,\n // so forwarding a path-based project locator would turn into a 404.\n const apiPath = path.replace(/^\\/api\\/v4\\/projects\\/([^/]+)/, (full, encoded: string) => {\n const target = decodeURIComponent(encoded);\n const match = /^(\\d+):(.+)$/.exec(target);\n return match ? `/api/v4/projects/${encodeURIComponent(match[1]!)}` : full;\n });\n\n if (this.#platform) {\n const proxyPath = `/v2/connections/${encodeURIComponent(this.#platform.connectionId)}/proxy${apiPath}${suffix}`;\n try {\n return await this.#platform.client.request<T>(method, proxyPath, options.body);\n } catch (error) {\n if (error instanceof PlatformApiError) throw new GitLabApiError(error.message, error.status);\n throw new GitLabApiError(error instanceof Error ? error.message : String(error), null);\n }\n }\n\n const direct = this.#direct!;\n const headers: Record<string, string> = {\n accept: 'application/json',\n 'private-token': direct.accessToken,\n };\n const init: RequestInit = { method, headers, signal: AbortSignal.timeout(15_000) };\n if (options.body !== undefined) {\n headers['content-type'] = 'application/json';\n init.body = JSON.stringify(options.body);\n }\n\n let response: Response;\n try {\n response = await direct.fetch(`${direct.baseUrl}${apiPath}${suffix}`, init);\n } catch (error) {\n const message = (error instanceof Error ? error.message : String(error))\n .split(direct.accessToken)\n .join('[REDACTED]');\n throw new GitLabApiError(message, null);\n }\n if (!response.ok) {\n const message = (await extractError(response)).split(direct.accessToken).join('[REDACTED]');\n throw new GitLabApiError(message, response.status);\n }\n if (response.status === 204) return undefined as T;\n return (await response.json()) as T;\n }\n}\n\nasync function extractError(response: Response): Promise<string> {\n try {\n const data = (await response.clone().json()) as Record<string, unknown>;\n if (typeof data.message === 'string' && data.message) return data.message;\n if (typeof data.error === 'string' && data.error) return data.error;\n } catch {\n // Fall through to the status-based message.\n }\n return `GitLab API request failed (${response.status})`;\n}\n\nfunction normalizeIssue(response: GitLabIssueResponse): GitLabIssue {\n const { labels, ...issue } = response;\n const labelDetails = (labels ?? []).filter((label): label is GitLabLabelDetail => typeof label !== 'string');\n return {\n ...issue,\n labels: (labels ?? []).map(label => (typeof label === 'string' ? label : label.name)),\n ...(labelDetails.length > 0 ? { labelDetails } : {}),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"api.js","names":[],"sources":["../../../src/integrations/gitlab/api.ts"],"sourcesContent":["import type { PlatformApiClient} from '../platform/api-client.js';\nimport { PlatformApiError } from '../platform/api-client.js';\n\nexport const GITLAB_PROJECTS_PAGE_SIZE = 100;\nexport const GITLAB_ISSUES_PAGE_SIZE = 30;\nexport const GITLAB_NOTES_PAGE_SIZE = 100;\nexport const GITLAB_MIN_ACCESS_LEVEL = 20;\nexport const GITLAB_MERGE_REQUESTS_PAGE_SIZE = 30;\nexport const GITLAB_DISCUSSIONS_PAGE_SIZE = 100;\n\nexport type GitLabApiErrorCode = 'gitlab_auth_failed' | 'gitlab_request_failed';\n\nexport class GitLabApiError extends Error {\n readonly status: number | null;\n readonly code: GitLabApiErrorCode;\n\n constructor(message: string, status: number | null) {\n super(message);\n this.name = 'GitLabApiError';\n this.status = status;\n this.code = status === 401 || status === 403 ? 'gitlab_auth_failed' : 'gitlab_request_failed';\n }\n}\n\nexport interface GitLabProject {\n id: number;\n name: string;\n path_with_namespace: string;\n web_url: string;\n default_branch?: string | null;\n}\n\nexport interface GitLabUser {\n id?: number;\n name?: string | null;\n username: string;\n}\n\nexport interface GitLabLabelDetail {\n name: string;\n color: string;\n text_color?: string;\n}\n\ntype GitLabIssueResponse = Omit<GitLabIssue, 'labels' | 'labelDetails'> & {\n labels?: Array<string | GitLabLabelDetail>;\n};\n\nexport interface GitLabIssue {\n id: number;\n iid: number;\n project_id: number;\n title: string;\n description?: string | null;\n state: 'opened' | 'closed';\n web_url: string;\n author?: GitLabUser | null;\n assignee?: GitLabUser | null;\n assignees?: GitLabUser[];\n labels?: string[];\n labelDetails?: GitLabLabelDetail[];\n weight?: number | null;\n user_notes_count?: number;\n created_at: string;\n updated_at: string;\n}\n\nexport interface GitLabNote {\n id: number;\n body: string;\n type?: string | null;\n author?: GitLabUser | null;\n created_at: string;\n updated_at?: string;\n system?: boolean;\n}\n\nexport interface GitLabDiffRefs {\n base_sha: string;\n start_sha: string;\n head_sha: string;\n}\n\nexport interface GitLabMergeRequest {\n id: number;\n iid: number;\n project_id: number;\n title: string;\n description?: string | null;\n state: 'opened' | 'closed' | 'merged' | 'locked';\n web_url: string;\n author?: GitLabUser | null;\n assignees?: GitLabUser[];\n reviewers?: GitLabUser[];\n labels?: string[];\n source_branch: string;\n target_branch: string;\n sha: string;\n merge_commit_sha?: string | null;\n squash_commit_sha?: string | null;\n diff_refs?: GitLabDiffRefs | null;\n merge_status?: string;\n detailed_merge_status?: string;\n draft?: boolean;\n work_in_progress?: boolean;\n merged_at?: string | null;\n user_notes_count?: number;\n created_at: string;\n updated_at: string;\n}\n\nexport interface GitLabMergeResult extends GitLabMergeRequest {\n message?: string;\n}\n\nexport interface GitLabDiscussionPosition {\n position_type: 'text';\n base_sha: string;\n start_sha: string;\n head_sha: string;\n old_path: string;\n new_path: string;\n old_line?: number | null;\n new_line?: number | null;\n line_range?: {\n start: GitLabDiscussionLine;\n end: GitLabDiscussionLine;\n };\n}\n\nexport interface GitLabDiscussionLine {\n line_code: string;\n type: 'old' | 'new';\n old_line?: number | null;\n new_line?: number | null;\n}\n\nexport interface GitLabDiscussionNote extends GitLabNote {\n position?: GitLabDiscussionPosition | null;\n resolvable?: boolean;\n resolved?: boolean;\n}\n\nexport interface GitLabDiscussion {\n id: string;\n individual_note?: boolean;\n notes: GitLabDiscussionNote[];\n}\n\nexport interface GitLabMergeRequestApprovals {\n approved: boolean;\n approvals_required?: number;\n approvals_left?: number;\n approved_by?: Array<{ user: GitLabUser }>;\n}\n\nexport interface GitLabMember {\n id: number;\n username: string;\n name?: string | null;\n state?: string;\n access_level?: number;\n}\n\nexport type GitLabCommitStatusState = 'pending' | 'running' | 'success' | 'failed' | 'canceled' | 'skipped';\n\nexport interface GitLabCommitStatus {\n id: number;\n sha: string;\n name: string;\n status: GitLabCommitStatusState;\n target_url: string | null;\n}\n\nexport interface GitLabApiClientConfig {\n baseUrl: string;\n accessToken: string;\n fetchImpl?: typeof fetch;\n}\n\nexport interface PlatformGitLabApiClientConfig {\n client: PlatformApiClient;\n connectionId: string;\n}\n\nexport class GitLabApiClient {\n readonly #direct: { baseUrl: string; accessToken: string; fetch: typeof fetch } | null;\n readonly #platform: PlatformGitLabApiClientConfig | null;\n\n constructor(config: GitLabApiClientConfig | PlatformGitLabApiClientConfig) {\n if ('client' in config) {\n if (!config.connectionId.trim()) throw new Error('GitLabApiClient is missing required config: connectionId.');\n this.#direct = null;\n this.#platform = config;\n return;\n }\n\n const accessToken = config.accessToken.trim();\n if (!accessToken) throw new Error('GitLabApiClient is missing required config: accessToken.');\n let url: URL;\n try {\n url = new URL(config.baseUrl);\n } catch {\n throw new Error('GitLabApiClient baseUrl must be an absolute HTTP(S) URL.');\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new Error('GitLabApiClient baseUrl must be an absolute HTTP(S) URL.');\n }\n if (url.username || url.password || url.search || url.hash) {\n throw new Error('GitLabApiClient baseUrl must not include credentials, query, or fragment.');\n }\n const hostname = url.hostname.replace(/^\\[|\\]$/g, '').toLowerCase();\n const isLoopback = hostname === 'localhost' || hostname === '::1' || /^127(?:\\.\\d{1,3}){3}$/.test(hostname);\n if (url.protocol === 'http:' && !isLoopback) {\n throw new Error('GitLabApiClient baseUrl must use HTTPS unless it targets a loopback host.');\n }\n this.#direct = {\n baseUrl: config.baseUrl.replace(/\\/+$/, ''),\n accessToken,\n fetch: config.fetchImpl ?? globalThis.fetch,\n };\n this.#platform = null;\n }\n\n async getCurrentUser(): Promise<GitLabUser> {\n return this.#request<GitLabUser>('GET', '/api/v4/user');\n }\n\n async getProject(projectId: string): Promise<GitLabProject> {\n return this.#request<GitLabProject>('GET', `/api/v4/projects/${encodeURIComponent(projectId)}`);\n }\n\n async listProjects(options: { page?: number } = {}): Promise<GitLabProject[]> {\n return this.#request<GitLabProject[]>('GET', '/api/v4/projects', {\n query: {\n membership: 'true',\n simple: 'true',\n with_issues_enabled: 'true',\n min_access_level: GITLAB_MIN_ACCESS_LEVEL,\n order_by: 'last_activity_at',\n sort: 'desc',\n page: options.page ?? 1,\n per_page: GITLAB_PROJECTS_PAGE_SIZE,\n },\n });\n }\n\n async listIssues(projectId: string, options: { page?: number; labels?: string[] } = {}): Promise<GitLabIssue[]> {\n const issues = await this.#request<GitLabIssueResponse[]>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/issues`,\n {\n query: {\n state: 'opened',\n scope: 'all',\n order_by: 'updated_at',\n sort: 'desc',\n page: options.page ?? 1,\n per_page: GITLAB_ISSUES_PAGE_SIZE,\n labels: options.labels?.length ? options.labels.join(',') : undefined,\n with_labels_details: 'true',\n },\n },\n );\n return issues.map(normalizeIssue);\n }\n\n async getIssue(projectId: string, issueIid: number): Promise<GitLabIssue> {\n // GitLab's single-issue endpoint returns label names even when\n // with_labels_details is requested. The IID-filtered list endpoint returns\n // the same issue with label colors, including for closed issues.\n const issues = await this.#request<GitLabIssueResponse[]>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/issues`,\n { query: { 'iids[]': issueIid, state: 'all', scope: 'all', with_labels_details: 'true' } },\n );\n const issue = issues.find(candidate => candidate.iid === issueIid);\n if (!issue) throw new GitLabApiError(`GitLab issue ${issueIid} was not found`, 404);\n return normalizeIssue(issue);\n }\n\n async listNotes(projectId: string, issueIid: number, options: { page?: number } = {}): Promise<GitLabNote[]> {\n return this.#request<GitLabNote[]>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/notes`,\n {\n query: { order_by: 'created_at', sort: 'asc', page: options.page ?? 1, per_page: GITLAB_NOTES_PAGE_SIZE },\n },\n );\n }\n\n async createNote(projectId: string, issueIid: number, body: string): Promise<GitLabNote> {\n return this.#request<GitLabNote>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/issues/${issueIid}/notes`,\n { body: { body } },\n );\n }\n\n async updateIssueState(projectId: string, issueIid: number, stateEvent: 'close' | 'reopen'): Promise<GitLabIssue> {\n return this.#request<GitLabIssue>('PUT', `/api/v4/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`, {\n body: { state_event: stateEvent },\n });\n }\n\n async listMergeRequests(\n projectId: string,\n options: { page?: number; state?: 'opened' | 'closed' | 'merged' | 'all' } = {},\n ): Promise<GitLabMergeRequest[]> {\n return this.#request<GitLabMergeRequest[]>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests`,\n {\n query: {\n state: options.state ?? 'opened',\n order_by: 'updated_at',\n sort: 'desc',\n page: options.page ?? 1,\n per_page: GITLAB_MERGE_REQUESTS_PAGE_SIZE,\n },\n },\n );\n }\n\n async getMergeRequest(projectId: string, mergeRequestIid: number): Promise<GitLabMergeRequest> {\n return this.#request<GitLabMergeRequest>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`,\n );\n }\n\n async createMergeRequest(\n projectId: string,\n input: {\n sourceBranch: string;\n targetBranch: string;\n title: string;\n description?: string;\n removeSourceBranch?: boolean;\n squash?: boolean;\n },\n ): Promise<GitLabMergeRequest> {\n return this.#request<GitLabMergeRequest>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests`,\n {\n body: {\n source_branch: input.sourceBranch,\n target_branch: input.targetBranch,\n title: input.title,\n description: input.description,\n remove_source_branch: input.removeSourceBranch,\n squash: input.squash,\n },\n },\n );\n }\n\n async updateMergeRequest(\n projectId: string,\n mergeRequestIid: number,\n input: {\n title?: string;\n description?: string;\n targetBranch?: string;\n stateEvent?: 'close' | 'reopen';\n removeSourceBranch?: boolean;\n squash?: boolean;\n reviewerIds?: number[];\n },\n ): Promise<GitLabMergeRequest> {\n return this.#request<GitLabMergeRequest>(\n 'PUT',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`,\n {\n body: {\n title: input.title,\n description: input.description,\n target_branch: input.targetBranch,\n state_event: input.stateEvent,\n remove_source_branch: input.removeSourceBranch,\n squash: input.squash,\n reviewer_ids: input.reviewerIds,\n },\n },\n );\n }\n\n async mergeMergeRequest(\n projectId: string,\n mergeRequestIid: number,\n options: {\n squash?: boolean;\n mergeCommitMessage?: string;\n squashCommitMessage?: string;\n shouldRemoveSourceBranch?: boolean;\n } = {},\n ): Promise<GitLabMergeResult> {\n return this.#request<GitLabMergeResult>(\n 'PUT',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/merge`,\n {\n body: {\n squash: options.squash,\n merge_commit_message: options.mergeCommitMessage,\n squash_commit_message: options.squashCommitMessage,\n should_remove_source_branch: options.shouldRemoveSourceBranch,\n },\n },\n );\n }\n\n async listMergeRequestNotes(\n projectId: string,\n mergeRequestIid: number,\n options: { page?: number } = {},\n ): Promise<GitLabNote[]> {\n return this.#request<GitLabNote[]>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/notes`,\n {\n query: {\n order_by: 'created_at',\n sort: 'asc',\n page: options.page ?? 1,\n per_page: GITLAB_NOTES_PAGE_SIZE,\n },\n },\n );\n }\n\n async createMergeRequestNote(projectId: string, mergeRequestIid: number, body: string): Promise<GitLabNote> {\n return this.#request<GitLabNote>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/notes`,\n { body: { body } },\n );\n }\n\n async getMergeRequestNote(projectId: string, mergeRequestIid: number, noteId: number): Promise<GitLabNote> {\n return this.#request<GitLabNote>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/notes/${noteId}`,\n );\n }\n\n async updateMergeRequestNote(\n projectId: string,\n mergeRequestIid: number,\n noteId: number,\n body: string,\n ): Promise<GitLabNote> {\n return this.#request<GitLabNote>(\n 'PUT',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/notes/${noteId}`,\n { body: { body } },\n );\n }\n\n async deleteMergeRequestNote(projectId: string, mergeRequestIid: number, noteId: number): Promise<void> {\n await this.#request<void>(\n 'DELETE',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/notes/${noteId}`,\n );\n }\n\n async listMergeRequestDiscussions(\n projectId: string,\n mergeRequestIid: number,\n options: { page?: number } = {},\n ): Promise<GitLabDiscussion[]> {\n return this.#request<GitLabDiscussion[]>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions`,\n { query: { page: options.page ?? 1, per_page: GITLAB_DISCUSSIONS_PAGE_SIZE } },\n );\n }\n\n async getMergeRequestDiscussion(\n projectId: string,\n mergeRequestIid: number,\n discussionId: string,\n ): Promise<GitLabDiscussion> {\n return this.#request<GitLabDiscussion>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${encodeURIComponent(discussionId)}`,\n );\n }\n\n async resolveMergeRequestDiscussion(\n projectId: string,\n mergeRequestIid: number,\n discussionId: string,\n resolved: boolean,\n ): Promise<GitLabDiscussion> {\n return this.#request<GitLabDiscussion>(\n 'PUT',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${encodeURIComponent(discussionId)}`,\n { body: { resolved } },\n );\n }\n\n async setCommitStatus(\n projectId: string,\n sha: string,\n status: { state: GitLabCommitStatusState; name: string; ref?: string; description?: string; targetUrl?: string },\n ): Promise<GitLabCommitStatus> {\n return this.#request<GitLabCommitStatus>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/statuses/${encodeURIComponent(sha)}`,\n {\n body: {\n state: status.state,\n name: status.name,\n ref: status.ref,\n description: status.description,\n target_url: status.targetUrl,\n },\n },\n );\n }\n\n async createMergeRequestDiscussion(\n projectId: string,\n mergeRequestIid: number,\n input: { body: string; commitId?: string; position?: GitLabDiscussionPosition },\n ): Promise<GitLabDiscussion> {\n return this.#request<GitLabDiscussion>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions`,\n { body: { body: input.body, commit_id: input.commitId, position: input.position } },\n );\n }\n\n async addMergeRequestDiscussionNote(\n projectId: string,\n mergeRequestIid: number,\n discussionId: string,\n body: string,\n ): Promise<GitLabDiscussionNote> {\n return this.#request<GitLabDiscussionNote>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${encodeURIComponent(discussionId)}/notes`,\n { body: { body } },\n );\n }\n\n async updateMergeRequestDiscussionNote(\n projectId: string,\n mergeRequestIid: number,\n discussionId: string,\n noteId: number,\n body: string,\n ): Promise<GitLabDiscussionNote> {\n return this.#request<GitLabDiscussionNote>(\n 'PUT',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${encodeURIComponent(discussionId)}/notes/${noteId}`,\n { body: { body } },\n );\n }\n\n async deleteMergeRequestDiscussionNote(\n projectId: string,\n mergeRequestIid: number,\n discussionId: string,\n noteId: number,\n ): Promise<void> {\n await this.#request<void>(\n 'DELETE',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/discussions/${encodeURIComponent(discussionId)}/notes/${noteId}`,\n );\n }\n\n async getMergeRequestApprovals(projectId: string, mergeRequestIid: number): Promise<GitLabMergeRequestApprovals> {\n return this.#request<GitLabMergeRequestApprovals>(\n 'GET',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/approvals`,\n );\n }\n\n async approveMergeRequest(projectId: string, mergeRequestIid: number, sha?: string): Promise<GitLabMergeRequest> {\n return this.#request<GitLabMergeRequest>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/approve`,\n sha ? { body: { sha } } : undefined,\n );\n }\n\n async unapproveMergeRequest(projectId: string, mergeRequestIid: number): Promise<GitLabMergeRequest> {\n return this.#request<GitLabMergeRequest>(\n 'POST',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}/unapprove`,\n );\n }\n\n async setMergeRequestReviewers(\n projectId: string,\n mergeRequestIid: number,\n reviewerIds: number[],\n ): Promise<GitLabMergeRequest> {\n return this.#request<GitLabMergeRequest>(\n 'PUT',\n `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${mergeRequestIid}`,\n { body: { reviewer_ids: reviewerIds } },\n );\n }\n\n async listProjectMembers(\n projectId: string,\n options: { query?: string; page?: number } = {},\n ): Promise<GitLabMember[]> {\n return this.#request<GitLabMember[]>('GET', `/api/v4/projects/${encodeURIComponent(projectId)}/members/all`, {\n query: {\n query: options.query,\n page: options.page ?? 1,\n per_page: GITLAB_DISCUSSIONS_PAGE_SIZE,\n },\n });\n }\n\n async #request<T>(\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n options: { query?: Record<string, string | number | undefined>; body?: unknown } = {},\n ): Promise<T> {\n const query = new URLSearchParams();\n for (const [key, value] of Object.entries(options.query ?? {})) {\n if (value !== undefined) query.set(key, String(value));\n }\n const suffix = query.size > 0 ? `?${query.toString()}` : '';\n // Repository targets carry both the immutable numeric ID for GitLab API\n // requests and the path for browser links. A Platform proxy decodes %2F,\n // so forwarding a path-based project locator would turn into a 404.\n const apiPath = path.replace(/^\\/api\\/v4\\/projects\\/([^/]+)/, (full, encoded: string) => {\n const target = decodeURIComponent(encoded);\n const match = /^(\\d+):(.+)$/.exec(target);\n return match ? `/api/v4/projects/${encodeURIComponent(match[1]!)}` : full;\n });\n\n if (this.#platform) {\n const proxyPath = `/v2/connections/${encodeURIComponent(this.#platform.connectionId)}/proxy${apiPath}${suffix}`;\n try {\n return await this.#platform.client.request<T>(method, proxyPath, options.body);\n } catch (error) {\n if (error instanceof PlatformApiError) throw new GitLabApiError(error.message, error.status);\n throw new GitLabApiError(error instanceof Error ? error.message : String(error), null);\n }\n }\n\n const direct = this.#direct!;\n const headers: Record<string, string> = {\n accept: 'application/json',\n 'private-token': direct.accessToken,\n };\n const init: RequestInit = { method, headers, signal: AbortSignal.timeout(15_000) };\n if (options.body !== undefined) {\n headers['content-type'] = 'application/json';\n init.body = JSON.stringify(options.body);\n }\n\n let response: Response;\n try {\n response = await direct.fetch(`${direct.baseUrl}${apiPath}${suffix}`, init);\n } catch (error) {\n const message = (error instanceof Error ? error.message : String(error))\n .split(direct.accessToken)\n .join('[REDACTED]');\n throw new GitLabApiError(message, null);\n }\n if (!response.ok) {\n const message = (await extractError(response)).split(direct.accessToken).join('[REDACTED]');\n throw new GitLabApiError(message, response.status);\n }\n if (response.status === 204) return undefined as T;\n return (await response.json()) as T;\n }\n}\n\nasync function extractError(response: Response): Promise<string> {\n try {\n const data = (await response.clone().json()) as Record<string, unknown>;\n if (typeof data.message === 'string' && data.message) return data.message;\n if (typeof data.error === 'string' && data.error) return data.error;\n } catch {\n // Fall through to the status-based message.\n }\n return `GitLab API request failed (${response.status})`;\n}\n\nfunction normalizeIssue(response: GitLabIssueResponse): GitLabIssue {\n const { labels, ...issue } = response;\n const labelDetails = (labels ?? []).filter((label): label is GitLabLabelDetail => typeof label !== 'string');\n return {\n ...issue,\n labels: (labels ?? []).map(label => (typeof label === 'string' ? label : label.name)),\n ...(labelDetails.length > 0 ? { labelDetails } : {}),\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","names":["#orgIdByResourceId","#resolveIntakeDispatch","#listSources","#listItems","#listIssues","#getIssue","#createComment","#updateIssue","#versionControlContext","#versionControlContextForInstallation","#projects","#auth","#sourceControl","#integrationStorage","#resolveSourceReference","#contextForReference","#toIntakeSource","#listIssuePage","#toIntakeIssue","#resolveRequest","#listAllNotes","#accessToken","#accessTokenType","#baseUrl","#webhookSecret","#context"],"sources":["../../../src/integrations/gitlab/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\n\nimport type { IntegrationConnection } from '../../capabilities/connection.js';\nimport type {\n CreateIntakeCommentInput,\n CreatedIntakeComment,\n GetIntakeIssueInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n IntakeItemPage,\n IntakeSource,\n ListIntakeIssuesInput,\n ListIntakeItemsInput,\n ResolveIntakeDispatchInput,\n ResolvedIntakeDispatch,\n UpdateIntakeIssueInput,\n} from '../../capabilities/intake.js';\nimport type { RouteAuth } from '../../routes/route.js';\nimport type { IntegrationStorageHandle } from '../../storage/domains/integrations/base.js';\nimport type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';\nimport type { SourceControlStorageHandle } from '../../storage/domains/source-control/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../base.js';\nimport { IssueReconcileWorker } from '../issue-reconcile-worker.js';\nimport { buildGitLabAgentTools } from './agent-tools.js';\nimport {\n GITLAB_ISSUES_PAGE_SIZE,\n GITLAB_NOTES_PAGE_SIZE,\n GITLAB_PROJECTS_PAGE_SIZE,\n GitLabApiClient,\n GitLabApiError,\n} from './api.js';\nimport type { GitLabIssue, GitLabNote, GitLabProject } from './api.js';\nimport { resolveGitLabRules } from './default-rules.js';\nimport type { GitLabEventRules, GitLabRuleOverrides } from './default-rules.js';\nimport { attachGitLabReconciler } from './reconciler.js';\nimport { gitlabReconciliationEnabled, gitlabReconciliationInterval } from './reconciliation-config.js';\nimport { buildGitLabRoutes } from './routes.js';\nimport { attachGitLabRules } from './rules.js';\nimport {\n createGitLabSubscriptionTools,\n parseCreatedMergeRequest,\n subscribeCurrentSessionToMergeRequest,\n} from './session-subscriptions.js';\nimport type { GitLabSubscriptionStorage } from './subscriptions.js';\nimport { buildGitLabVersionControl } from './version-control.js';\n\ninterface GitLabConnectionContext {\n id: string;\n label: string | null;\n api: GitLabApiClient;\n connection: IntegrationConnection;\n host: string;\n webBaseUrl?: string;\n /** Resolves a fresh provider token for a single brokered git operation. */\n repositoryAccessToken?: () => Promise<string>;\n}\n\ninterface GitLabSourceReference {\n /** Present only on legacy v1 ids; authentication is not resource identity. */\n connectionId?: string;\n /** Canonical v2 ids use the GitLab instance host plus immutable project id. */\n host?: string;\n projectId: string;\n /** Legacy ids carry display metadata; v2 resolves it from GitLab. */\n projectPath?: string;\n}\n\ninterface ResolvedGitLabSourceReference extends GitLabSourceReference {\n connectionId: string;\n host: string;\n projectPath: string;\n}\n\ninterface GitLabIssueReference extends GitLabSourceReference {\n issueIid: number;\n}\n\nexport interface GitLabMergeRequestReference {\n version: 1;\n host: string;\n projectId: number;\n mergeRequestIid: number;\n}\n\ninterface GitLabPageCursor {\n source: number;\n page: number;\n}\n\nexport interface GitLabStatusConnection {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n}\n\nconst DIRECT_CONNECTION_ID = 'direct';\nconst GITLAB_CONNECTION_TOKEN_PREFIX = 'gitlab-connection:';\nconst GITLAB_SOURCE_PREFIX = 'gitlab-project:';\nconst GITLAB_ISSUE_PREFIX = 'gitlab-issue:';\nconst GITLAB_MERGE_REQUEST_PREFIX = 'gitlab-pr:';\nexport const GITLAB_TRUSTED_ACCESS_LEVEL = 30;\n// Bound issue detail reads to 2,000 notes. GitLab does not expose truncation through the Intake contract.\nconst MAX_NOTES_PAGES = 20;\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport abstract class GitLabIntegrationBase implements FactoryIntegration {\n readonly id = 'gitlab';\n #projects: FactoryProjectsStorage | undefined;\n #sourceControl: SourceControlStorageHandle | undefined;\n #integrationStorage: GitLabSubscriptionStorage | undefined;\n #auth: RouteAuth | undefined;\n readonly #orgIdByResourceId = new Map<string, string | null>();\n readonly rules: GitLabEventRules;\n\n constructor(rules?: GitLabRuleOverrides) {\n this.rules = resolveGitLabRules(rules);\n }\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: () => this.#listSources(),\n listItems: input => this.#listItems(input),\n listIssues: input => this.#listIssues(input),\n getIssue: input => this.#getIssue(input),\n createComment: input => this.#createComment(input),\n updateIssue: input => this.#updateIssue(input),\n };\n readonly versionControl = buildGitLabVersionControl({\n contextForConnection: connection => this.#versionControlContext(connection),\n contextForStoredInstallation: (connection, host) => this.#versionControlContextForInstallation(connection, host),\n });\n initialize({\n storage,\n projects,\n auth,\n sourceControl,\n }: {\n storage?: IntegrationStorageHandle;\n projects: FactoryProjectsStorage;\n auth: RouteAuth;\n sourceControl?: SourceControlStorageHandle;\n }): void {\n this.#projects = projects;\n this.#auth = auth;\n this.#sourceControl = sourceControl;\n this.#integrationStorage = storage as GitLabSubscriptionStorage | undefined;\n }\n\n /** Generic integration persistence (merge-request subscriptions). */\n get integrationStorage(): GitLabSubscriptionStorage {\n if (!this.#integrationStorage) {\n throw new Error(`${this.constructor.name} is not initialized — the factory binds storage during prepare().`);\n }\n return this.#integrationStorage;\n }\n\n /** Source-control rows for this integration; absent when the host runs without them. */\n get sourceControlStorage(): SourceControlStorageHandle | undefined {\n return this.#sourceControl;\n }\n\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n get projects(): FactoryProjectsStorage {\n if (!this.#projects) {\n throw new Error(`${this.constructor.name} is not initialized — the factory binds storage during prepare().`);\n }\n return this.#projects;\n }\n\n async resolveOrgId(resourceId: string): Promise<string | null> {\n const cached = this.#orgIdByResourceId.get(resourceId);\n if (cached !== undefined) return cached;\n if (!UUID_PATTERN.test(resourceId)) {\n this.#orgIdByResourceId.set(resourceId, null);\n return null;\n }\n try {\n await this.projects.ensureReady();\n const project = await this.projects.getById({ id: resourceId });\n const orgId = project?.orgId ?? null;\n this.#orgIdByResourceId.set(resourceId, orgId);\n return orgId;\n } catch {\n return null;\n }\n }\n\n clearCaches(): void {\n this.#orgIdByResourceId.clear();\n }\n\n abstract hasActiveConnections(): Promise<boolean>;\n abstract authFailureMessage(): string;\n protected abstract activeContexts(): Promise<GitLabConnectionContext[]>;\n protected abstract contextById(connectionId: string): Promise<GitLabConnectionContext>;\n\n /** Resolve only a GitLab repository linked to this Factory in this organization. */\n async getLinkedRepository(input: { orgId: string; factoryProjectId: string; projectRepositoryId: string }) {\n if (!this.#sourceControl) return null;\n const connections = await this.#sourceControl.connections.list({\n orgId: input.orgId,\n factoryProjectId: input.factoryProjectId,\n });\n for (const connection of connections) {\n if (connection.integrationId !== 'gitlab') continue;\n const links = await this.#sourceControl.projectRepositories.list({\n orgId: input.orgId,\n connectionId: connection.id,\n });\n const link = links.find(candidate => candidate.id === input.projectRepositoryId);\n if (!link) continue;\n const repository = await this.#sourceControl.repositories.get({ orgId: input.orgId, id: link.repositoryId });\n const installation = await this.#sourceControl.installations.get({\n orgId: input.orgId,\n id: connection.installationId,\n });\n if (!repository || !installation) return null;\n const host = installation.providerMetadata.host;\n if (typeof host !== 'string' || !host) return null;\n return { repository, host: normalizeGitLabHost(host) };\n }\n return null;\n }\n\n async getProjectMemberAccessLevel(\n connectionId: string,\n projectId: string,\n username: string,\n ): Promise<number | undefined> {\n const normalized = username.trim().toLowerCase();\n if (!normalized) return undefined;\n const members = await (await this.contextById(connectionId)).api.listProjectMembers(projectId, { query: username });\n const member = members.find(\n candidate => candidate.username.toLowerCase() === normalized && candidate.state !== 'blocked',\n );\n return member?.access_level;\n }\n\n async getWorkItemAuthorUsername(\n connectionId: string,\n projectId: string,\n kind: 'issue' | 'merge_request',\n iid: number,\n ): Promise<string | undefined> {\n const api = (await this.contextById(connectionId)).api;\n const item = kind === 'issue' ? await api.getIssue(projectId, iid) : await api.getMergeRequest(projectId, iid);\n return item.author?.username;\n }\n\n async isProjectMemberTrustedForSource(sourceId: string, username: string): Promise<boolean> {\n const reference = decodeSourceId(sourceId);\n if (!reference) throw new GitLabApiError('GitLab source identity is invalid.', 400);\n const { context, source } = await this.#resolveSourceReference(reference);\n const accessLevel = await this.getProjectMemberAccessLevel(context.id, source.projectId, username);\n return (accessLevel ?? 0) >= GITLAB_TRUSTED_ACCESS_LEVEL;\n }\n\n async getIssueForFactoryProject(input: {\n orgId: string;\n factoryProjectId: string;\n issueId: string;\n }): Promise<IntakeIssueDetail | null> {\n if (!this.#sourceControl) throw new GitLabApiError('GitLab source control is unavailable.', 503);\n const reference = decodeIssueReference(input.issueId);\n const locator = parseIssueLocator(input.issueId);\n if (!reference && !locator) {\n throw new GitLabApiError('GitLab issue must include a project and issue IID.', 400);\n }\n\n const normalizePath = (value: string) => value.replace(/^\\/+|\\/+$/g, '').toLowerCase();\n const matches: Array<{\n connectionId: string;\n projectId: string;\n projectPath: string;\n }> = [];\n const connections = await this.#sourceControl.connections.list({\n orgId: input.orgId,\n factoryProjectId: input.factoryProjectId,\n });\n for (const connection of connections) {\n const installation = await this.#sourceControl.installations.get({\n orgId: input.orgId,\n id: connection.installationId,\n });\n if (!installation) continue;\n for (const projectRepository of await this.#sourceControl.projectRepositories.list({\n orgId: input.orgId,\n connectionId: connection.id,\n })) {\n const repository = await this.#sourceControl.repositories.get({\n orgId: input.orgId,\n id: projectRepository.repositoryId,\n });\n if (!repository) continue;\n if (reference?.projectId && repository.externalId !== reference.projectId) continue;\n if (reference?.projectPath && normalizePath(repository.slug) !== normalizePath(reference.projectPath)) continue;\n if (locator?.projectPath && normalizePath(repository.slug) !== normalizePath(locator.projectPath)) continue;\n if (reference?.connectionId && installation.externalId !== reference.connectionId) continue;\n const installationHost =\n typeof installation.providerMetadata.host === 'string'\n ? normalizeGitLabHost(installation.providerMetadata.host)\n : null;\n if (reference?.host && installationHost !== normalizeGitLabHost(reference.host)) continue;\n if (locator?.host && installationHost !== normalizeGitLabHost(locator.host)) continue;\n matches.push({\n connectionId: installation.externalId,\n projectId: repository.externalId,\n projectPath: repository.slug,\n });\n }\n }\n const match = matches.sort((left, right) => left.connectionId.localeCompare(right.connectionId))[0];\n if (!match) throw new GitLabApiError('GitLab issue is outside the active Factory project.', 404);\n return this.intake.getIssue({\n connection: gitlabConnection(match.connectionId),\n sourceId: encodeSourceId(match),\n issueId: input.issueId,\n });\n }\n\n /** Platform mode exposes org connections; direct mode returns undefined. */\n async statusConnections(): Promise<GitLabStatusConnection[] | undefined> {\n return undefined;\n }\n\n /** Direct mode verifies its configured credential; Platform mode uses connection status. */\n async verifyStatus(): Promise<void> {\n // Platform connection state is authoritative unless a provider overrides this probe.\n }\n\n protected get webhookSecret(): string | undefined {\n return undefined;\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n if (!gitlabReconciliationEnabled()) return [];\n const reconcile = attachGitLabReconciler(this, ctx);\n if (!reconcile) return [];\n const intervalMs = gitlabReconciliationInterval();\n return [\n new IssueReconcileWorker({\n integrationId: this.id,\n reconcile,\n ...(intervalMs ? { intervalMs } : {}),\n }),\n ];\n }\n routes(ctx: IntegrationContext): ApiRoute[] {\n const ingestFactoryEvent = attachGitLabRules(this, ctx);\n return buildGitLabRoutes({\n gitlab: this,\n auth: ctx.auth,\n intake: ctx.storage?.intake,\n emitAudit: ctx.hooks?.emitAudit,\n sandbox: ctx.sandbox,\n webhookSecret: this.webhookSecret,\n ingestFactoryEvent,\n ...(ctx.controller ? { controller: ctx.controller } : {}),\n });\n }\n\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildGitLabAgentTools({ requestContext: args.requestContext, gitlab: this });\n }\n\n sessionTools({ requestContext }: { requestContext: RequestContext }): IntegrationTools {\n return createGitLabSubscriptionTools(requestContext, this);\n }\n\n async postToolObserver({\n toolContext,\n requestContext,\n }: Parameters<NonNullable<FactoryIntegration['postToolObserver']>>[0]): Promise<void> {\n const mergeRequestUrl = parseCreatedMergeRequest(toolContext);\n if (!mergeRequestUrl || !requestContext) return;\n await subscribeCurrentSessionToMergeRequest(requestContext, mergeRequestUrl, 'auto-create-change-request', this);\n }\n\n abstract diagnostics(): Record<string, unknown>;\n\n /** Prefer the configured direct token over an older Platform installation on the same host. */\n async resolveActiveConnectionForHost(storedConnectionId: string, host: string): Promise<string> {\n if (this.diagnostics().mode !== 'direct') return storedConnectionId;\n const [direct] = await this.activeContexts();\n return direct && normalizeGitLabHost(direct.host) === normalizeGitLabHost(host) ? direct.id : storedConnectionId;\n }\n\n async #versionControlContext(connection: IntegrationConnection): Promise<GitLabConnectionContext> {\n const connectionId = connectionIdFromConnection(connection);\n if (connectionId) return this.contextById(connectionId);\n const contexts = await this.activeContexts();\n if (contexts.length !== 1) {\n throw new GitLabApiError(\n 'GitLab version-control requests must identify a connection when multiple GitLab accounts are connected.',\n 400,\n );\n }\n return contexts[0]!;\n }\n async #versionControlContextForInstallation(\n connection: IntegrationConnection,\n host: string | undefined,\n ): Promise<GitLabConnectionContext> {\n // A direct PAT intentionally overrides Platform credentials. Old links can\n // retain their Platform connection id; use the PAT only for the same host.\n if (this.diagnostics().mode === 'direct' && host) {\n const [direct] = await this.activeContexts();\n if (direct && normalizeGitLabHost(host) === normalizeGitLabHost(direct.host)) return direct;\n }\n return this.#versionControlContext(connection);\n }\n async #contextForReference(reference: GitLabSourceReference): Promise<GitLabConnectionContext> {\n if (reference.connectionId) return this.contextById(reference.connectionId);\n const contexts = (await this.activeContexts()).filter(\n context => !reference.host || normalizeGitLabHost(context.host) === normalizeGitLabHost(reference.host),\n );\n if (contexts.length === 0) {\n throw new GitLabApiError('No active GitLab connection can access this source.', 401);\n }\n if (contexts.length === 1) return contexts[0]!;\n\n if (this.#sourceControl) {\n const linkedConnectionIds = new Set(\n (await this.#sourceControl.projectRepositories.listConfiguredExternalKeys())\n .filter(key => key.repositoryExternalId === reference.projectId)\n .map(key => key.installationExternalId),\n );\n const linked = contexts.filter(context => linkedConnectionIds.has(context.id));\n if (linked.length > 0) return linked.sort((left, right) => left.id.localeCompare(right.id))[0]!;\n }\n\n for (const context of contexts.sort((left, right) => left.id.localeCompare(right.id))) {\n try {\n await context.api.getProject(reference.projectId);\n return context;\n } catch (error) {\n if (!(error instanceof GitLabApiError) || ![401, 403, 404].includes(error.status ?? 0)) throw error;\n // This credential cannot access the project; try another active account for the same canonical source.\n }\n }\n throw new GitLabApiError('No active GitLab connection can access this source.', 404);\n }\n\n async #resolveSourceReference(\n reference: GitLabSourceReference,\n ): Promise<{ context: GitLabConnectionContext; source: ResolvedGitLabSourceReference }> {\n const context = await this.#contextForReference(reference);\n const projectPath =\n reference.projectPath ?? (await context.api.getProject(reference.projectId)).path_with_namespace;\n return {\n context,\n source: {\n connectionId: context.id,\n host: normalizeGitLabHost(reference.host ?? context.host),\n projectId: reference.projectId,\n projectPath,\n },\n };\n }\n\n async #resolveIntakeDispatch({ externalSource }: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> {\n if (externalSource.type !== 'issue') return null;\n const reference = decodeIssueReference(externalSource.externalId);\n if (!reference) return null;\n const { context, source } = await this.#resolveSourceReference(reference);\n return {\n connection: context.connection,\n sourceId: encodeSourceId(source),\n issueId: String(reference.issueIid),\n };\n }\n\n async #listSources(): Promise<IntakeSource[]> {\n const sources = new Map<string, IntakeSource>();\n for (const context of await this.activeContexts()) {\n for (let page = 1; ; page++) {\n const projects = await context.api.listProjects({ page });\n for (const project of projects) {\n const source = this.#toIntakeSource(context, project);\n if (!sources.has(source.id)) sources.set(source.id, source);\n }\n if (projects.length < GITLAB_PROJECTS_PAGE_SIZE) break;\n }\n }\n return [...sources.values()];\n }\n\n async #listItems(input: ListIntakeItemsInput): Promise<IntakeItemPage> {\n const result = await this.#listIssuePage(input.sourceIds, input.cursor);\n return {\n items: result.issues.map(({ issue, source, context }) => ({\n source: {\n type: 'issue',\n externalId: encodeIssueReference({ ...source, issueIid: issue.iid }),\n url: issue.web_url,\n },\n sourceId: encodeSourceId(source),\n title: `${source.projectPath}#${issue.iid}: ${issue.title}`,\n status: issue.state,\n labels: issue.labels ?? [],\n assignee: displayName(issue.assignee),\n createdAt: issue.created_at,\n updatedAt: issue.updated_at,\n metadata: {\n identifier: `${source.projectPath}#${issue.iid}`,\n projectId: source.projectId,\n projectPath: source.projectPath,\n connectionId: context.id,\n accountLabel: context.label,\n labelColors: Object.fromEntries((issue.labelDetails ?? []).map(label => [label.name, label.color])),\n },\n })),\n nextCursor: result.nextCursor,\n };\n }\n\n async #listIssues(input: ListIntakeIssuesInput) {\n const result = await this.#listIssuePage(input.sourceIds, input.cursor, input.labels);\n return {\n issues: result.issues.map(({ issue, source }) => ({\n ...this.#toIntakeIssue(issue, source.projectPath),\n sourceId: encodeSourceId(source),\n })),\n nextCursor: result.nextCursor,\n };\n }\n\n async #listIssuePage(sourceIds: string[], cursor?: string, labels?: string[]) {\n const sources = sourceIds.map(decodeSourceId).filter((source): source is GitLabSourceReference => source !== null);\n if (sources.length === 0) return { issues: [], nextCursor: null };\n const current = decodePageCursor(cursor);\n if (!current || current.source >= sources.length) return { issues: [], nextCursor: null };\n\n for (let sourceIndex = current.source; sourceIndex < sources.length; sourceIndex++) {\n const { source, context } = await this.#resolveSourceReference(sources[sourceIndex]!);\n const page = sourceIndex === current.source ? current.page : 1;\n const issues = await context.api.listIssues(source.projectId, { page, labels });\n const nextCursor =\n issues.length === GITLAB_ISSUES_PAGE_SIZE\n ? encodePageCursor({ source: sourceIndex, page: page + 1 })\n : sourceIndex + 1 < sources.length\n ? encodePageCursor({ source: sourceIndex + 1, page: 1 })\n : null;\n return { issues: issues.map(issue => ({ issue, source, context })), nextCursor };\n }\n return { issues: [], nextCursor: null };\n }\n\n async #getIssue(input: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> {\n const resolved = await this.#resolveRequest(input);\n let issue: GitLabIssue;\n try {\n issue = await resolved.context.api.getIssue(resolved.projectId, resolved.issueIid);\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n const notes = await this.#listAllNotes(resolved.context.api, resolved.projectId, resolved.issueIid);\n return {\n ...this.#toIntakeIssue(issue, resolved.projectPath),\n description: issue.description?.trim() || null,\n comments: notes\n .filter(note => !note.system)\n .map(note => ({ author: displayName(note.author), body: note.body, createdAt: note.created_at })),\n };\n }\n\n async #listAllNotes(api: GitLabApiClient, projectId: string, issueIid: number): Promise<GitLabNote[]> {\n const notes: GitLabNote[] = [];\n for (let page = 1; page <= MAX_NOTES_PAGES; page++) {\n const result = await api.listNotes(projectId, issueIid, { page });\n notes.push(...result);\n if (result.length < GITLAB_NOTES_PAGE_SIZE) break;\n }\n return notes;\n }\n\n async #createComment(input: CreateIntakeCommentInput): Promise<CreatedIntakeComment | null> {\n const resolved = await this.#resolveRequest(input);\n try {\n const issue = await resolved.context.api.getIssue(resolved.projectId, resolved.issueIid);\n const note = await resolved.context.api.createNote(resolved.projectId, resolved.issueIid, input.body);\n return { id: String(note.id), url: `${issue.web_url}#note_${note.id}` };\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n }\n\n async #updateIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n const resolved = await this.#resolveRequest(input);\n let issue: GitLabIssue;\n try {\n issue = await resolved.context.api.getIssue(resolved.projectId, resolved.issueIid);\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n const stateEvent = targetStateEvent(input);\n if (!stateEvent) return null;\n if ((stateEvent === 'close' && issue.state === 'closed') || (stateEvent === 'reopen' && issue.state === 'opened')) {\n return this.#toIntakeIssue(issue, resolved.projectPath);\n }\n const updated = await resolved.context.api.updateIssueState(resolved.projectId, resolved.issueIid, stateEvent);\n return this.#toIntakeIssue(updated, resolved.projectPath);\n }\n\n async #resolveRequest(input: GetIntakeIssueInput): Promise<{\n context: GitLabConnectionContext;\n projectId: string;\n projectPath: string;\n issueIid: number;\n }> {\n const issueReference = decodeIssueReference(input.issueId);\n const source = input.sourceId ? decodeSourceId(input.sourceId) : null;\n const locator = parseIssueLocator(input.issueId);\n const reference = issueReference ?? source;\n let context: GitLabConnectionContext;\n if (reference) {\n context = await this.#contextForReference(reference);\n } else {\n const connectionId = connectionIdFromConnection(input.connection);\n const contexts = connectionId ? [await this.contextById(connectionId)] : await this.activeContexts();\n if (contexts.length !== 1) {\n throw new GitLabApiError('GitLab issue reference must resolve to exactly one active connection.', 400);\n }\n context = contexts[0]!;\n }\n const projectId = issueReference?.projectId ?? source?.projectId ?? locator?.projectPath;\n const issueIid = issueReference?.issueIid ?? locator?.issueIid ?? parsePositiveInteger(input.issueId);\n if (!projectId || !issueIid) {\n throw new GitLabApiError(\n 'GitLab issue must include a project and issue IID (for example, group/project#42).',\n 400,\n );\n }\n const projectPath =\n issueReference?.projectPath ??\n source?.projectPath ??\n locator?.projectPath ??\n (await context.api.getProject(projectId)).path_with_namespace;\n return { context, projectId, projectPath, issueIid };\n }\n\n #toIntakeSource(context: GitLabConnectionContext, project: GitLabProject): IntakeSource {\n const reference: GitLabSourceReference = {\n host: normalizeGitLabHost(context.host),\n projectId: String(project.id),\n projectPath: project.path_with_namespace,\n };\n return {\n id: encodeSourceId(reference),\n name: project.path_with_namespace,\n type: 'project',\n metadata: {\n projectId: String(project.id),\n projectPath: project.path_with_namespace,\n defaultBranch: project.default_branch ?? null,\n connectionId: context.id,\n accountLabel: context.label,\n url: project.web_url,\n },\n };\n }\n\n #toIntakeIssue(issue: GitLabIssue, projectPath: string): IntakeIssue {\n const assignees = issue.assignees?.map(displayName).filter((name): name is string => name !== null) ?? [];\n return {\n id: String(issue.iid),\n identifier: `${projectPath}#${issue.iid}`,\n title: issue.title,\n url: issue.web_url,\n author: displayName(issue.author),\n authorUsername: issue.author?.username ?? null,\n state: issue.state,\n stateType: issue.state === 'closed' ? 'completed' : 'unstarted',\n priority: typeof issue.weight === 'number' ? String(issue.weight) : null,\n assignee: displayName(issue.assignee) ?? assignees[0] ?? null,\n assignees,\n source: projectPath,\n labels: issue.labels ?? [],\n labelColors: Object.fromEntries((issue.labelDetails ?? []).map(label => [label.name, label.color])),\n commentCount: issue.user_notes_count ?? null,\n createdAt: issue.created_at,\n updatedAt: issue.updated_at,\n };\n }\n}\n\nexport type GitLabAccessTokenType = 'personal' | 'group';\n\nexport interface GitLabIntegrationConfig {\n rules?: GitLabRuleOverrides;\n accessToken?: string;\n /** Defaults to `personal`; both token types authenticate identically but have different resource reach. */\n accessTokenType?: GitLabAccessTokenType;\n baseUrl?: string;\n webhookSecret?: string;\n fetchImpl?: typeof fetch;\n}\n\nconst DIRECT_CONNECTION_TOKEN = 'gitlab-direct-access-token';\n\nexport class GitLabIntegration extends GitLabIntegrationBase {\n readonly #accessToken: string;\n readonly #accessTokenType: GitLabAccessTokenType;\n readonly #baseUrl: string;\n readonly #webhookSecret: string | undefined;\n readonly #context: GitLabConnectionContext;\n\n constructor(config: GitLabIntegrationConfig = {}) {\n super(config.rules);\n const accessToken = config.accessToken?.trim() || process.env.GITLAB_ACCESS_TOKEN?.trim();\n if (!accessToken) {\n throw new Error('GitLabIntegration: missing required GITLAB_ACCESS_TOKEN.');\n }\n this.#accessToken = accessToken;\n this.#accessTokenType = parseAccessTokenType(\n config.accessTokenType ?? process.env.GITLAB_ACCESS_TOKEN_TYPE?.trim(),\n );\n this.#baseUrl = (config.baseUrl ?? process.env.GITLAB_BASE_URL?.trim() ?? 'https://gitlab.com').replace(/\\/+$/, '');\n this.#webhookSecret = config.webhookSecret?.trim() || process.env.GITLAB_WEBHOOK_SECRET?.trim() || undefined;\n const api = new GitLabApiClient({\n baseUrl: this.#baseUrl,\n accessToken: this.#accessToken,\n ...(config.fetchImpl ? { fetchImpl: config.fetchImpl } : {}),\n });\n this.#context = {\n id: DIRECT_CONNECTION_ID,\n label: new URL(this.#baseUrl).host,\n api,\n connection: { type: 'oauth', accessToken: DIRECT_CONNECTION_TOKEN },\n host: new URL(this.#baseUrl).host,\n webBaseUrl: this.#baseUrl,\n repositoryAccessToken: async () => this.#accessToken,\n };\n }\n\n protected override get webhookSecret(): string | undefined {\n return this.#webhookSecret;\n }\n\n async hasActiveConnections(): Promise<boolean> {\n return true;\n }\n\n override async verifyStatus(): Promise<void> {\n await this.#context.api.getCurrentUser();\n }\n\n authFailureMessage(): string {\n return `GitLab rejected the configured ${this.#accessTokenType} access token. Check the token and its scopes.`;\n }\n\n protected async activeContexts(): Promise<GitLabConnectionContext[]> {\n return [this.#context];\n }\n\n protected async contextById(connectionId: string): Promise<GitLabConnectionContext> {\n if (connectionId !== DIRECT_CONNECTION_ID) {\n throw new GitLabApiError('GitLab connection is unavailable.', 401);\n }\n return this.#context;\n }\n\n diagnostics(): Record<string, unknown> {\n return {\n configured: true,\n mode: 'direct',\n accessTokenType: this.#accessTokenType,\n endpointHost: new URL(this.#baseUrl).host,\n webhookConfigured: Boolean(this.#webhookSecret),\n };\n }\n}\n\nfunction parseAccessTokenType(value: string | undefined): GitLabAccessTokenType {\n const normalized = value?.trim().toLowerCase() || 'personal';\n if (normalized === 'personal' || normalized === 'group') return normalized;\n throw new Error(\"GitLabIntegration: GITLAB_ACCESS_TOKEN_TYPE must be 'personal' or 'group'.\");\n}\n\nexport function encodeSourceId(reference: GitLabSourceReference): string {\n if (reference.host) {\n return `${GITLAB_SOURCE_PREFIX}${encodeOpaque({\n version: 2,\n host: normalizeGitLabHost(reference.host),\n projectId: reference.projectId,\n })}`;\n }\n if (!reference.connectionId || !reference.projectPath) {\n throw new Error('GitLab source reference is missing its instance host.');\n }\n return `${GITLAB_SOURCE_PREFIX}${encodeOpaque(reference)}`;\n}\n\nexport function decodeSourceId(value: string): GitLabSourceReference | null {\n return decodeOpaque(value, GITLAB_SOURCE_PREFIX, isSourceReference);\n}\n\nexport function encodeIssueReference(reference: GitLabIssueReference): string {\n if (reference.host) {\n return `${GITLAB_ISSUE_PREFIX}${encodeOpaque({\n version: 2,\n host: normalizeGitLabHost(reference.host),\n projectId: reference.projectId,\n issueIid: reference.issueIid,\n })}`;\n }\n if (!reference.connectionId || !reference.projectPath) {\n throw new Error('GitLab issue reference is missing its instance host.');\n }\n return `${GITLAB_ISSUE_PREFIX}${encodeOpaque(reference)}`;\n}\n\nexport function decodeIssueReference(value: string): GitLabIssueReference | null {\n return decodeOpaque(value, GITLAB_ISSUE_PREFIX, isIssueReference);\n}\n\nexport function decodeMergeRequestReference(value: string): GitLabMergeRequestReference | null {\n return decodeOpaque(value, GITLAB_MERGE_REQUEST_PREFIX, isMergeRequestReference);\n}\n\nexport function gitlabConnection(connectionId: string): IntegrationConnection {\n return { type: 'oauth', accessToken: `${GITLAB_CONNECTION_TOKEN_PREFIX}${connectionId}` };\n}\n\nfunction connectionIdFromConnection(connection: IntegrationConnection): string | null {\n if (connection.type !== 'oauth' || !connection.accessToken.startsWith(GITLAB_CONNECTION_TOKEN_PREFIX)) return null;\n return connection.accessToken.slice(GITLAB_CONNECTION_TOKEN_PREFIX.length) || null;\n}\n\nfunction encodeOpaque(value: unknown): string {\n return Buffer.from(JSON.stringify(value)).toString('base64url');\n}\n\nfunction decodeOpaque<T>(value: string, prefix: string, guard: (value: unknown) => value is T): T | null {\n if (!value.startsWith(prefix)) return null;\n try {\n const decoded = JSON.parse(Buffer.from(value.slice(prefix.length), 'base64url').toString('utf8')) as unknown;\n return guard(decoded) ? decoded : null;\n } catch {\n return null;\n }\n}\n\nexport function normalizeGitLabHost(host: string): string {\n return host.trim().toLowerCase().replace(/\\.$/, '');\n}\n\nfunction isSourceReference(value: unknown): value is GitLabSourceReference {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const ref = value as Record<string, unknown>;\n if (typeof ref.projectId !== 'string' || ref.projectId.length === 0) return false;\n const canonical = ref.version === 2 && typeof ref.host === 'string' && normalizeGitLabHost(ref.host).length > 0;\n const legacy =\n typeof ref.connectionId === 'string' &&\n ref.connectionId.length > 0 &&\n typeof ref.projectPath === 'string' &&\n ref.projectPath.length > 0;\n return canonical || legacy;\n}\n\nfunction isIssueReference(value: unknown): value is GitLabIssueReference {\n return (\n isSourceReference(value) &&\n Number.isSafeInteger((value as GitLabIssueReference).issueIid) &&\n (value as GitLabIssueReference).issueIid > 0\n );\n}\n\nfunction isMergeRequestReference(value: unknown): value is GitLabMergeRequestReference {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const ref = value as Record<string, unknown>;\n return (\n ref.version === 1 &&\n typeof ref.host === 'string' &&\n normalizeGitLabHost(ref.host).length > 0 &&\n Number.isSafeInteger(ref.projectId) &&\n (ref.projectId as number) > 0 &&\n Number.isSafeInteger(ref.mergeRequestIid) &&\n (ref.mergeRequestIid as number) > 0\n );\n}\n\nfunction encodePageCursor(cursor: GitLabPageCursor): string {\n return encodeOpaque(cursor);\n}\n\nfunction decodePageCursor(value: string | undefined): GitLabPageCursor | null {\n if (!value) return { source: 0, page: 1 };\n try {\n const decoded = JSON.parse(Buffer.from(value, 'base64url').toString('utf8')) as Record<string, unknown>;\n if (!Number.isSafeInteger(decoded.source) || Number(decoded.source) < 0) return null;\n if (!Number.isSafeInteger(decoded.page) || Number(decoded.page) < 1) return null;\n return { source: Number(decoded.source), page: Number(decoded.page) };\n } catch {\n return null;\n }\n}\n\nfunction displayName(user: { name?: string | null; username: string } | null | undefined): string | null {\n return user?.name?.trim() || user?.username || null;\n}\n\nfunction parsePositiveInteger(value: string): number | null {\n if (!/^\\d+$/.test(value)) return null;\n const parsed = Number(value);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;\n}\n\nfunction parseIssueLocator(value: string): {\n host?: string;\n projectPath: string;\n issueIid: number;\n} | null {\n const shorthand = value.match(/^(.+?)#(\\d+)$/);\n if (shorthand) {\n const issueIid = parsePositiveInteger(shorthand[2]!);\n const projectPath = shorthand[1]!.replace(/^\\/+|\\/+$/g, '');\n return issueIid && projectPath ? { projectPath, issueIid } : null;\n }\n try {\n const url = new URL(value);\n const match = url.pathname.match(/^\\/(.+)\\/-\\/issues\\/(\\d+)\\/?$/);\n if (!match) return null;\n const issueIid = parsePositiveInteger(match[2]!);\n if (!issueIid) return null;\n const projectPath = decodeURIComponent(match[1]!);\n return { host: url.host, projectPath, issueIid };\n } catch {\n return null;\n }\n}\n\nfunction targetStateEvent(input: UpdateIntakeIssueInput): 'close' | 'reopen' | null {\n if (input.state.kind === 'byType') {\n // GitLab issues have only opened/closed states, so canceled is intentionally mapped to close.\n return input.state.stateType === 'completed' || input.state.stateType === 'canceled' ? 'close' : 'reopen';\n }\n const name = input.state.name.trim().toLowerCase();\n if (name === 'closed' || name === 'close') return 'close';\n if (name === 'opened' || name === 'open' || name === 'reopen') return 'reopen';\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"integration.js","names":[],"sources":["../../../src/integrations/gitlab/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\n\nimport type { IntegrationConnection } from '../../capabilities/connection.js';\nimport type {\n CreateIntakeCommentInput,\n CreatedIntakeComment,\n GetIntakeIssueInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n IntakeItemPage,\n IntakeSource,\n ListIntakeIssuesInput,\n ListIntakeItemsInput,\n ResolveIntakeDispatchInput,\n ResolvedIntakeDispatch,\n UpdateIntakeIssueInput,\n} from '../../capabilities/intake.js';\nimport type { RouteAuth } from '../../routes/route.js';\nimport type { IntegrationStorageHandle } from '../../storage/domains/integrations/base.js';\nimport type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';\nimport type { SourceControlStorageHandle } from '../../storage/domains/source-control/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../base.js';\nimport { IssueReconcileWorker } from '../issue-reconcile-worker.js';\nimport { buildGitLabAgentTools } from './agent-tools.js';\nimport {\n GITLAB_ISSUES_PAGE_SIZE,\n GITLAB_NOTES_PAGE_SIZE,\n GITLAB_PROJECTS_PAGE_SIZE,\n GitLabApiClient,\n GitLabApiError,\n} from './api.js';\nimport type { GitLabIssue, GitLabNote, GitLabProject } from './api.js';\nimport { resolveGitLabRules } from './default-rules.js';\nimport type { GitLabEventRules, GitLabRuleOverrides } from './default-rules.js';\nimport { attachGitLabReconciler } from './reconciler.js';\nimport { gitlabReconciliationEnabled, gitlabReconciliationInterval } from './reconciliation-config.js';\nimport { buildGitLabRoutes } from './routes.js';\nimport { attachGitLabRules } from './rules.js';\nimport {\n createGitLabSubscriptionTools,\n parseCreatedMergeRequest,\n subscribeCurrentSessionToMergeRequest,\n} from './session-subscriptions.js';\nimport type { GitLabSubscriptionStorage } from './subscriptions.js';\nimport { buildGitLabVersionControl } from './version-control.js';\n\ninterface GitLabConnectionContext {\n id: string;\n label: string | null;\n api: GitLabApiClient;\n connection: IntegrationConnection;\n host: string;\n webBaseUrl?: string;\n /** Resolves a fresh provider token for a single brokered git operation. */\n repositoryAccessToken?: () => Promise<string>;\n}\n\ninterface GitLabSourceReference {\n /** Present only on legacy v1 ids; authentication is not resource identity. */\n connectionId?: string;\n /** Canonical v2 ids use the GitLab instance host plus immutable project id. */\n host?: string;\n projectId: string;\n /** Legacy ids carry display metadata; v2 resolves it from GitLab. */\n projectPath?: string;\n}\n\ninterface ResolvedGitLabSourceReference extends GitLabSourceReference {\n connectionId: string;\n host: string;\n projectPath: string;\n}\n\ninterface GitLabIssueReference extends GitLabSourceReference {\n issueIid: number;\n}\n\nexport interface GitLabMergeRequestReference {\n version: 1;\n host: string;\n projectId: number;\n mergeRequestIid: number;\n}\n\ninterface GitLabPageCursor {\n source: number;\n page: number;\n}\n\nexport interface GitLabStatusConnection {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n}\n\nconst DIRECT_CONNECTION_ID = 'direct';\nconst GITLAB_CONNECTION_TOKEN_PREFIX = 'gitlab-connection:';\nconst GITLAB_SOURCE_PREFIX = 'gitlab-project:';\nconst GITLAB_ISSUE_PREFIX = 'gitlab-issue:';\nconst GITLAB_MERGE_REQUEST_PREFIX = 'gitlab-pr:';\nexport const GITLAB_TRUSTED_ACCESS_LEVEL = 30;\n// Bound issue detail reads to 2,000 notes. GitLab does not expose truncation through the Intake contract.\nconst MAX_NOTES_PAGES = 20;\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport abstract class GitLabIntegrationBase implements FactoryIntegration {\n readonly id = 'gitlab';\n #projects: FactoryProjectsStorage | undefined;\n #sourceControl: SourceControlStorageHandle | undefined;\n #integrationStorage: GitLabSubscriptionStorage | undefined;\n #auth: RouteAuth | undefined;\n readonly #orgIdByResourceId = new Map<string, string | null>();\n readonly rules: GitLabEventRules;\n\n constructor(rules?: GitLabRuleOverrides) {\n this.rules = resolveGitLabRules(rules);\n }\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: () => this.#listSources(),\n listItems: input => this.#listItems(input),\n listIssues: input => this.#listIssues(input),\n getIssue: input => this.#getIssue(input),\n createComment: input => this.#createComment(input),\n updateIssue: input => this.#updateIssue(input),\n };\n readonly versionControl = buildGitLabVersionControl({\n contextForConnection: connection => this.#versionControlContext(connection),\n contextForStoredInstallation: (connection, host) => this.#versionControlContextForInstallation(connection, host),\n });\n initialize({\n storage,\n projects,\n auth,\n sourceControl,\n }: {\n storage?: IntegrationStorageHandle;\n projects: FactoryProjectsStorage;\n auth: RouteAuth;\n sourceControl?: SourceControlStorageHandle;\n }): void {\n this.#projects = projects;\n this.#auth = auth;\n this.#sourceControl = sourceControl;\n this.#integrationStorage = storage as GitLabSubscriptionStorage | undefined;\n }\n\n /** Generic integration persistence (merge-request subscriptions). */\n get integrationStorage(): GitLabSubscriptionStorage {\n if (!this.#integrationStorage) {\n throw new Error(`${this.constructor.name} is not initialized — the factory binds storage during prepare().`);\n }\n return this.#integrationStorage;\n }\n\n /** Source-control rows for this integration; absent when the host runs without them. */\n get sourceControlStorage(): SourceControlStorageHandle | undefined {\n return this.#sourceControl;\n }\n\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n get projects(): FactoryProjectsStorage {\n if (!this.#projects) {\n throw new Error(`${this.constructor.name} is not initialized — the factory binds storage during prepare().`);\n }\n return this.#projects;\n }\n\n async resolveOrgId(resourceId: string): Promise<string | null> {\n const cached = this.#orgIdByResourceId.get(resourceId);\n if (cached !== undefined) return cached;\n if (!UUID_PATTERN.test(resourceId)) {\n this.#orgIdByResourceId.set(resourceId, null);\n return null;\n }\n try {\n await this.projects.ensureReady();\n const project = await this.projects.getById({ id: resourceId });\n const orgId = project?.orgId ?? null;\n this.#orgIdByResourceId.set(resourceId, orgId);\n return orgId;\n } catch {\n return null;\n }\n }\n\n clearCaches(): void {\n this.#orgIdByResourceId.clear();\n }\n\n abstract hasActiveConnections(): Promise<boolean>;\n abstract authFailureMessage(): string;\n protected abstract activeContexts(): Promise<GitLabConnectionContext[]>;\n protected abstract contextById(connectionId: string): Promise<GitLabConnectionContext>;\n\n /** Resolve only a GitLab repository linked to this Factory in this organization. */\n async getLinkedRepository(input: { orgId: string; factoryProjectId: string; projectRepositoryId: string }) {\n if (!this.#sourceControl) return null;\n const connections = await this.#sourceControl.connections.list({\n orgId: input.orgId,\n factoryProjectId: input.factoryProjectId,\n });\n for (const connection of connections) {\n if (connection.integrationId !== 'gitlab') continue;\n const links = await this.#sourceControl.projectRepositories.list({\n orgId: input.orgId,\n connectionId: connection.id,\n });\n const link = links.find(candidate => candidate.id === input.projectRepositoryId);\n if (!link) continue;\n const repository = await this.#sourceControl.repositories.get({ orgId: input.orgId, id: link.repositoryId });\n const installation = await this.#sourceControl.installations.get({\n orgId: input.orgId,\n id: connection.installationId,\n });\n if (!repository || !installation) return null;\n const host = installation.providerMetadata.host;\n if (typeof host !== 'string' || !host) return null;\n return { repository, host: normalizeGitLabHost(host) };\n }\n return null;\n }\n\n async getProjectMemberAccessLevel(\n connectionId: string,\n projectId: string,\n username: string,\n ): Promise<number | undefined> {\n const normalized = username.trim().toLowerCase();\n if (!normalized) return undefined;\n const members = await (await this.contextById(connectionId)).api.listProjectMembers(projectId, { query: username });\n const member = members.find(\n candidate => candidate.username.toLowerCase() === normalized && candidate.state !== 'blocked',\n );\n return member?.access_level;\n }\n\n async getWorkItemAuthorUsername(\n connectionId: string,\n projectId: string,\n kind: 'issue' | 'merge_request',\n iid: number,\n ): Promise<string | undefined> {\n const api = (await this.contextById(connectionId)).api;\n const item = kind === 'issue' ? await api.getIssue(projectId, iid) : await api.getMergeRequest(projectId, iid);\n return item.author?.username;\n }\n\n async isProjectMemberTrustedForSource(sourceId: string, username: string): Promise<boolean> {\n const reference = decodeSourceId(sourceId);\n if (!reference) throw new GitLabApiError('GitLab source identity is invalid.', 400);\n const { context, source } = await this.#resolveSourceReference(reference);\n const accessLevel = await this.getProjectMemberAccessLevel(context.id, source.projectId, username);\n return (accessLevel ?? 0) >= GITLAB_TRUSTED_ACCESS_LEVEL;\n }\n\n async getIssueForFactoryProject(input: {\n orgId: string;\n factoryProjectId: string;\n issueId: string;\n }): Promise<IntakeIssueDetail | null> {\n if (!this.#sourceControl) throw new GitLabApiError('GitLab source control is unavailable.', 503);\n const reference = decodeIssueReference(input.issueId);\n const locator = parseIssueLocator(input.issueId);\n if (!reference && !locator) {\n throw new GitLabApiError('GitLab issue must include a project and issue IID.', 400);\n }\n\n const normalizePath = (value: string) => value.replace(/^\\/+|\\/+$/g, '').toLowerCase();\n const matches: Array<{\n connectionId: string;\n projectId: string;\n projectPath: string;\n }> = [];\n const connections = await this.#sourceControl.connections.list({\n orgId: input.orgId,\n factoryProjectId: input.factoryProjectId,\n });\n for (const connection of connections) {\n const installation = await this.#sourceControl.installations.get({\n orgId: input.orgId,\n id: connection.installationId,\n });\n if (!installation) continue;\n for (const projectRepository of await this.#sourceControl.projectRepositories.list({\n orgId: input.orgId,\n connectionId: connection.id,\n })) {\n const repository = await this.#sourceControl.repositories.get({\n orgId: input.orgId,\n id: projectRepository.repositoryId,\n });\n if (!repository) continue;\n if (reference?.projectId && repository.externalId !== reference.projectId) continue;\n if (reference?.projectPath && normalizePath(repository.slug) !== normalizePath(reference.projectPath)) continue;\n if (locator?.projectPath && normalizePath(repository.slug) !== normalizePath(locator.projectPath)) continue;\n if (reference?.connectionId && installation.externalId !== reference.connectionId) continue;\n const installationHost =\n typeof installation.providerMetadata.host === 'string'\n ? normalizeGitLabHost(installation.providerMetadata.host)\n : null;\n if (reference?.host && installationHost !== normalizeGitLabHost(reference.host)) continue;\n if (locator?.host && installationHost !== normalizeGitLabHost(locator.host)) continue;\n matches.push({\n connectionId: installation.externalId,\n projectId: repository.externalId,\n projectPath: repository.slug,\n });\n }\n }\n const match = matches.sort((left, right) => left.connectionId.localeCompare(right.connectionId))[0];\n if (!match) throw new GitLabApiError('GitLab issue is outside the active Factory project.', 404);\n return this.intake.getIssue({\n connection: gitlabConnection(match.connectionId),\n sourceId: encodeSourceId(match),\n issueId: input.issueId,\n });\n }\n\n /** Platform mode exposes org connections; direct mode returns undefined. */\n async statusConnections(): Promise<GitLabStatusConnection[] | undefined> {\n return undefined;\n }\n\n /** Direct mode verifies its configured credential; Platform mode uses connection status. */\n async verifyStatus(): Promise<void> {\n // Platform connection state is authoritative unless a provider overrides this probe.\n }\n\n protected get webhookSecret(): string | undefined {\n return undefined;\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n if (!gitlabReconciliationEnabled()) return [];\n const reconcile = attachGitLabReconciler(this, ctx);\n if (!reconcile) return [];\n const intervalMs = gitlabReconciliationInterval();\n return [\n new IssueReconcileWorker({\n integrationId: this.id,\n reconcile,\n ...(intervalMs ? { intervalMs } : {}),\n }),\n ];\n }\n routes(ctx: IntegrationContext): ApiRoute[] {\n const ingestFactoryEvent = attachGitLabRules(this, ctx);\n return buildGitLabRoutes({\n gitlab: this,\n auth: ctx.auth,\n intake: ctx.storage?.intake,\n emitAudit: ctx.hooks?.emitAudit,\n sandbox: ctx.sandbox,\n webhookSecret: this.webhookSecret,\n ingestFactoryEvent,\n ...(ctx.controller ? { controller: ctx.controller } : {}),\n });\n }\n\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildGitLabAgentTools({ requestContext: args.requestContext, gitlab: this });\n }\n\n sessionTools({ requestContext }: { requestContext: RequestContext }): IntegrationTools {\n return createGitLabSubscriptionTools(requestContext, this);\n }\n\n async postToolObserver({\n toolContext,\n requestContext,\n }: Parameters<NonNullable<FactoryIntegration['postToolObserver']>>[0]): Promise<void> {\n const mergeRequestUrl = parseCreatedMergeRequest(toolContext);\n if (!mergeRequestUrl || !requestContext) return;\n await subscribeCurrentSessionToMergeRequest(requestContext, mergeRequestUrl, 'auto-create-change-request', this);\n }\n\n abstract diagnostics(): Record<string, unknown>;\n\n /** Prefer the configured direct token over an older Platform installation on the same host. */\n async resolveActiveConnectionForHost(storedConnectionId: string, host: string): Promise<string> {\n if (this.diagnostics().mode !== 'direct') return storedConnectionId;\n const [direct] = await this.activeContexts();\n return direct && normalizeGitLabHost(direct.host) === normalizeGitLabHost(host) ? direct.id : storedConnectionId;\n }\n\n async #versionControlContext(connection: IntegrationConnection): Promise<GitLabConnectionContext> {\n const connectionId = connectionIdFromConnection(connection);\n if (connectionId) return this.contextById(connectionId);\n const contexts = await this.activeContexts();\n if (contexts.length !== 1) {\n throw new GitLabApiError(\n 'GitLab version-control requests must identify a connection when multiple GitLab accounts are connected.',\n 400,\n );\n }\n return contexts[0]!;\n }\n async #versionControlContextForInstallation(\n connection: IntegrationConnection,\n host: string | undefined,\n ): Promise<GitLabConnectionContext> {\n // A direct PAT intentionally overrides Platform credentials. Old links can\n // retain their Platform connection id; use the PAT only for the same host.\n if (this.diagnostics().mode === 'direct' && host) {\n const [direct] = await this.activeContexts();\n if (direct && normalizeGitLabHost(host) === normalizeGitLabHost(direct.host)) return direct;\n }\n return this.#versionControlContext(connection);\n }\n async #contextForReference(reference: GitLabSourceReference): Promise<GitLabConnectionContext> {\n if (reference.connectionId) return this.contextById(reference.connectionId);\n const contexts = (await this.activeContexts()).filter(\n context => !reference.host || normalizeGitLabHost(context.host) === normalizeGitLabHost(reference.host),\n );\n if (contexts.length === 0) {\n throw new GitLabApiError('No active GitLab connection can access this source.', 401);\n }\n if (contexts.length === 1) return contexts[0]!;\n\n if (this.#sourceControl) {\n const linkedConnectionIds = new Set(\n (await this.#sourceControl.projectRepositories.listConfiguredExternalKeys())\n .filter(key => key.repositoryExternalId === reference.projectId)\n .map(key => key.installationExternalId),\n );\n const linked = contexts.filter(context => linkedConnectionIds.has(context.id));\n if (linked.length > 0) return linked.sort((left, right) => left.id.localeCompare(right.id))[0]!;\n }\n\n for (const context of contexts.sort((left, right) => left.id.localeCompare(right.id))) {\n try {\n await context.api.getProject(reference.projectId);\n return context;\n } catch (error) {\n if (!(error instanceof GitLabApiError) || ![401, 403, 404].includes(error.status ?? 0)) throw error;\n // This credential cannot access the project; try another active account for the same canonical source.\n }\n }\n throw new GitLabApiError('No active GitLab connection can access this source.', 404);\n }\n\n async #resolveSourceReference(\n reference: GitLabSourceReference,\n ): Promise<{ context: GitLabConnectionContext; source: ResolvedGitLabSourceReference }> {\n const context = await this.#contextForReference(reference);\n const projectPath =\n reference.projectPath ?? (await context.api.getProject(reference.projectId)).path_with_namespace;\n return {\n context,\n source: {\n connectionId: context.id,\n host: normalizeGitLabHost(reference.host ?? context.host),\n projectId: reference.projectId,\n projectPath,\n },\n };\n }\n\n async #resolveIntakeDispatch({ externalSource }: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> {\n if (externalSource.type !== 'issue') return null;\n const reference = decodeIssueReference(externalSource.externalId);\n if (!reference) return null;\n const { context, source } = await this.#resolveSourceReference(reference);\n return {\n connection: context.connection,\n sourceId: encodeSourceId(source),\n issueId: String(reference.issueIid),\n };\n }\n\n async #listSources(): Promise<IntakeSource[]> {\n const sources = new Map<string, IntakeSource>();\n for (const context of await this.activeContexts()) {\n for (let page = 1; ; page++) {\n const projects = await context.api.listProjects({ page });\n for (const project of projects) {\n const source = this.#toIntakeSource(context, project);\n if (!sources.has(source.id)) sources.set(source.id, source);\n }\n if (projects.length < GITLAB_PROJECTS_PAGE_SIZE) break;\n }\n }\n return [...sources.values()];\n }\n\n async #listItems(input: ListIntakeItemsInput): Promise<IntakeItemPage> {\n const result = await this.#listIssuePage(input.sourceIds, input.cursor);\n return {\n items: result.issues.map(({ issue, source, context }) => ({\n source: {\n type: 'issue',\n externalId: encodeIssueReference({ ...source, issueIid: issue.iid }),\n url: issue.web_url,\n },\n sourceId: encodeSourceId(source),\n title: `${source.projectPath}#${issue.iid}: ${issue.title}`,\n status: issue.state,\n labels: issue.labels ?? [],\n assignee: displayName(issue.assignee),\n createdAt: issue.created_at,\n updatedAt: issue.updated_at,\n metadata: {\n identifier: `${source.projectPath}#${issue.iid}`,\n projectId: source.projectId,\n projectPath: source.projectPath,\n connectionId: context.id,\n accountLabel: context.label,\n labelColors: Object.fromEntries((issue.labelDetails ?? []).map(label => [label.name, label.color])),\n },\n })),\n nextCursor: result.nextCursor,\n };\n }\n\n async #listIssues(input: ListIntakeIssuesInput) {\n const result = await this.#listIssuePage(input.sourceIds, input.cursor, input.labels);\n return {\n issues: result.issues.map(({ issue, source }) => ({\n ...this.#toIntakeIssue(issue, source.projectPath),\n sourceId: encodeSourceId(source),\n })),\n nextCursor: result.nextCursor,\n };\n }\n\n async #listIssuePage(sourceIds: string[], cursor?: string, labels?: string[]) {\n const sources = sourceIds.map(decodeSourceId).filter((source): source is GitLabSourceReference => source !== null);\n if (sources.length === 0) return { issues: [], nextCursor: null };\n const current = decodePageCursor(cursor);\n if (!current || current.source >= sources.length) return { issues: [], nextCursor: null };\n\n for (let sourceIndex = current.source; sourceIndex < sources.length; sourceIndex++) {\n const { source, context } = await this.#resolveSourceReference(sources[sourceIndex]!);\n const page = sourceIndex === current.source ? current.page : 1;\n const issues = await context.api.listIssues(source.projectId, { page, labels });\n const nextCursor =\n issues.length === GITLAB_ISSUES_PAGE_SIZE\n ? encodePageCursor({ source: sourceIndex, page: page + 1 })\n : sourceIndex + 1 < sources.length\n ? encodePageCursor({ source: sourceIndex + 1, page: 1 })\n : null;\n return { issues: issues.map(issue => ({ issue, source, context })), nextCursor };\n }\n return { issues: [], nextCursor: null };\n }\n\n async #getIssue(input: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> {\n const resolved = await this.#resolveRequest(input);\n let issue: GitLabIssue;\n try {\n issue = await resolved.context.api.getIssue(resolved.projectId, resolved.issueIid);\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n const notes = await this.#listAllNotes(resolved.context.api, resolved.projectId, resolved.issueIid);\n return {\n ...this.#toIntakeIssue(issue, resolved.projectPath),\n description: issue.description?.trim() || null,\n comments: notes\n .filter(note => !note.system)\n .map(note => ({ author: displayName(note.author), body: note.body, createdAt: note.created_at })),\n };\n }\n\n async #listAllNotes(api: GitLabApiClient, projectId: string, issueIid: number): Promise<GitLabNote[]> {\n const notes: GitLabNote[] = [];\n for (let page = 1; page <= MAX_NOTES_PAGES; page++) {\n const result = await api.listNotes(projectId, issueIid, { page });\n notes.push(...result);\n if (result.length < GITLAB_NOTES_PAGE_SIZE) break;\n }\n return notes;\n }\n\n async #createComment(input: CreateIntakeCommentInput): Promise<CreatedIntakeComment | null> {\n const resolved = await this.#resolveRequest(input);\n try {\n const issue = await resolved.context.api.getIssue(resolved.projectId, resolved.issueIid);\n const note = await resolved.context.api.createNote(resolved.projectId, resolved.issueIid, input.body);\n return { id: String(note.id), url: `${issue.web_url}#note_${note.id}` };\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n }\n\n async #updateIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n const resolved = await this.#resolveRequest(input);\n let issue: GitLabIssue;\n try {\n issue = await resolved.context.api.getIssue(resolved.projectId, resolved.issueIid);\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n const stateEvent = targetStateEvent(input);\n if (!stateEvent) return null;\n if ((stateEvent === 'close' && issue.state === 'closed') || (stateEvent === 'reopen' && issue.state === 'opened')) {\n return this.#toIntakeIssue(issue, resolved.projectPath);\n }\n const updated = await resolved.context.api.updateIssueState(resolved.projectId, resolved.issueIid, stateEvent);\n return this.#toIntakeIssue(updated, resolved.projectPath);\n }\n\n async #resolveRequest(input: GetIntakeIssueInput): Promise<{\n context: GitLabConnectionContext;\n projectId: string;\n projectPath: string;\n issueIid: number;\n }> {\n const issueReference = decodeIssueReference(input.issueId);\n const source = input.sourceId ? decodeSourceId(input.sourceId) : null;\n const locator = parseIssueLocator(input.issueId);\n const reference = issueReference ?? source;\n let context: GitLabConnectionContext;\n if (reference) {\n context = await this.#contextForReference(reference);\n } else {\n const connectionId = connectionIdFromConnection(input.connection);\n const contexts = connectionId ? [await this.contextById(connectionId)] : await this.activeContexts();\n if (contexts.length !== 1) {\n throw new GitLabApiError('GitLab issue reference must resolve to exactly one active connection.', 400);\n }\n context = contexts[0]!;\n }\n const projectId = issueReference?.projectId ?? source?.projectId ?? locator?.projectPath;\n const issueIid = issueReference?.issueIid ?? locator?.issueIid ?? parsePositiveInteger(input.issueId);\n if (!projectId || !issueIid) {\n throw new GitLabApiError(\n 'GitLab issue must include a project and issue IID (for example, group/project#42).',\n 400,\n );\n }\n const projectPath =\n issueReference?.projectPath ??\n source?.projectPath ??\n locator?.projectPath ??\n (await context.api.getProject(projectId)).path_with_namespace;\n return { context, projectId, projectPath, issueIid };\n }\n\n #toIntakeSource(context: GitLabConnectionContext, project: GitLabProject): IntakeSource {\n const reference: GitLabSourceReference = {\n host: normalizeGitLabHost(context.host),\n projectId: String(project.id),\n projectPath: project.path_with_namespace,\n };\n return {\n id: encodeSourceId(reference),\n name: project.path_with_namespace,\n type: 'project',\n metadata: {\n projectId: String(project.id),\n projectPath: project.path_with_namespace,\n defaultBranch: project.default_branch ?? null,\n connectionId: context.id,\n accountLabel: context.label,\n url: project.web_url,\n },\n };\n }\n\n #toIntakeIssue(issue: GitLabIssue, projectPath: string): IntakeIssue {\n const assignees = issue.assignees?.map(displayName).filter((name): name is string => name !== null) ?? [];\n return {\n id: String(issue.iid),\n identifier: `${projectPath}#${issue.iid}`,\n title: issue.title,\n url: issue.web_url,\n author: displayName(issue.author),\n authorUsername: issue.author?.username ?? null,\n state: issue.state,\n stateType: issue.state === 'closed' ? 'completed' : 'unstarted',\n priority: typeof issue.weight === 'number' ? String(issue.weight) : null,\n assignee: displayName(issue.assignee) ?? assignees[0] ?? null,\n assignees,\n source: projectPath,\n labels: issue.labels ?? [],\n labelColors: Object.fromEntries((issue.labelDetails ?? []).map(label => [label.name, label.color])),\n commentCount: issue.user_notes_count ?? null,\n createdAt: issue.created_at,\n updatedAt: issue.updated_at,\n };\n }\n}\n\nexport type GitLabAccessTokenType = 'personal' | 'group';\n\nexport interface GitLabIntegrationConfig {\n rules?: GitLabRuleOverrides;\n accessToken?: string;\n /** Defaults to `personal`; both token types authenticate identically but have different resource reach. */\n accessTokenType?: GitLabAccessTokenType;\n baseUrl?: string;\n webhookSecret?: string;\n fetchImpl?: typeof fetch;\n}\n\nconst DIRECT_CONNECTION_TOKEN = 'gitlab-direct-access-token';\n\nexport class GitLabIntegration extends GitLabIntegrationBase {\n readonly #accessToken: string;\n readonly #accessTokenType: GitLabAccessTokenType;\n readonly #baseUrl: string;\n readonly #webhookSecret: string | undefined;\n readonly #context: GitLabConnectionContext;\n\n constructor(config: GitLabIntegrationConfig = {}) {\n super(config.rules);\n const accessToken = config.accessToken?.trim() || process.env.GITLAB_ACCESS_TOKEN?.trim();\n if (!accessToken) {\n throw new Error('GitLabIntegration: missing required GITLAB_ACCESS_TOKEN.');\n }\n this.#accessToken = accessToken;\n this.#accessTokenType = parseAccessTokenType(\n config.accessTokenType ?? process.env.GITLAB_ACCESS_TOKEN_TYPE?.trim(),\n );\n this.#baseUrl = (config.baseUrl ?? process.env.GITLAB_BASE_URL?.trim() ?? 'https://gitlab.com').replace(/\\/+$/, '');\n this.#webhookSecret = config.webhookSecret?.trim() || process.env.GITLAB_WEBHOOK_SECRET?.trim() || undefined;\n const api = new GitLabApiClient({\n baseUrl: this.#baseUrl,\n accessToken: this.#accessToken,\n ...(config.fetchImpl ? { fetchImpl: config.fetchImpl } : {}),\n });\n this.#context = {\n id: DIRECT_CONNECTION_ID,\n label: new URL(this.#baseUrl).host,\n api,\n connection: { type: 'oauth', accessToken: DIRECT_CONNECTION_TOKEN },\n host: new URL(this.#baseUrl).host,\n webBaseUrl: this.#baseUrl,\n repositoryAccessToken: async () => this.#accessToken,\n };\n }\n\n protected override get webhookSecret(): string | undefined {\n return this.#webhookSecret;\n }\n\n async hasActiveConnections(): Promise<boolean> {\n return true;\n }\n\n override async verifyStatus(): Promise<void> {\n await this.#context.api.getCurrentUser();\n }\n\n authFailureMessage(): string {\n return `GitLab rejected the configured ${this.#accessTokenType} access token. Check the token and its scopes.`;\n }\n\n protected async activeContexts(): Promise<GitLabConnectionContext[]> {\n return [this.#context];\n }\n\n protected async contextById(connectionId: string): Promise<GitLabConnectionContext> {\n if (connectionId !== DIRECT_CONNECTION_ID) {\n throw new GitLabApiError('GitLab connection is unavailable.', 401);\n }\n return this.#context;\n }\n\n diagnostics(): Record<string, unknown> {\n return {\n configured: true,\n mode: 'direct',\n accessTokenType: this.#accessTokenType,\n endpointHost: new URL(this.#baseUrl).host,\n webhookConfigured: Boolean(this.#webhookSecret),\n };\n }\n}\n\nfunction parseAccessTokenType(value: string | undefined): GitLabAccessTokenType {\n const normalized = value?.trim().toLowerCase() || 'personal';\n if (normalized === 'personal' || normalized === 'group') return normalized;\n throw new Error(\"GitLabIntegration: GITLAB_ACCESS_TOKEN_TYPE must be 'personal' or 'group'.\");\n}\n\nexport function encodeSourceId(reference: GitLabSourceReference): string {\n if (reference.host) {\n return `${GITLAB_SOURCE_PREFIX}${encodeOpaque({\n version: 2,\n host: normalizeGitLabHost(reference.host),\n projectId: reference.projectId,\n })}`;\n }\n if (!reference.connectionId || !reference.projectPath) {\n throw new Error('GitLab source reference is missing its instance host.');\n }\n return `${GITLAB_SOURCE_PREFIX}${encodeOpaque(reference)}`;\n}\n\nexport function decodeSourceId(value: string): GitLabSourceReference | null {\n return decodeOpaque(value, GITLAB_SOURCE_PREFIX, isSourceReference);\n}\n\nexport function encodeIssueReference(reference: GitLabIssueReference): string {\n if (reference.host) {\n return `${GITLAB_ISSUE_PREFIX}${encodeOpaque({\n version: 2,\n host: normalizeGitLabHost(reference.host),\n projectId: reference.projectId,\n issueIid: reference.issueIid,\n })}`;\n }\n if (!reference.connectionId || !reference.projectPath) {\n throw new Error('GitLab issue reference is missing its instance host.');\n }\n return `${GITLAB_ISSUE_PREFIX}${encodeOpaque(reference)}`;\n}\n\nexport function decodeIssueReference(value: string): GitLabIssueReference | null {\n return decodeOpaque(value, GITLAB_ISSUE_PREFIX, isIssueReference);\n}\n\nexport function decodeMergeRequestReference(value: string): GitLabMergeRequestReference | null {\n return decodeOpaque(value, GITLAB_MERGE_REQUEST_PREFIX, isMergeRequestReference);\n}\n\nexport function gitlabConnection(connectionId: string): IntegrationConnection {\n return { type: 'oauth', accessToken: `${GITLAB_CONNECTION_TOKEN_PREFIX}${connectionId}` };\n}\n\nfunction connectionIdFromConnection(connection: IntegrationConnection): string | null {\n if (connection.type !== 'oauth' || !connection.accessToken.startsWith(GITLAB_CONNECTION_TOKEN_PREFIX)) return null;\n return connection.accessToken.slice(GITLAB_CONNECTION_TOKEN_PREFIX.length) || null;\n}\n\nfunction encodeOpaque(value: unknown): string {\n return Buffer.from(JSON.stringify(value)).toString('base64url');\n}\n\nfunction decodeOpaque<T>(value: string, prefix: string, guard: (value: unknown) => value is T): T | null {\n if (!value.startsWith(prefix)) return null;\n try {\n const decoded = JSON.parse(Buffer.from(value.slice(prefix.length), 'base64url').toString('utf8')) as unknown;\n return guard(decoded) ? decoded : null;\n } catch {\n return null;\n }\n}\n\nexport function normalizeGitLabHost(host: string): string {\n return host.trim().toLowerCase().replace(/\\.$/, '');\n}\n\nfunction isSourceReference(value: unknown): value is GitLabSourceReference {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const ref = value as Record<string, unknown>;\n if (typeof ref.projectId !== 'string' || ref.projectId.length === 0) return false;\n const canonical = ref.version === 2 && typeof ref.host === 'string' && normalizeGitLabHost(ref.host).length > 0;\n const legacy =\n typeof ref.connectionId === 'string' &&\n ref.connectionId.length > 0 &&\n typeof ref.projectPath === 'string' &&\n ref.projectPath.length > 0;\n return canonical || legacy;\n}\n\nfunction isIssueReference(value: unknown): value is GitLabIssueReference {\n return (\n isSourceReference(value) &&\n Number.isSafeInteger((value as GitLabIssueReference).issueIid) &&\n (value as GitLabIssueReference).issueIid > 0\n );\n}\n\nfunction isMergeRequestReference(value: unknown): value is GitLabMergeRequestReference {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const ref = value as Record<string, unknown>;\n return (\n ref.version === 1 &&\n typeof ref.host === 'string' &&\n normalizeGitLabHost(ref.host).length > 0 &&\n Number.isSafeInteger(ref.projectId) &&\n (ref.projectId as number) > 0 &&\n Number.isSafeInteger(ref.mergeRequestIid) &&\n (ref.mergeRequestIid as number) > 0\n );\n}\n\nfunction encodePageCursor(cursor: GitLabPageCursor): string {\n return encodeOpaque(cursor);\n}\n\nfunction decodePageCursor(value: string | undefined): GitLabPageCursor | null {\n if (!value) return { source: 0, page: 1 };\n try {\n const decoded = JSON.parse(Buffer.from(value, 'base64url').toString('utf8')) as Record<string, unknown>;\n if (!Number.isSafeInteger(decoded.source) || Number(decoded.source) < 0) return null;\n if (!Number.isSafeInteger(decoded.page) || Number(decoded.page) < 1) return null;\n return { source: Number(decoded.source), page: Number(decoded.page) };\n } catch {\n return null;\n }\n}\n\nfunction displayName(user: { name?: string | null; username: string } | null | undefined): string | null {\n return user?.name?.trim() || user?.username || null;\n}\n\nfunction parsePositiveInteger(value: string): number | null {\n if (!/^\\d+$/.test(value)) return null;\n const parsed = Number(value);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;\n}\n\nfunction parseIssueLocator(value: string): {\n host?: string;\n projectPath: string;\n issueIid: number;\n} | null {\n const shorthand = value.match(/^(.+?)#(\\d+)$/);\n if (shorthand) {\n const issueIid = parsePositiveInteger(shorthand[2]!);\n const projectPath = shorthand[1]!.replace(/^\\/+|\\/+$/g, '');\n return issueIid && projectPath ? { projectPath, issueIid } : null;\n }\n try {\n const url = new URL(value);\n const match = url.pathname.match(/^\\/(.+)\\/-\\/issues\\/(\\d+)\\/?$/);\n if (!match) return null;\n const issueIid = parsePositiveInteger(match[2]!);\n if (!issueIid) return null;\n const projectPath = decodeURIComponent(match[1]!);\n return { host: url.host, projectPath, issueIid };\n } catch {\n return null;\n }\n}\n\nfunction targetStateEvent(input: UpdateIntakeIssueInput): 'close' | 'reopen' | null {\n if (input.state.kind === 'byType') {\n // GitLab issues have only opened/closed states, so canceled is intentionally mapped to close.\n return input.state.stateType === 'completed' || input.state.stateType === 'canceled' ? 'close' : 'reopen';\n }\n const name = input.state.name.trim().toLowerCase();\n if (name === 'closed' || name === 'close') return 'close';\n if (name === 'opened' || name === 'open' || name === 'reopen') return 'reopen';\n return null;\n}\n"],"mappings":"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"}
@@ -6,7 +6,8 @@ function configuredValue(primary, legacy) {
6
6
  function gitlabReconciliationEnabled() {
7
7
  const primary = process.env.MASTRACODE_GITLAB_RECONCILE_ENABLED;
8
8
  const value = configuredValue(primary, process.env.MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED);
9
- return optionalBoolean(primary?.trim() ? "MASTRACODE_GITLAB_RECONCILE_ENABLED" : "MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED", value, "gitlab") ?? true;
9
+ const name = primary?.trim() ? "MASTRACODE_GITLAB_RECONCILE_ENABLED" : "MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED";
10
+ return optionalBoolean(name, value, "gitlab") ?? true;
10
11
  }
11
12
  function gitlabReconciliationInterval() {
12
13
  return optionalPositiveInteger(configuredValue(process.env.MASTRACODE_GITLAB_RECONCILE_INTERVAL_MS, process.env.MASTRACODE_GITLAB_ISSUE_RECONCILE_INTERVAL_MS));
@@ -1 +1 @@
1
- {"version":3,"file":"reconciliation-config.js","names":[],"sources":["../../../src/integrations/gitlab/reconciliation-config.ts"],"sourcesContent":["import { optionalBoolean, optionalPositiveInteger } from '../reconciliation-config.js';\n\nfunction configuredValue(primary: string | undefined, legacy: string | undefined): string | undefined {\n return primary?.trim() ? primary : legacy;\n}\n\nexport function gitlabReconciliationEnabled(): boolean {\n const primary = process.env.MASTRACODE_GITLAB_RECONCILE_ENABLED;\n const value = configuredValue(primary, process.env.MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED);\n const name = primary?.trim() ? 'MASTRACODE_GITLAB_RECONCILE_ENABLED' : 'MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED';\n return optionalBoolean(name, value, 'gitlab') ?? true;\n}\n\nexport function gitlabReconciliationInterval(): number | undefined {\n return optionalPositiveInteger(\n configuredValue(\n process.env.MASTRACODE_GITLAB_RECONCILE_INTERVAL_MS,\n process.env.MASTRACODE_GITLAB_ISSUE_RECONCILE_INTERVAL_MS,\n ),\n );\n}\n"],"mappings":";;AAEA,SAAS,gBAAgB,SAA6B,QAAgD;CACpG,OAAO,SAAS,KAAK,IAAI,UAAU;AACrC;AAEA,SAAgB,8BAAuC;CACrD,MAAM,UAAU,QAAQ,IAAI;CAC5B,MAAM,QAAQ,gBAAgB,SAAS,QAAQ,IAAI,yCAAyC;CAE5F,OAAO,gBADM,SAAS,KAAK,IAAI,wCAAwC,6CAC1C,OAAO,QAAQ,KAAK;AACnD;AAEA,SAAgB,+BAAmD;CACjE,OAAO,wBACL,gBACE,QAAQ,IAAI,yCACZ,QAAQ,IAAI,6CACd,CACF;AACF"}
1
+ {"version":3,"file":"reconciliation-config.js","names":[],"sources":["../../../src/integrations/gitlab/reconciliation-config.ts"],"sourcesContent":["import { optionalBoolean, optionalPositiveInteger } from '../reconciliation-config.js';\n\nfunction configuredValue(primary: string | undefined, legacy: string | undefined): string | undefined {\n return primary?.trim() ? primary : legacy;\n}\n\nexport function gitlabReconciliationEnabled(): boolean {\n const primary = process.env.MASTRACODE_GITLAB_RECONCILE_ENABLED;\n const value = configuredValue(primary, process.env.MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED);\n const name = primary?.trim() ? 'MASTRACODE_GITLAB_RECONCILE_ENABLED' : 'MASTRACODE_GITLAB_ISSUE_RECONCILE_ENABLED';\n return optionalBoolean(name, value, 'gitlab') ?? true;\n}\n\nexport function gitlabReconciliationInterval(): number | undefined {\n return optionalPositiveInteger(\n configuredValue(\n process.env.MASTRACODE_GITLAB_RECONCILE_INTERVAL_MS,\n process.env.MASTRACODE_GITLAB_ISSUE_RECONCILE_INTERVAL_MS,\n ),\n );\n}\n"],"mappings":";;AAEA,SAAS,gBAAgB,SAA6B,QAAgD;CACpG,OAAO,SAAS,KAAK,IAAI,UAAU;AACrC;AAEA,SAAgB,8BAAuC;CACrD,MAAM,UAAU,QAAQ,IAAI;CAC5B,MAAM,QAAQ,gBAAgB,SAAS,QAAQ,IAAI,yCAAyC;CAC5F,MAAM,OAAO,SAAS,KAAK,IAAI,wCAAwC;CACvE,OAAO,gBAAgB,MAAM,OAAO,QAAQ,KAAK;AACnD;AAEA,SAAgB,+BAAmD;CACjE,OAAO,wBACL,gBACE,QAAQ,IAAI,yCACZ,QAAQ,IAAI,6CACd,CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/integrations/gitlab/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAS1E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAG9D,OAAO,KAAK,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAQrF,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAC/B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,SAAS,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE,iFAAiF;IACjF,UAAU,CAAC,EAAE,2BAA2B,CAAC,YAAY,CAAC,CAAC;CACxD;AAkGD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,QAAQ,EAAE,CAyZ/E"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/integrations/gitlab/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAS1E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAG9D,OAAO,KAAK,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAQrF,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAC/B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,SAAS,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE,iFAAiF;IACjF,UAAU,CAAC,EAAE,2BAA2B,CAAC,YAAY,CAAC,CAAC;CACxD;AAkGD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,QAAQ,EAAE,CA8Z/E"}
@@ -429,16 +429,20 @@ function buildGitLabRoutes(options) {
429
429
  await auth.ensureUser(loose(c));
430
430
  const tenant = auth.tenant(loose(c));
431
431
  if (!tenant?.orgId) return c.json({ error: "unauthorized" }, 401);
432
+ const sessionId = c.req.query("sessionId");
432
433
  const resourceId = c.req.query("resourceId");
433
434
  const threadId = c.req.query("threadId");
434
- const sessionScope = c.req.query("scope");
435
- if (!resourceId || !threadId) return c.json({ error: "resourceId and threadId are required" }, 400);
436
- const subscriptions = await listMergeRequestSubscriptionsForThread({
435
+ const target = !threadId ? null : sessionId ? {
436
+ orgId: tenant.orgId,
437
+ sessionId,
438
+ threadId
439
+ } : resourceId ? {
437
440
  orgId: tenant.orgId,
438
441
  resourceId,
439
- threadId,
440
- sessionScope
441
- }, gitlab.integrationStorage);
442
+ threadId
443
+ } : null;
444
+ if (!target) return c.json({ error: "sessionId (or resourceId) and threadId are required" }, 400);
445
+ const subscriptions = await listMergeRequestSubscriptionsForThread(target, gitlab.integrationStorage);
442
446
  return c.json({ subscriptions: subscriptions.map((subscription) => ({
443
447
  id: subscription.id,
444
448
  repoFullName: subscription.data.projectPath,