@shipfox/api-integration-core 2.0.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 (275) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/.turbo/turbo-type$colon$emit.log +1 -0
  3. package/.turbo/turbo-type.log +1 -0
  4. package/CHANGELOG.md +264 -0
  5. package/LICENSE +21 -0
  6. package/dist/config.d.ts +9 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +29 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/core/agent-tool-selection.d.ts +15 -0
  11. package/dist/core/agent-tool-selection.d.ts.map +1 -0
  12. package/dist/core/agent-tool-selection.js +48 -0
  13. package/dist/core/agent-tool-selection.js.map +1 -0
  14. package/dist/core/entities/connection.d.ts +2 -0
  15. package/dist/core/entities/connection.d.ts.map +1 -0
  16. package/dist/core/entities/connection.js +3 -0
  17. package/dist/core/entities/connection.js.map +1 -0
  18. package/dist/core/entities/provider.d.ts +6 -0
  19. package/dist/core/entities/provider.d.ts.map +1 -0
  20. package/dist/core/entities/provider.js +3 -0
  21. package/dist/core/entities/provider.js.map +1 -0
  22. package/dist/core/errors.d.ts +29 -0
  23. package/dist/core/errors.d.ts.map +1 -0
  24. package/dist/core/errors.js +40 -0
  25. package/dist/core/errors.js.map +1 -0
  26. package/dist/core/providers/agent-tools.d.ts +2 -0
  27. package/dist/core/providers/agent-tools.d.ts.map +1 -0
  28. package/dist/core/providers/agent-tools.js +3 -0
  29. package/dist/core/providers/agent-tools.js.map +1 -0
  30. package/dist/core/providers/redact-checkout-spec.d.ts +9 -0
  31. package/dist/core/providers/redact-checkout-spec.d.ts.map +1 -0
  32. package/dist/core/providers/redact-checkout-spec.js +25 -0
  33. package/dist/core/providers/redact-checkout-spec.js.map +1 -0
  34. package/dist/core/providers/registry.d.ts +9 -0
  35. package/dist/core/providers/registry.d.ts.map +1 -0
  36. package/dist/core/providers/registry.js +52 -0
  37. package/dist/core/providers/registry.js.map +1 -0
  38. package/dist/core/providers/source-control.d.ts +2 -0
  39. package/dist/core/providers/source-control.d.ts.map +1 -0
  40. package/dist/core/providers/source-control.js +3 -0
  41. package/dist/core/providers/source-control.js.map +1 -0
  42. package/dist/core/source-control-service.d.ts +46 -0
  43. package/dist/core/source-control-service.d.ts.map +1 -0
  44. package/dist/core/source-control-service.js +84 -0
  45. package/dist/core/source-control-service.js.map +1 -0
  46. package/dist/db/connections.d.ts +67 -0
  47. package/dist/db/connections.d.ts.map +1 -0
  48. package/dist/db/connections.js +130 -0
  49. package/dist/db/connections.js.map +1 -0
  50. package/dist/db/db.d.ts +865 -0
  51. package/dist/db/db.d.ts.map +1 -0
  52. package/dist/db/db.js +22 -0
  53. package/dist/db/db.js.map +1 -0
  54. package/dist/db/migrations.d.ts +2 -0
  55. package/dist/db/migrations.d.ts.map +1 -0
  56. package/dist/db/migrations.js +5 -0
  57. package/dist/db/migrations.js.map +1 -0
  58. package/dist/db/schema/common.d.ts +2 -0
  59. package/dist/db/schema/common.d.ts.map +1 -0
  60. package/dist/db/schema/common.js +4 -0
  61. package/dist/db/schema/common.js.map +1 -0
  62. package/dist/db/schema/connections.d.ts +165 -0
  63. package/dist/db/schema/connections.d.ts.map +1 -0
  64. package/dist/db/schema/connections.js +37 -0
  65. package/dist/db/schema/connections.js.map +1 -0
  66. package/dist/db/schema/outbox.d.ts +195 -0
  67. package/dist/db/schema/outbox.d.ts.map +1 -0
  68. package/dist/db/schema/outbox.js +5 -0
  69. package/dist/db/schema/outbox.js.map +1 -0
  70. package/dist/db/schema/webhook-deliveries.d.ts +76 -0
  71. package/dist/db/schema/webhook-deliveries.d.ts.map +1 -0
  72. package/dist/db/schema/webhook-deliveries.js +22 -0
  73. package/dist/db/schema/webhook-deliveries.js.map +1 -0
  74. package/dist/db/webhook-deliveries.d.ts +55 -0
  75. package/dist/db/webhook-deliveries.d.ts.map +1 -0
  76. package/dist/db/webhook-deliveries.js +132 -0
  77. package/dist/db/webhook-deliveries.js.map +1 -0
  78. package/dist/index.d.ts +54 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +105 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/metrics/index.d.ts +2 -0
  83. package/dist/metrics/index.d.ts.map +1 -0
  84. package/dist/metrics/index.js +3 -0
  85. package/dist/metrics/index.js.map +1 -0
  86. package/dist/metrics/instance.d.ts +8 -0
  87. package/dist/metrics/instance.d.ts.map +1 -0
  88. package/dist/metrics/instance.js +17 -0
  89. package/dist/metrics/instance.js.map +1 -0
  90. package/dist/presentation/dto/integrations.d.ts +36 -0
  91. package/dist/presentation/dto/integrations.d.ts.map +1 -0
  92. package/dist/presentation/dto/integrations.js +26 -0
  93. package/dist/presentation/dto/integrations.js.map +1 -0
  94. package/dist/presentation/routes/agent-tools-gateway/audit.d.ts +24 -0
  95. package/dist/presentation/routes/agent-tools-gateway/audit.d.ts.map +1 -0
  96. package/dist/presentation/routes/agent-tools-gateway/audit.js +52 -0
  97. package/dist/presentation/routes/agent-tools-gateway/audit.js.map +1 -0
  98. package/dist/presentation/routes/agent-tools-gateway/dispatch.d.ts +7 -0
  99. package/dist/presentation/routes/agent-tools-gateway/dispatch.d.ts.map +1 -0
  100. package/dist/presentation/routes/agent-tools-gateway/dispatch.js +108 -0
  101. package/dist/presentation/routes/agent-tools-gateway/dispatch.js.map +1 -0
  102. package/dist/presentation/routes/agent-tools-gateway/index.d.ts +12 -0
  103. package/dist/presentation/routes/agent-tools-gateway/index.d.ts.map +1 -0
  104. package/dist/presentation/routes/agent-tools-gateway/index.js +46 -0
  105. package/dist/presentation/routes/agent-tools-gateway/index.js.map +1 -0
  106. package/dist/presentation/routes/agent-tools-gateway/mcp-server.d.ts +17 -0
  107. package/dist/presentation/routes/agent-tools-gateway/mcp-server.d.ts.map +1 -0
  108. package/dist/presentation/routes/agent-tools-gateway/mcp-server.js +126 -0
  109. package/dist/presentation/routes/agent-tools-gateway/mcp-server.js.map +1 -0
  110. package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.d.ts +37 -0
  111. package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.d.ts.map +1 -0
  112. package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.js +145 -0
  113. package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.js.map +1 -0
  114. package/dist/presentation/routes/errors.d.ts +2 -0
  115. package/dist/presentation/routes/errors.d.ts.map +1 -0
  116. package/dist/presentation/routes/errors.js +53 -0
  117. package/dist/presentation/routes/errors.js.map +1 -0
  118. package/dist/presentation/routes/index.d.ts +14 -0
  119. package/dist/presentation/routes/index.d.ts.map +1 -0
  120. package/dist/presentation/routes/index.js +26 -0
  121. package/dist/presentation/routes/index.js.map +1 -0
  122. package/dist/presentation/routes/list-connections.d.ts +3 -0
  123. package/dist/presentation/routes/list-connections.d.ts.map +1 -0
  124. package/dist/presentation/routes/list-connections.js +58 -0
  125. package/dist/presentation/routes/list-connections.js.map +1 -0
  126. package/dist/presentation/routes/list-providers.d.ts +3 -0
  127. package/dist/presentation/routes/list-providers.d.ts.map +1 -0
  128. package/dist/presentation/routes/list-providers.js +27 -0
  129. package/dist/presentation/routes/list-providers.js.map +1 -0
  130. package/dist/presentation/routes/list-repositories.d.ts +3 -0
  131. package/dist/presentation/routes/list-repositories.d.ts.map +1 -0
  132. package/dist/presentation/routes/list-repositories.js +41 -0
  133. package/dist/presentation/routes/list-repositories.js.map +1 -0
  134. package/dist/presentation/routes/manage-connections.d.ts +4 -0
  135. package/dist/presentation/routes/manage-connections.d.ts.map +1 -0
  136. package/dist/presentation/routes/manage-connections.js +81 -0
  137. package/dist/presentation/routes/manage-connections.js.map +1 -0
  138. package/dist/providers/connection-slug.d.ts +2 -0
  139. package/dist/providers/connection-slug.d.ts.map +1 -0
  140. package/dist/providers/connection-slug.js +19 -0
  141. package/dist/providers/connection-slug.js.map +1 -0
  142. package/dist/providers/cron.d.ts +3 -0
  143. package/dist/providers/cron.d.ts.map +1 -0
  144. package/dist/providers/cron.js +13 -0
  145. package/dist/providers/cron.js.map +1 -0
  146. package/dist/providers/gitea.d.ts +3 -0
  147. package/dist/providers/gitea.d.ts.map +1 -0
  148. package/dist/providers/gitea.js +75 -0
  149. package/dist/providers/gitea.js.map +1 -0
  150. package/dist/providers/github.d.ts +3 -0
  151. package/dist/providers/github.d.ts.map +1 -0
  152. package/dist/providers/github.js +109 -0
  153. package/dist/providers/github.js.map +1 -0
  154. package/dist/providers/linear.d.ts +3 -0
  155. package/dist/providers/linear.d.ts.map +1 -0
  156. package/dist/providers/linear.js +134 -0
  157. package/dist/providers/linear.js.map +1 -0
  158. package/dist/providers/modules.d.ts +3 -0
  159. package/dist/providers/modules.d.ts.map +1 -0
  160. package/dist/providers/modules.js +26 -0
  161. package/dist/providers/modules.js.map +1 -0
  162. package/dist/providers/sentry.d.ts +3 -0
  163. package/dist/providers/sentry.d.ts.map +1 -0
  164. package/dist/providers/sentry.js +83 -0
  165. package/dist/providers/sentry.js.map +1 -0
  166. package/dist/providers/types.d.ts +58 -0
  167. package/dist/providers/types.d.ts.map +1 -0
  168. package/dist/providers/types.js +3 -0
  169. package/dist/providers/types.js.map +1 -0
  170. package/dist/providers/webhook.d.ts +3 -0
  171. package/dist/providers/webhook.d.ts.map +1 -0
  172. package/dist/providers/webhook.js +24 -0
  173. package/dist/providers/webhook.js.map +1 -0
  174. package/dist/temporal/activities/index.d.ts +5 -0
  175. package/dist/temporal/activities/index.d.ts.map +1 -0
  176. package/dist/temporal/activities/index.js +8 -0
  177. package/dist/temporal/activities/index.js.map +1 -0
  178. package/dist/temporal/activities/prune-webhook-deliveries.d.ts +4 -0
  179. package/dist/temporal/activities/prune-webhook-deliveries.d.ts.map +1 -0
  180. package/dist/temporal/activities/prune-webhook-deliveries.js +11 -0
  181. package/dist/temporal/activities/prune-webhook-deliveries.js.map +1 -0
  182. package/dist/temporal/constants.d.ts +3 -0
  183. package/dist/temporal/constants.d.ts.map +1 -0
  184. package/dist/temporal/constants.js +4 -0
  185. package/dist/temporal/constants.js.map +1 -0
  186. package/dist/temporal/workflows/index.d.ts +2 -0
  187. package/dist/temporal/workflows/index.d.ts.map +1 -0
  188. package/dist/temporal/workflows/index.js +3 -0
  189. package/dist/temporal/workflows/index.js.map +1 -0
  190. package/dist/temporal/workflows/prune-webhook-deliveries-cron.d.ts +2 -0
  191. package/dist/temporal/workflows/prune-webhook-deliveries-cron.d.ts.map +1 -0
  192. package/dist/temporal/workflows/prune-webhook-deliveries-cron.js +14 -0
  193. package/dist/temporal/workflows/prune-webhook-deliveries-cron.js.map +1 -0
  194. package/dist/tsconfig.test.tsbuildinfo +1 -0
  195. package/drizzle/0000_initial.sql +41 -0
  196. package/drizzle/meta/0001_snapshot.json +341 -0
  197. package/drizzle/meta/_journal.json +13 -0
  198. package/drizzle.config.ts +7 -0
  199. package/package.json +85 -0
  200. package/src/config.test.ts +32 -0
  201. package/src/config.ts +28 -0
  202. package/src/core/agent-tool-selection.test.ts +137 -0
  203. package/src/core/agent-tool-selection.ts +80 -0
  204. package/src/core/entities/connection.ts +4 -0
  205. package/src/core/entities/provider.ts +21 -0
  206. package/src/core/errors.ts +55 -0
  207. package/src/core/providers/agent-tools.ts +10 -0
  208. package/src/core/providers/redact-checkout-spec.test.ts +78 -0
  209. package/src/core/providers/redact-checkout-spec.ts +20 -0
  210. package/src/core/providers/registry.test.ts +138 -0
  211. package/src/core/providers/registry.ts +90 -0
  212. package/src/core/providers/source-control.ts +17 -0
  213. package/src/core/source-control-service.test.ts +230 -0
  214. package/src/core/source-control-service.ts +161 -0
  215. package/src/db/connections.test.ts +296 -0
  216. package/src/db/connections.ts +262 -0
  217. package/src/db/db.ts +22 -0
  218. package/src/db/migrations.ts +4 -0
  219. package/src/db/schema/common.ts +3 -0
  220. package/src/db/schema/connections.ts +49 -0
  221. package/src/db/schema/outbox.ts +4 -0
  222. package/src/db/schema/webhook-deliveries.ts +19 -0
  223. package/src/db/webhook-deliveries.test.ts +267 -0
  224. package/src/db/webhook-deliveries.ts +207 -0
  225. package/src/index.ts +212 -0
  226. package/src/metrics/index.ts +1 -0
  227. package/src/metrics/instance.ts +35 -0
  228. package/src/presentation/dto/integrations.ts +39 -0
  229. package/src/presentation/routes/agent-tools-gateway/agent-tools-gateway.route.test.ts +371 -0
  230. package/src/presentation/routes/agent-tools-gateway/audit.test.ts +86 -0
  231. package/src/presentation/routes/agent-tools-gateway/audit.ts +90 -0
  232. package/src/presentation/routes/agent-tools-gateway/dispatch.ts +139 -0
  233. package/src/presentation/routes/agent-tools-gateway/index.ts +62 -0
  234. package/src/presentation/routes/agent-tools-gateway/mcp-server.test.ts +308 -0
  235. package/src/presentation/routes/agent-tools-gateway/mcp-server.ts +155 -0
  236. package/src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.test.ts +181 -0
  237. package/src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.ts +245 -0
  238. package/src/presentation/routes/errors.ts +63 -0
  239. package/src/presentation/routes/index.ts +53 -0
  240. package/src/presentation/routes/list-connections.test.ts +227 -0
  241. package/src/presentation/routes/list-connections.ts +56 -0
  242. package/src/presentation/routes/list-providers.test.ts +113 -0
  243. package/src/presentation/routes/list-providers.ts +28 -0
  244. package/src/presentation/routes/list-repositories.test.ts +150 -0
  245. package/src/presentation/routes/list-repositories.ts +47 -0
  246. package/src/presentation/routes/manage-connections.test.ts +125 -0
  247. package/src/presentation/routes/manage-connections.ts +77 -0
  248. package/src/providers/connection-slug.ts +19 -0
  249. package/src/providers/cron.ts +13 -0
  250. package/src/providers/gitea.ts +103 -0
  251. package/src/providers/github.ts +150 -0
  252. package/src/providers/linear.ts +179 -0
  253. package/src/providers/modules.test.ts +40 -0
  254. package/src/providers/modules.ts +32 -0
  255. package/src/providers/sentry.ts +113 -0
  256. package/src/providers/types.ts +52 -0
  257. package/src/providers/webhook.ts +30 -0
  258. package/src/temporal/activities/index.ts +7 -0
  259. package/src/temporal/activities/prune-webhook-deliveries.test.ts +39 -0
  260. package/src/temporal/activities/prune-webhook-deliveries.ts +9 -0
  261. package/src/temporal/constants.ts +3 -0
  262. package/src/temporal/workflows/index.ts +1 -0
  263. package/src/temporal/workflows/prune-webhook-deliveries-cron.ts +15 -0
  264. package/test/agent-tools-gateway-helpers.ts +210 -0
  265. package/test/env.ts +23 -0
  266. package/test/factories/connection.ts +21 -0
  267. package/test/globalSetup.ts +22 -0
  268. package/test/index.ts +1 -0
  269. package/test/route-utils.ts +104 -0
  270. package/test/setup.ts +19 -0
  271. package/tsconfig.build.json +9 -0
  272. package/tsconfig.build.tsbuildinfo +1 -0
  273. package/tsconfig.json +3 -0
  274. package/tsconfig.test.json +8 -0
  275. package/vitest.config.ts +13 -0
