@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.
- package/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +264 -0
- package/LICENSE +21 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +29 -0
- package/dist/config.js.map +1 -0
- package/dist/core/agent-tool-selection.d.ts +15 -0
- package/dist/core/agent-tool-selection.d.ts.map +1 -0
- package/dist/core/agent-tool-selection.js +48 -0
- package/dist/core/agent-tool-selection.js.map +1 -0
- package/dist/core/entities/connection.d.ts +2 -0
- package/dist/core/entities/connection.d.ts.map +1 -0
- package/dist/core/entities/connection.js +3 -0
- package/dist/core/entities/connection.js.map +1 -0
- package/dist/core/entities/provider.d.ts +6 -0
- package/dist/core/entities/provider.d.ts.map +1 -0
- package/dist/core/entities/provider.js +3 -0
- package/dist/core/entities/provider.js.map +1 -0
- package/dist/core/errors.d.ts +29 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +40 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/providers/agent-tools.d.ts +2 -0
- package/dist/core/providers/agent-tools.d.ts.map +1 -0
- package/dist/core/providers/agent-tools.js +3 -0
- package/dist/core/providers/agent-tools.js.map +1 -0
- package/dist/core/providers/redact-checkout-spec.d.ts +9 -0
- package/dist/core/providers/redact-checkout-spec.d.ts.map +1 -0
- package/dist/core/providers/redact-checkout-spec.js +25 -0
- package/dist/core/providers/redact-checkout-spec.js.map +1 -0
- package/dist/core/providers/registry.d.ts +9 -0
- package/dist/core/providers/registry.d.ts.map +1 -0
- package/dist/core/providers/registry.js +52 -0
- package/dist/core/providers/registry.js.map +1 -0
- package/dist/core/providers/source-control.d.ts +2 -0
- package/dist/core/providers/source-control.d.ts.map +1 -0
- package/dist/core/providers/source-control.js +3 -0
- package/dist/core/providers/source-control.js.map +1 -0
- package/dist/core/source-control-service.d.ts +46 -0
- package/dist/core/source-control-service.d.ts.map +1 -0
- package/dist/core/source-control-service.js +84 -0
- package/dist/core/source-control-service.js.map +1 -0
- package/dist/db/connections.d.ts +67 -0
- package/dist/db/connections.d.ts.map +1 -0
- package/dist/db/connections.js +130 -0
- package/dist/db/connections.js.map +1 -0
- package/dist/db/db.d.ts +865 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +22 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/migrations.d.ts +2 -0
- package/dist/db/migrations.d.ts.map +1 -0
- package/dist/db/migrations.js +5 -0
- package/dist/db/migrations.js.map +1 -0
- package/dist/db/schema/common.d.ts +2 -0
- package/dist/db/schema/common.d.ts.map +1 -0
- package/dist/db/schema/common.js +4 -0
- package/dist/db/schema/common.js.map +1 -0
- package/dist/db/schema/connections.d.ts +165 -0
- package/dist/db/schema/connections.d.ts.map +1 -0
- package/dist/db/schema/connections.js +37 -0
- package/dist/db/schema/connections.js.map +1 -0
- package/dist/db/schema/outbox.d.ts +195 -0
- package/dist/db/schema/outbox.d.ts.map +1 -0
- package/dist/db/schema/outbox.js +5 -0
- package/dist/db/schema/outbox.js.map +1 -0
- package/dist/db/schema/webhook-deliveries.d.ts +76 -0
- package/dist/db/schema/webhook-deliveries.d.ts.map +1 -0
- package/dist/db/schema/webhook-deliveries.js +22 -0
- package/dist/db/schema/webhook-deliveries.js.map +1 -0
- package/dist/db/webhook-deliveries.d.ts +55 -0
- package/dist/db/webhook-deliveries.d.ts.map +1 -0
- package/dist/db/webhook-deliveries.js +132 -0
- package/dist/db/webhook-deliveries.js.map +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +105 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics/index.d.ts +2 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +3 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/instance.d.ts +8 -0
- package/dist/metrics/instance.d.ts.map +1 -0
- package/dist/metrics/instance.js +17 -0
- package/dist/metrics/instance.js.map +1 -0
- package/dist/presentation/dto/integrations.d.ts +36 -0
- package/dist/presentation/dto/integrations.d.ts.map +1 -0
- package/dist/presentation/dto/integrations.js +26 -0
- package/dist/presentation/dto/integrations.js.map +1 -0
- package/dist/presentation/routes/agent-tools-gateway/audit.d.ts +24 -0
- package/dist/presentation/routes/agent-tools-gateway/audit.d.ts.map +1 -0
- package/dist/presentation/routes/agent-tools-gateway/audit.js +52 -0
- package/dist/presentation/routes/agent-tools-gateway/audit.js.map +1 -0
- package/dist/presentation/routes/agent-tools-gateway/dispatch.d.ts +7 -0
- package/dist/presentation/routes/agent-tools-gateway/dispatch.d.ts.map +1 -0
- package/dist/presentation/routes/agent-tools-gateway/dispatch.js +108 -0
- package/dist/presentation/routes/agent-tools-gateway/dispatch.js.map +1 -0
- package/dist/presentation/routes/agent-tools-gateway/index.d.ts +12 -0
- package/dist/presentation/routes/agent-tools-gateway/index.d.ts.map +1 -0
- package/dist/presentation/routes/agent-tools-gateway/index.js +46 -0
- package/dist/presentation/routes/agent-tools-gateway/index.js.map +1 -0
- package/dist/presentation/routes/agent-tools-gateway/mcp-server.d.ts +17 -0
- package/dist/presentation/routes/agent-tools-gateway/mcp-server.d.ts.map +1 -0
- package/dist/presentation/routes/agent-tools-gateway/mcp-server.js +126 -0
- package/dist/presentation/routes/agent-tools-gateway/mcp-server.js.map +1 -0
- package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.d.ts +37 -0
- package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.d.ts.map +1 -0
- package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.js +145 -0
- package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.js.map +1 -0
- package/dist/presentation/routes/errors.d.ts +2 -0
- package/dist/presentation/routes/errors.d.ts.map +1 -0
- package/dist/presentation/routes/errors.js +53 -0
- package/dist/presentation/routes/errors.js.map +1 -0
- package/dist/presentation/routes/index.d.ts +14 -0
- package/dist/presentation/routes/index.d.ts.map +1 -0
- package/dist/presentation/routes/index.js +26 -0
- package/dist/presentation/routes/index.js.map +1 -0
- package/dist/presentation/routes/list-connections.d.ts +3 -0
- package/dist/presentation/routes/list-connections.d.ts.map +1 -0
- package/dist/presentation/routes/list-connections.js +58 -0
- package/dist/presentation/routes/list-connections.js.map +1 -0
- package/dist/presentation/routes/list-providers.d.ts +3 -0
- package/dist/presentation/routes/list-providers.d.ts.map +1 -0
- package/dist/presentation/routes/list-providers.js +27 -0
- package/dist/presentation/routes/list-providers.js.map +1 -0
- package/dist/presentation/routes/list-repositories.d.ts +3 -0
- package/dist/presentation/routes/list-repositories.d.ts.map +1 -0
- package/dist/presentation/routes/list-repositories.js +41 -0
- package/dist/presentation/routes/list-repositories.js.map +1 -0
- package/dist/presentation/routes/manage-connections.d.ts +4 -0
- package/dist/presentation/routes/manage-connections.d.ts.map +1 -0
- package/dist/presentation/routes/manage-connections.js +81 -0
- package/dist/presentation/routes/manage-connections.js.map +1 -0
- package/dist/providers/connection-slug.d.ts +2 -0
- package/dist/providers/connection-slug.d.ts.map +1 -0
- package/dist/providers/connection-slug.js +19 -0
- package/dist/providers/connection-slug.js.map +1 -0
- package/dist/providers/cron.d.ts +3 -0
- package/dist/providers/cron.d.ts.map +1 -0
- package/dist/providers/cron.js +13 -0
- package/dist/providers/cron.js.map +1 -0
- package/dist/providers/gitea.d.ts +3 -0
- package/dist/providers/gitea.d.ts.map +1 -0
- package/dist/providers/gitea.js +75 -0
- package/dist/providers/gitea.js.map +1 -0
- package/dist/providers/github.d.ts +3 -0
- package/dist/providers/github.d.ts.map +1 -0
- package/dist/providers/github.js +109 -0
- package/dist/providers/github.js.map +1 -0
- package/dist/providers/linear.d.ts +3 -0
- package/dist/providers/linear.d.ts.map +1 -0
- package/dist/providers/linear.js +134 -0
- package/dist/providers/linear.js.map +1 -0
- package/dist/providers/modules.d.ts +3 -0
- package/dist/providers/modules.d.ts.map +1 -0
- package/dist/providers/modules.js +26 -0
- package/dist/providers/modules.js.map +1 -0
- package/dist/providers/sentry.d.ts +3 -0
- package/dist/providers/sentry.d.ts.map +1 -0
- package/dist/providers/sentry.js +83 -0
- package/dist/providers/sentry.js.map +1 -0
- package/dist/providers/types.d.ts +58 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +3 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/providers/webhook.d.ts +3 -0
- package/dist/providers/webhook.d.ts.map +1 -0
- package/dist/providers/webhook.js +24 -0
- package/dist/providers/webhook.js.map +1 -0
- package/dist/temporal/activities/index.d.ts +5 -0
- package/dist/temporal/activities/index.d.ts.map +1 -0
- package/dist/temporal/activities/index.js +8 -0
- package/dist/temporal/activities/index.js.map +1 -0
- package/dist/temporal/activities/prune-webhook-deliveries.d.ts +4 -0
- package/dist/temporal/activities/prune-webhook-deliveries.d.ts.map +1 -0
- package/dist/temporal/activities/prune-webhook-deliveries.js +11 -0
- package/dist/temporal/activities/prune-webhook-deliveries.js.map +1 -0
- package/dist/temporal/constants.d.ts +3 -0
- package/dist/temporal/constants.d.ts.map +1 -0
- package/dist/temporal/constants.js +4 -0
- package/dist/temporal/constants.js.map +1 -0
- package/dist/temporal/workflows/index.d.ts +2 -0
- package/dist/temporal/workflows/index.d.ts.map +1 -0
- package/dist/temporal/workflows/index.js +3 -0
- package/dist/temporal/workflows/index.js.map +1 -0
- package/dist/temporal/workflows/prune-webhook-deliveries-cron.d.ts +2 -0
- package/dist/temporal/workflows/prune-webhook-deliveries-cron.d.ts.map +1 -0
- package/dist/temporal/workflows/prune-webhook-deliveries-cron.js +14 -0
- package/dist/temporal/workflows/prune-webhook-deliveries-cron.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/drizzle/0000_initial.sql +41 -0
- package/drizzle/meta/0001_snapshot.json +341 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +7 -0
- package/package.json +85 -0
- package/src/config.test.ts +32 -0
- package/src/config.ts +28 -0
- package/src/core/agent-tool-selection.test.ts +137 -0
- package/src/core/agent-tool-selection.ts +80 -0
- package/src/core/entities/connection.ts +4 -0
- package/src/core/entities/provider.ts +21 -0
- package/src/core/errors.ts +55 -0
- package/src/core/providers/agent-tools.ts +10 -0
- package/src/core/providers/redact-checkout-spec.test.ts +78 -0
- package/src/core/providers/redact-checkout-spec.ts +20 -0
- package/src/core/providers/registry.test.ts +138 -0
- package/src/core/providers/registry.ts +90 -0
- package/src/core/providers/source-control.ts +17 -0
- package/src/core/source-control-service.test.ts +230 -0
- package/src/core/source-control-service.ts +161 -0
- package/src/db/connections.test.ts +296 -0
- package/src/db/connections.ts +262 -0
- package/src/db/db.ts +22 -0
- package/src/db/migrations.ts +4 -0
- package/src/db/schema/common.ts +3 -0
- package/src/db/schema/connections.ts +49 -0
- package/src/db/schema/outbox.ts +4 -0
- package/src/db/schema/webhook-deliveries.ts +19 -0
- package/src/db/webhook-deliveries.test.ts +267 -0
- package/src/db/webhook-deliveries.ts +207 -0
- package/src/index.ts +212 -0
- package/src/metrics/index.ts +1 -0
- package/src/metrics/instance.ts +35 -0
- package/src/presentation/dto/integrations.ts +39 -0
- package/src/presentation/routes/agent-tools-gateway/agent-tools-gateway.route.test.ts +371 -0
- package/src/presentation/routes/agent-tools-gateway/audit.test.ts +86 -0
- package/src/presentation/routes/agent-tools-gateway/audit.ts +90 -0
- package/src/presentation/routes/agent-tools-gateway/dispatch.ts +139 -0
- package/src/presentation/routes/agent-tools-gateway/index.ts +62 -0
- package/src/presentation/routes/agent-tools-gateway/mcp-server.test.ts +308 -0
- package/src/presentation/routes/agent-tools-gateway/mcp-server.ts +155 -0
- package/src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.test.ts +181 -0
- package/src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.ts +245 -0
- package/src/presentation/routes/errors.ts +63 -0
- package/src/presentation/routes/index.ts +53 -0
- package/src/presentation/routes/list-connections.test.ts +227 -0
- package/src/presentation/routes/list-connections.ts +56 -0
- package/src/presentation/routes/list-providers.test.ts +113 -0
- package/src/presentation/routes/list-providers.ts +28 -0
- package/src/presentation/routes/list-repositories.test.ts +150 -0
- package/src/presentation/routes/list-repositories.ts +47 -0
- package/src/presentation/routes/manage-connections.test.ts +125 -0
- package/src/presentation/routes/manage-connections.ts +77 -0
- package/src/providers/connection-slug.ts +19 -0
- package/src/providers/cron.ts +13 -0
- package/src/providers/gitea.ts +103 -0
- package/src/providers/github.ts +150 -0
- package/src/providers/linear.ts +179 -0
- package/src/providers/modules.test.ts +40 -0
- package/src/providers/modules.ts +32 -0
- package/src/providers/sentry.ts +113 -0
- package/src/providers/types.ts +52 -0
- package/src/providers/webhook.ts +30 -0
- package/src/temporal/activities/index.ts +7 -0
- package/src/temporal/activities/prune-webhook-deliveries.test.ts +39 -0
- package/src/temporal/activities/prune-webhook-deliveries.ts +9 -0
- package/src/temporal/constants.ts +3 -0
- package/src/temporal/workflows/index.ts +1 -0
- package/src/temporal/workflows/prune-webhook-deliveries-cron.ts +15 -0
- package/test/agent-tools-gateway-helpers.ts +210 -0
- package/test/env.ts +23 -0
- package/test/factories/connection.ts +21 -0
- package/test/globalSetup.ts +22 -0
- package/test/index.ts +1 -0
- package/test/route-utils.ts +104 -0
- package/test/setup.ts +19 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +8 -0
- 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
|
+
}
|