@@ -0,0 +1,207 @@
1
+ import {
2
+ INTEGRATION_EVENT_RECEIVED,
3
+ INTEGRATION_SOURCE_COMMIT_PUSHED,
4
+ type IntegrationEventReceivedEvent,
5
+ type IntegrationsEventMap,
6
+ type SourcePushPayload,
7
+ } from '@shipfox/api-integration-core-dto';
8
+ import {writeOutboxEvent, writeOutboxEvents} from '@shipfox/node-outbox';
9
+ import {lt} from 'drizzle-orm';
10
+ import {db} from './db.js';
11
+ import {integrationsOutbox} from './schema/outbox.js';
12
+ import {integrationsWebhookDeliveries} from './schema/webhook-deliveries.js';
13
+
14
+ type IntegrationDb = ReturnType<typeof db>;
15
+ type IntegrationTx = Parameters<Parameters<IntegrationDb['transaction']>[0]>[0];
16
+ type Executor = IntegrationDb | IntegrationTx;
17
+
18
+ function connectionDedupScope(connectionId: string): string {
19
+ return `connection:${connectionId}`;
20
+ }
21
+
22
+ function providerDedupScope(provider: string): string {
23
+ return `provider:${provider}`;
24
+ }
25
+
26
+ function receivedEventDedupScope(event: IntegrationEventReceivedEvent): string {
27
+ if (event.provider === 'webhook') return connectionDedupScope(event.connectionId);
28
+ return providerDedupScope(event.provider);
29
+ }
30
+
31
+ export interface PublishIntegrationEventReceivedParams {
32
+ tx: Executor;
33
+ event: IntegrationEventReceivedEvent;
34
+ }
35
+
36
+ export interface PublishIntegrationEventReceivedResult {
37
+ published: boolean;
38
+ }
39
+
40
+ export async function publishIntegrationEventReceived(
41
+ params: PublishIntegrationEventReceivedParams,
42
+ ): Promise<PublishIntegrationEventReceivedResult> {
43
+ const inserted = await params.tx
44
+ .insert(integrationsWebhookDeliveries)
45
+ .values({
46
+ provider: params.event.provider,
47
+ dedupScope: receivedEventDedupScope(params.event),
48
+ deliveryId: params.event.deliveryId,
49
+ })
50
+ .onConflictDoNothing({
51
+ target: [
52
+ integrationsWebhookDeliveries.provider,
53
+ integrationsWebhookDeliveries.dedupScope,
54
+ integrationsWebhookDeliveries.deliveryId,
55
+ ],
56
+ })
57
+ .returning({deliveryId: integrationsWebhookDeliveries.deliveryId});
58
+
59
+ if (inserted.length === 0) return {published: false};
60
+
61
+ await writeOutboxEvent(params.tx, integrationsOutbox, {
62
+ type: INTEGRATION_EVENT_RECEIVED,
63
+ payload: params.event,
64
+ });
65
+
66
+ return {published: true};
67
+ }
68
+
69
+ export interface PublishSourcePushParams {
70
+ tx: IntegrationTx;
71
+ provider: string;
72
+ source: string;
73
+ workspaceId: string;
74
+ connectionId: string;
75
+ connectionName: string;
76
+ deliveryId: string;
77
+ receivedAt: string;
78
+ rawPayload: unknown;
79
+ push: SourcePushPayload;
80
+ }
81
+
82
+ // Emits a single source-control push as two outbox rows: the generic
83
+ // `INTEGRATION_EVENT_RECEIVED` envelope with the raw provider payload for triggers, and the
84
+ // typed `INTEGRATION_SOURCE_COMMIT_PUSHED` event for domain modules. One delivery-dedup gates
85
+ // both, so a redelivered webhook writes nothing. Requires a transaction: the dedup insert and
86
+ // both outbox rows must commit or roll back together.
87
+ export async function publishSourcePush(
88
+ params: PublishSourcePushParams,
89
+ ): Promise<{published: boolean}> {
90
+ const inserted = await params.tx
91
+ .insert(integrationsWebhookDeliveries)
92
+ .values({
93
+ provider: params.provider,
94
+ dedupScope: providerDedupScope(params.provider),
95
+ deliveryId: params.deliveryId,
96
+ })
97
+ .onConflictDoNothing({
98
+ target: [
99
+ integrationsWebhookDeliveries.provider,
100
+ integrationsWebhookDeliveries.dedupScope,
101
+ integrationsWebhookDeliveries.deliveryId,
102
+ ],
103
+ })
104
+ .returning({deliveryId: integrationsWebhookDeliveries.deliveryId});
105
+
106
+ if (inserted.length === 0) return {published: false};
107
+
108
+ await writeOutboxEvents<IntegrationsEventMap>(params.tx, integrationsOutbox, [
109
+ {
110
+ type: INTEGRATION_EVENT_RECEIVED,
111
+ payload: {
112
+ provider: params.provider,
113
+ source: params.source,
114
+ event: 'push',
115
+ workspaceId: params.workspaceId,
116
+ connectionId: params.connectionId,
117
+ connectionName: params.connectionName,
118
+ deliveryId: params.deliveryId,
119
+ receivedAt: params.receivedAt,
120
+ payload: params.rawPayload,
121
+ },
122
+ },
123
+ {
124
+ type: INTEGRATION_SOURCE_COMMIT_PUSHED,
125
+ payload: {
126
+ provider: params.provider,
127
+ workspaceId: params.workspaceId,
128
+ connectionId: params.connectionId,
129
+ deliveryId: params.deliveryId,
130
+ receivedAt: params.receivedAt,
131
+ push: params.push,
132
+ },
133
+ },
134
+ ]);
135
+
136
+ return {published: true};
137
+ }
138
+
139
+ export interface PublishSourceCommitPushedParams {
140
+ provider: string;
141
+ workspaceId: string;
142
+ connectionId: string;
143
+ deliveryId: string;
144
+ receivedAt: string;
145
+ push: SourcePushPayload;
146
+ }
147
+
148
+ // Emits ONLY the typed `INTEGRATION_SOURCE_COMMIT_PUSHED` event, intentionally skipping the
149
+ // `INTEGRATION_EVENT_RECEIVED` envelope so triggers do not run workflows, and skipping the
150
+ // delivery-dedup row so it is never suppressed. Used to force a definitions re-sync without
151
+ // simulating an inbound webhook.
152
+ export async function publishSourceCommitPushed(
153
+ params: PublishSourceCommitPushedParams,
154
+ ): Promise<void> {
155
+ await writeOutboxEvent<IntegrationsEventMap>(db(), integrationsOutbox, {
156
+ type: INTEGRATION_SOURCE_COMMIT_PUSHED,
157
+ payload: {
158
+ provider: params.provider,
159
+ workspaceId: params.workspaceId,
160
+ connectionId: params.connectionId,
161
+ deliveryId: params.deliveryId,
162
+ receivedAt: params.receivedAt,
163
+ push: params.push,
164
+ },
165
+ });
166
+ }
167
+
168
+ export interface RecordDeliveryOnlyParams {
169
+ tx: Executor;
170
+ provider: string;
171
+ deliveryId: string;
172
+ }
173
+
174
+ export async function recordDeliveryOnly(params: RecordDeliveryOnlyParams): Promise<void> {
175
+ await params.tx
176
+ .insert(integrationsWebhookDeliveries)
177
+ .values({
178
+ provider: params.provider,
179
+ dedupScope: providerDedupScope(params.provider),
180
+ deliveryId: params.deliveryId,
181
+ })
182
+ .onConflictDoNothing({
183
+ target: [
184
+ integrationsWebhookDeliveries.provider,
185
+ integrationsWebhookDeliveries.dedupScope,
186
+ integrationsWebhookDeliveries.deliveryId,
187
+ ],
188
+ });
189
+ }
190
+
191
+ export interface PruneWebhookDeliveriesParams {
192
+ olderThan: Date;
193
+ }
194
+
195
+ export async function pruneWebhookDeliveries(
196
+ params: PruneWebhookDeliveriesParams,
197
+ ): Promise<{deleted: number}> {
198
+ const result = await db()
199
+ .delete(integrationsWebhookDeliveries)
200
+ .where(lt(integrationsWebhookDeliveries.receivedAt, params.olderThan));
201
+ return {deleted: result.rowCount ?? 0};
202
+ }
203
+
204
+ export type PublishIntegrationEventReceivedFn = typeof publishIntegrationEventReceived;
205
+ export type PublishSourcePushFn = typeof publishSourcePush;
206
+ export type PublishSourceCommitPushedFn = typeof publishSourceCommitPushed;
207
+ export type RecordDeliveryOnlyFn = typeof recordDeliveryOnly;
package/src/index.ts ADDED
@@ -0,0 +1,212 @@
1
+ import {dirname, resolve} from 'node:path';
2
+ import {fileURLToPath} from 'node:url';
3
+ import {integrationsEventSchemas} from '@shipfox/api-integration-core-dto';
4
+ import type {ShipfoxModule} from '@shipfox/node-module';
5
+ import {logger} from '@shipfox/node-opentelemetry';
6
+ import type {IntegrationProvider} from '#core/entities/provider.js';
7
+ import {
8
+ createIntegrationProviderRegistry,
9
+ type IntegrationProviderRegistry,
10
+ } from '#core/providers/registry.js';
11
+ import {
12
+ createSourceControlIntegrationService,
13
+ type IntegrationSourceControlService,
14
+ } from '#core/source-control-service.js';
15
+ import {getIntegrationConnectionById} from '#db/connections.js';
16
+ import {db} from '#db/db.js';
17
+ import {migrationsPath} from '#db/migrations.js';
18
+ import {integrationsOutbox} from '#db/schema/outbox.js';
19
+ import {createIntegrationRoutes, type LeasedAgentStepLoader} from '#presentation/routes/index.js';
20
+ import {loadEnabledProviderModules} from '#providers/modules.js';
21
+ import type {IntegrationModuleParts, IntegrationProviderSecrets} from '#providers/types.js';
22
+ import {createIntegrationsMaintenanceActivities} from '#temporal/activities/index.js';
23
+ import {INTEGRATIONS_MAINTENANCE_TASK_QUEUE} from '#temporal/constants.js';
24
+
25
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
26
+ const maintenanceWorkflowsPath = resolve(packageRoot, 'dist/temporal/workflows/index.js');
27
+
28
+ export {
29
+ buildProviderRepositoryId,
30
+ MAX_REPOSITORY_FILE_BYTES,
31
+ parseProviderRepositoryId,
32
+ } from '@shipfox/api-integration-core-dto';
33
+ export type {
34
+ AgentToolCatalogs,
35
+ AgentToolSelectionCatalogs,
36
+ LoadWorkspaceConnectionSnapshot,
37
+ WorkspaceConnectionSnapshot,
38
+ WorkspaceConnectionSnapshotEntry,
39
+ } from '#core/agent-tool-selection.js';
40
+ export {
41
+ buildAgentToolCatalogs,
42
+ buildAgentToolSelectionCatalogs,
43
+ createWorkspaceConnectionSnapshotLoader,
44
+ } from '#core/agent-tool-selection.js';
45
+ export type {
46
+ IntegrationConnection,
47
+ IntegrationConnectionLifecycleStatus,
48
+ } from '#core/entities/connection.js';
49
+ export type {
50
+ IntegrationCapability,
51
+ IntegrationProvider,
52
+ IntegrationProviderAdapters,
53
+ IntegrationProviderKind,
54
+ RegisteredIntegrationProvider,
55
+ } from '#core/entities/provider.js';
56
+ export type {IntegrationProviderErrorReason} from '#core/errors.js';
57
+ export {
58
+ ConnectionSlugConflictError,
59
+ IntegrationCapabilityUnavailableError,
60
+ IntegrationCheckoutUnsupportedError,
61
+ IntegrationConnectionInactiveError,
62
+ IntegrationConnectionNotFoundError,
63
+ IntegrationConnectionWorkspaceMismatchError,
64
+ IntegrationProviderError,
65
+ IntegrationProviderUnavailableError,
66
+ } from '#core/errors.js';
67
+ export type {
68
+ AgentToolCallInput,
69
+ AgentToolCatalogEntry,
70
+ AgentToolCatalogMethod,
71
+ AgentToolJsonSchema,
72
+ AgentToolSensitivity,
73
+ AgentToolSession,
74
+ AgentToolsProvider,
75
+ OpenAgentToolsSessionInput,
76
+ } from '#core/providers/agent-tools.js';
77
+ export {redactCheckoutSpec} from '#core/providers/redact-checkout-spec.js';
78
+ export type {IntegrationProviderRegistry} from '#core/providers/registry.js';
79
+ export type {
80
+ CheckoutCredentials,
81
+ CheckoutPermissions,
82
+ CheckoutSpec,
83
+ CreateCheckoutSpecInput,
84
+ FetchFileInput,
85
+ FileEntry,
86
+ FilePage,
87
+ FileSnapshot,
88
+ ListFilesInput,
89
+ ListRepositoriesInput,
90
+ RepositoryPage,
91
+ RepositorySnapshot,
92
+ RepositoryVisibility,
93
+ ResolveRepositoryInput,
94
+ SourceControlProvider,
95
+ } from '#core/providers/source-control.js';
96
+ export type {IntegrationSourceControlService} from '#core/source-control-service.js';
97
+ export {createSourceControlIntegrationService} from '#core/source-control-service.js';
98
+ export type {GetIntegrationConnectionByIdFn} from '#db/connections.js';
99
+ export {getIntegrationConnectionById} from '#db/connections.js';
100
+ export type {
101
+ PublishIntegrationEventReceivedFn,
102
+ PublishIntegrationEventReceivedParams,
103
+ PublishIntegrationEventReceivedResult,
104
+ PublishSourcePushFn,
105
+ PublishSourcePushParams,
106
+ RecordDeliveryOnlyFn,
107
+ RecordDeliveryOnlyParams,
108
+ } from '#db/webhook-deliveries.js';
109
+ export {pruneWebhookDeliveries} from '#db/webhook-deliveries.js';
110
+ export {integrationRouteErrorHandler} from '#presentation/routes/errors.js';
111
+
112
+ export interface CreateIntegrationsModuleOptions {
113
+ providers?: IntegrationProvider[] | undefined;
114
+ /**
115
+ * Pre-built module parts, bypassing config-gated provider loading. Test-only seam
116
+ * for exercising a provider's database, workers, or startup tasks directly. Takes
117
+ * precedence over `providers`.
118
+ */
119
+ parts?: IntegrationModuleParts[] | undefined;
120
+ secrets?: IntegrationProviderSecrets | undefined;
121
+ agentTools?:
122
+ | {
123
+ loadLeasedAgentStep: LeasedAgentStepLoader;
124
+ }
125
+ | undefined;
126
+ }
127
+
128
+ export interface IntegrationsContext {
129
+ module: ShipfoxModule;
130
+ registry: IntegrationProviderRegistry;
131
+ capabilities: {
132
+ sourceControl: IntegrationSourceControlService;
133
+ };
134
+ sourceControl: IntegrationSourceControlService;
135
+ /**
136
+ * Runs every enabled provider's one-shot boot-time tasks, after modules are initialized
137
+ * (migrations done). Failures are isolated and logged, never rethrown, so a provider task
138
+ * can never gate API boot. No-op when no enabled provider contributes a task.
139
+ */
140
+ runStartupTasks: () => Promise<void>;
141
+ }
142
+
143
+ export async function createIntegrationsModule(
144
+ options: CreateIntegrationsModuleOptions = {},
145
+ ): Promise<ShipfoxModule> {
146
+ return (await createIntegrationsContext(options)).module;
147
+ }
148
+
149
+ export async function createIntegrationsContext(
150
+ options: CreateIntegrationsModuleOptions = {},
151
+ ): Promise<IntegrationsContext> {
152
+ const parts: IntegrationModuleParts[] =
153
+ options.parts ??
154
+ (options.providers
155
+ ? options.providers.map((provider) => ({provider}))
156
+ : await loadEnabledProviderModules({secrets: options.secrets}));
157
+
158
+ const registry = createIntegrationProviderRegistry(parts.map((part) => part.provider));
159
+ const sourceControl = createSourceControlIntegrationService({
160
+ registry,
161
+ getIntegrationConnectionById,
162
+ });
163
+
164
+ async function runStartupTasks(): Promise<void> {
165
+ for (const task of parts.flatMap((part) => part.startupTasks ?? [])) {
166
+ // A provider convenience must never gate API boot.
167
+ try {
168
+ await task();
169
+ } catch (error) {
170
+ logger().error({err: error}, 'Integration startup task failed, continuing boot');
171
+ }
172
+ }
173
+ }
174
+
175
+ const module: ShipfoxModule = {
176
+ name: 'integrations',
177
+ startupTasks: runStartupTasks,
178
+ database: [
179
+ {db, migrationsPath},
180
+ ...parts.flatMap((part) => (part.database ? [part.database] : [])),
181
+ ],
182
+ routes: createIntegrationRoutes(registry, sourceControl, {
183
+ agentTools: options.agentTools
184
+ ? {
185
+ loadLeasedAgentStep: options.agentTools.loadLeasedAgentStep,
186
+ getIntegrationConnectionById,
187
+ }
188
+ : undefined,
189
+ }),
190
+ e2eRoutes: parts.flatMap((part) => part.e2eRoutes ?? []),
191
+ publishers: [
192
+ {name: 'integrations', table: integrationsOutbox, db, eventSchemas: integrationsEventSchemas},
193
+ ],
194
+ workers: [
195
+ {
196
+ taskQueue: INTEGRATIONS_MAINTENANCE_TASK_QUEUE,
197
+ workflowsPath: maintenanceWorkflowsPath,
198
+ activities: createIntegrationsMaintenanceActivities,
199
+ workflows: [
200
+ {
201
+ name: 'pruneWebhookDeliveriesCron',
202
+ id: 'integrations-prune-webhook-deliveries',
203
+ cronSchedule: '0 3 * * *',
204
+ },
205
+ ],
206
+ },
207
+ ...parts.flatMap((part) => part.workers ?? []),
208
+ ],
209
+ };
210
+
211
+ return {module, registry, capabilities: {sourceControl}, sourceControl, runStartupTasks};
212
+ }
@@ -0,0 +1 @@
1
+ export * from './instance.js';
@@ -0,0 +1,35 @@
1
+ import {instanceMetrics} from '@shipfox/node-opentelemetry';
2
+
3
+ const meter = instanceMetrics.getMeter('integrations');
4
+
5
+ export type IntegrationAgentToolCallOutcome =
6
+ | 'success'
7
+ | 'tool-error'
8
+ | 'invalid-request'
9
+ | 'exception';
10
+
11
+ const agentToolCallCount = meter.createCounter<{
12
+ provider: string;
13
+ tool: string;
14
+ method: string;
15
+ outcome: IntegrationAgentToolCallOutcome;
16
+ }>('integrations_agent_tool_call', {
17
+ description: 'Integration agent tool calls by provider, tool, method, and outcome',
18
+ });
19
+
20
+ function recordMetric(record: () => void): void {
21
+ try {
22
+ record();
23
+ } catch {
24
+ // Metrics must not affect integration tool call outcomes.
25
+ }
26
+ }
27
+
28
+ export function recordIntegrationAgentToolCall(params: {
29
+ provider: string;
30
+ tool: string;
31
+ method: string;
32
+ outcome: IntegrationAgentToolCallOutcome;
33
+ }): void {
34
+ recordMetric(() => agentToolCallCount.add(1, params));
35
+ }
@@ -0,0 +1,39 @@
1
+ import {toIntegrationConnectionDto as toCoreIntegrationConnectionDto} from '@shipfox/api-integration-core-dto';
2
+ import type {IntegrationConnection} from '#core/entities/connection.js';
3
+ import type {
4
+ IntegrationCapability,
5
+ RegisteredIntegrationProvider,
6
+ } from '#core/entities/provider.js';
7
+ import type {RepositorySnapshot} from '#core/providers/source-control.js';
8
+
9
+ export function toIntegrationProviderDto(provider: RegisteredIntegrationProvider) {
10
+ return {
11
+ provider: provider.provider,
12
+ display_name: provider.displayName,
13
+ capabilities: provider.capabilities,
14
+ };
15
+ }
16
+
17
+ export function toIntegrationConnectionDto(
18
+ connection: IntegrationConnection,
19
+ options: {
20
+ capabilities: IntegrationCapability[];
21
+ externalUrl?: string | undefined;
22
+ },
23
+ ) {
24
+ return toCoreIntegrationConnectionDto(connection, options);
25
+ }
26
+
27
+ export function toRepositoryDto(connectionId: string, repository: RepositorySnapshot) {
28
+ return {
29
+ connection_id: connectionId,
30
+ external_repository_id: repository.externalRepositoryId,
31
+ owner: repository.owner,
32
+ name: repository.name,
33
+ full_name: repository.fullName,
34
+ default_branch: repository.defaultBranch,
35
+ visibility: repository.visibility,
36
+ clone_url: repository.cloneUrl,
37
+ html_url: repository.htmlUrl,
38
+ };
39
+ }