@mastra/factory 0.16.0-alpha.9 → 0.16.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/README.md +37 -0
- package/dist/auth.js +1 -1
- package/dist/auth.js.map +1 -1
- package/dist/boards/review.d.ts.map +1 -1
- package/dist/boards/review.js +19 -8
- package/dist/boards/review.js.map +1 -1
- package/dist/boards/work.d.ts.map +1 -1
- package/dist/boards/work.js +31 -3
- package/dist/boards/work.js.map +1 -1
- package/dist/capabilities/intake.d.ts +4 -0
- package/dist/capabilities/intake.d.ts.map +1 -1
- package/dist/capabilities/version-control.d.ts +15 -0
- package/dist/capabilities/version-control.d.ts.map +1 -1
- package/dist/factory.d.ts +7 -7
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +100 -40
- package/dist/factory.js.map +1 -1
- package/dist/integrations/base.d.ts +3 -0
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/integration.d.ts.map +1 -1
- package/dist/integrations/github/integration.js +21 -0
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/routes.js +2 -1
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +41 -42
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +416 -227
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/github/webhook.d.ts +2 -5
- package/dist/integrations/github/webhook.d.ts.map +1 -1
- package/dist/integrations/github/webhook.js +8 -48
- package/dist/integrations/github/webhook.js.map +1 -1
- package/dist/integrations/gitlab/agent-tools.d.ts +14 -0
- package/dist/integrations/gitlab/agent-tools.d.ts.map +1 -0
- package/dist/integrations/gitlab/agent-tools.js +42 -0
- package/dist/integrations/gitlab/agent-tools.js.map +1 -0
- package/dist/integrations/gitlab/api.d.ts +222 -0
- package/dist/integrations/gitlab/api.d.ts.map +1 -0
- package/dist/integrations/gitlab/api.js +274 -0
- package/dist/integrations/gitlab/api.js.map +1 -0
- package/dist/integrations/gitlab/default-rules.d.ts +125 -0
- package/dist/integrations/gitlab/default-rules.d.ts.map +1 -0
- package/dist/integrations/gitlab/default-rules.js +172 -0
- package/dist/integrations/gitlab/default-rules.js.map +1 -0
- package/dist/integrations/gitlab/integration.d.ts +138 -0
- package/dist/integrations/gitlab/integration.d.ts.map +1 -0
- package/dist/integrations/gitlab/integration.js +705 -0
- package/dist/integrations/gitlab/integration.js.map +1 -0
- package/dist/integrations/gitlab/issue-reconciler.d.ts +6 -0
- package/dist/integrations/gitlab/issue-reconciler.d.ts.map +1 -0
- package/dist/integrations/gitlab/issue-reconciler.js +123 -0
- package/dist/integrations/gitlab/issue-reconciler.js.map +1 -0
- package/dist/integrations/gitlab/merge-request-reconciler.d.ts +6 -0
- package/dist/integrations/gitlab/merge-request-reconciler.d.ts.map +1 -0
- package/dist/integrations/gitlab/merge-request-reconciler.js +267 -0
- package/dist/integrations/gitlab/merge-request-reconciler.js.map +1 -0
- package/dist/integrations/gitlab/reconciler.d.ts +5 -0
- package/dist/integrations/gitlab/reconciler.d.ts.map +1 -0
- package/dist/integrations/gitlab/reconciler.js +31 -0
- package/dist/integrations/gitlab/reconciler.js.map +1 -0
- package/dist/integrations/gitlab/reconciliation-config.d.ts +3 -0
- package/dist/integrations/gitlab/reconciliation-config.d.ts.map +1 -0
- package/dist/integrations/gitlab/reconciliation-config.js +17 -0
- package/dist/integrations/gitlab/reconciliation-config.js.map +1 -0
- package/dist/integrations/gitlab/routes.d.ts +20 -0
- package/dist/integrations/gitlab/routes.d.ts.map +1 -0
- package/dist/integrations/gitlab/routes.js +473 -0
- package/dist/integrations/gitlab/routes.js.map +1 -0
- package/dist/integrations/gitlab/rules.d.ts +35 -0
- package/dist/integrations/gitlab/rules.d.ts.map +1 -0
- package/dist/integrations/gitlab/rules.js +391 -0
- package/dist/integrations/gitlab/rules.js.map +1 -0
- package/dist/integrations/gitlab/session-subscriptions.d.ts +32 -0
- package/dist/integrations/gitlab/session-subscriptions.d.ts.map +1 -0
- package/dist/integrations/gitlab/session-subscriptions.js +192 -0
- package/dist/integrations/gitlab/session-subscriptions.js.map +1 -0
- package/dist/integrations/gitlab/subscriptions.d.ts +70 -0
- package/dist/integrations/gitlab/subscriptions.d.ts.map +1 -0
- package/dist/integrations/gitlab/subscriptions.js +76 -0
- package/dist/integrations/gitlab/subscriptions.js.map +1 -0
- package/dist/integrations/gitlab/version-control.d.ts +16 -0
- package/dist/integrations/gitlab/version-control.d.ts.map +1 -0
- package/dist/integrations/gitlab/version-control.js +622 -0
- package/dist/integrations/gitlab/version-control.js.map +1 -0
- package/dist/integrations/gitlab/webhook-dispatch.d.ts +75 -0
- package/dist/integrations/gitlab/webhook-dispatch.d.ts.map +1 -0
- package/dist/integrations/gitlab/webhook-dispatch.js +235 -0
- package/dist/integrations/gitlab/webhook-dispatch.js.map +1 -0
- package/dist/integrations/gitlab/webhook.d.ts +72 -0
- package/dist/integrations/gitlab/webhook.d.ts.map +1 -0
- package/dist/integrations/gitlab/webhook.js +201 -0
- package/dist/integrations/gitlab/webhook.js.map +1 -0
- package/dist/integrations/incidentio/integration.js +1 -1
- package/dist/integrations/issue-reconciler.d.ts +3 -3
- package/dist/integrations/issue-reconciler.d.ts.map +1 -1
- package/dist/integrations/issue-reconciler.js +22 -2
- package/dist/integrations/issue-reconciler.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +21 -0
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/platform/gitlab/event-worker.d.ts +64 -0
- package/dist/integrations/platform/gitlab/event-worker.d.ts.map +1 -0
- package/dist/integrations/platform/gitlab/event-worker.js +306 -0
- package/dist/integrations/platform/gitlab/event-worker.js.map +1 -0
- package/dist/integrations/platform/gitlab/integration.d.ts +57 -0
- package/dist/integrations/platform/gitlab/integration.d.ts.map +1 -0
- package/dist/integrations/platform/gitlab/integration.js +125 -0
- package/dist/integrations/platform/gitlab/integration.js.map +1 -0
- package/dist/integrations/platform/incidentio/integration.js +1 -1
- package/dist/integrations/slack/integration.d.ts.map +1 -1
- package/dist/integrations/slack/integration.js +3 -3
- package/dist/integrations/slack/integration.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts +2 -0
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +21 -7
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/integrations/subscription-session.d.ts +37 -0
- package/dist/integrations/subscription-session.d.ts.map +1 -0
- package/dist/integrations/subscription-session.js +83 -0
- package/dist/integrations/subscription-session.js.map +1 -0
- package/dist/packages/_internals/workspace/dist/index.js +1 -11
- package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
- package/dist/routes/intake.d.ts.map +1 -1
- package/dist/routes/intake.js +4 -2
- package/dist/routes/intake.js.map +1 -1
- package/dist/routes/oauth.js +1 -1
- package/dist/routes/projects.d.ts +1 -0
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +1 -0
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/source-control-sessions.d.ts +20 -0
- package/dist/routes/source-control-sessions.d.ts.map +1 -0
- package/dist/routes/source-control-sessions.js +319 -0
- package/dist/routes/source-control-sessions.js.map +1 -0
- package/dist/routes/source-control-settings.d.ts +10 -0
- package/dist/routes/source-control-settings.d.ts.map +1 -0
- package/dist/routes/source-control-settings.js +87 -0
- package/dist/routes/source-control-settings.js.map +1 -0
- package/dist/routes/surface.d.ts +3 -3
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +55 -13
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/tenant-credentials.d.ts +8 -0
- package/dist/routes/tenant-credentials.d.ts.map +1 -1
- package/dist/routes/tenant-credentials.js +20 -3
- package/dist/routes/tenant-credentials.js.map +1 -1
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +1 -1
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +7 -1
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/start-coordinator.d.ts +1 -1
- package/dist/rules/start-coordinator.d.ts.map +1 -1
- package/dist/rules/start-coordinator.js +3 -2
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +6 -3
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +67 -3
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js +23 -2
- package/dist/rules/types.js.map +1 -1
- package/dist/rules/validation.d.ts.map +1 -1
- package/dist/rules/validation.js +2 -0
- package/dist/rules/validation.js.map +1 -1
- package/dist/sandbox/git-ref.d.ts +7 -0
- package/dist/sandbox/git-ref.d.ts.map +1 -0
- package/dist/sandbox/git-ref.js +15 -0
- package/dist/sandbox/git-ref.js.map +1 -0
- package/dist/sandbox/session-retirement.js +1 -1
- package/dist/sandbox/workdir.d.ts.map +1 -1
- package/dist/sandbox/workdir.js +6 -6
- package/dist/sandbox/workdir.js.map +1 -1
- package/dist/session/factory-session.d.ts +31 -1
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +60 -1
- package/dist/session/factory-session.js.map +1 -1
- package/dist/session/source-control-tools.d.ts +16 -0
- package/dist/session/source-control-tools.d.ts.map +1 -0
- package/dist/session/source-control-tools.js +464 -0
- package/dist/session/source-control-tools.js.map +1 -0
- package/dist/storage/domains/intake/base.d.ts +18 -0
- package/dist/storage/domains/intake/base.d.ts.map +1 -1
- package/dist/storage/domains/intake/base.js +77 -0
- package/dist/storage/domains/intake/base.js.map +1 -1
- package/dist/work-item-branch.d.ts +3 -1
- package/dist/work-item-branch.d.ts.map +1 -1
- package/dist/work-item-branch.js +22 -4
- package/dist/work-item-branch.js.map +1 -1
- package/dist/workspace.d.ts +15 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +57 -23
- package/dist/workspace.js.map +1 -1
- package/factory-skills/factory-gitlab-rereview/SKILL.md +19 -0
- package/factory-skills/factory-gitlab-review/SKILL.md +30 -0
- package/factory-skills/factory-rereview/SKILL.md +13 -6
- package/factory-skills/factory-review/SKILL.md +13 -6
- package/factory-skills/factory-review/references/archaeology.md +225 -0
- package/factory-skills/factory-review/references/categories/README.md +34 -0
- package/factory-skills/factory-review/references/categories/behavior-change.md +36 -0
- package/factory-skills/factory-review/references/categories/bug-fix.md +37 -0
- package/factory-skills/factory-review/references/categories/docs.md +32 -0
- package/factory-skills/factory-review/references/categories/experimental-flagged.md +34 -0
- package/factory-skills/factory-review/references/categories/infra-tooling.md +37 -0
- package/factory-skills/factory-review/references/categories/internal-capability.md +35 -0
- package/factory-skills/factory-review/references/categories/large-cross-cutting.md +35 -0
- package/factory-skills/factory-review/references/categories/mechanical.md +36 -0
- package/factory-skills/factory-review/references/categories/performance.md +33 -0
- package/factory-skills/factory-review/references/categories/public-api.md +38 -0
- package/factory-skills/factory-review/references/categories/refactor.md +35 -0
- package/factory-skills/factory-review/references/categories/revert.md +31 -0
- package/factory-skills/factory-review/references/categories/schema-storage.md +39 -0
- package/factory-skills/factory-review/references/categories/security.md +35 -0
- package/factory-skills/factory-review/references/categories/tests-only.md +38 -0
- package/package.json +9 -9
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { PlatformApiError } from "../api-client.js";
|
|
2
|
+
import { parseGitLabWebhookBody, processGitLabWebhook } from "../../gitlab/webhook.js";
|
|
3
|
+
import { NoopLeaseProvider, isLeaseProvider } from "@mastra/core/events";
|
|
4
|
+
import { randomUUID } from "crypto";
|
|
5
|
+
import { MastraWorker } from "@mastra/core/worker";
|
|
6
|
+
//#region src/integrations/platform/gitlab/event-worker.ts
|
|
7
|
+
const DEFAULT_POLL_INTERVAL_MS = 2e4;
|
|
8
|
+
const EVENT_PAGE_SIZE = 500;
|
|
9
|
+
/**
|
|
10
|
+
* Pages drained per connection per cycle. A connection that never runs dry
|
|
11
|
+
* (a busy group webhook, or a backlog after downtime) hands over to the next
|
|
12
|
+
* connection after this many pages and resumes from its saved cursor next tick.
|
|
13
|
+
*/
|
|
14
|
+
const MAX_PAGES_PER_CONNECTION_PER_CYCLE = 10;
|
|
15
|
+
const MIN_LEASE_TTL_MS = 3e4;
|
|
16
|
+
const CURSOR_ORG_ID = "__platform_gitlab_event_worker__";
|
|
17
|
+
const CURSOR_USER_ID = "worker";
|
|
18
|
+
const DELIVERY_ID_PREFIX = "platform";
|
|
19
|
+
/**
|
|
20
|
+
* Tails the Platform event log for every active GitLab connection and feeds
|
|
21
|
+
* each stored webhook body through the same ingress the direct
|
|
22
|
+
* `/web/gitlab/webhook` route uses. A Platform-managed connection has no
|
|
23
|
+
* project webhook pointing at Factory, so this is how issue, note, merge
|
|
24
|
+
* request and push events reach the rules engine and subscribed sessions.
|
|
25
|
+
*
|
|
26
|
+
* One replica polls at a time (lease). Each connection keeps its own cursor,
|
|
27
|
+
* persisted only after a whole page has been processed, so a crash mid-page
|
|
28
|
+
* replays the page rather than losing it; replay handling in the rules ingress
|
|
29
|
+
* and the delivery-id dedupe keys make that safe.
|
|
30
|
+
*/
|
|
31
|
+
var PlatformGitLabEventWorker = class extends MastraWorker {
|
|
32
|
+
name = "platform-gitlab-events";
|
|
33
|
+
#client;
|
|
34
|
+
#controller;
|
|
35
|
+
#gitlab;
|
|
36
|
+
#storage;
|
|
37
|
+
#listConnections;
|
|
38
|
+
#ingestFactoryEvent;
|
|
39
|
+
#intervalMs;
|
|
40
|
+
#now;
|
|
41
|
+
#process;
|
|
42
|
+
#leaseOwner = randomUUID();
|
|
43
|
+
#running = false;
|
|
44
|
+
#timer;
|
|
45
|
+
#leaseRenewalTimer;
|
|
46
|
+
#inFlight;
|
|
47
|
+
#leaseProvider = NoopLeaseProvider;
|
|
48
|
+
#leaseTtlMs;
|
|
49
|
+
#hasLease = false;
|
|
50
|
+
#startedAt = 0;
|
|
51
|
+
#settings = {
|
|
52
|
+
version: 1,
|
|
53
|
+
connections: {}
|
|
54
|
+
};
|
|
55
|
+
constructor(config) {
|
|
56
|
+
super();
|
|
57
|
+
this.#client = config.client;
|
|
58
|
+
this.#controller = config.controller;
|
|
59
|
+
this.#gitlab = config.gitlab;
|
|
60
|
+
this.#storage = config.storage;
|
|
61
|
+
this.#listConnections = config.listConnections;
|
|
62
|
+
this.#ingestFactoryEvent = config.ingestFactoryEvent;
|
|
63
|
+
this.#intervalMs = config.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
64
|
+
if (!Number.isFinite(this.#intervalMs) || this.#intervalMs <= 0) throw new Error("Platform GitLab event polling interval must be a positive number.");
|
|
65
|
+
this.#leaseTtlMs = Math.max(MIN_LEASE_TTL_MS, this.#intervalMs * 3);
|
|
66
|
+
this.#now = config.now ?? Date.now;
|
|
67
|
+
this.#process = config.process ?? processGitLabWebhook;
|
|
68
|
+
}
|
|
69
|
+
async init(deps) {
|
|
70
|
+
await super.init(deps);
|
|
71
|
+
this.#leaseProvider = getLeaseProvider(deps.pubsub);
|
|
72
|
+
}
|
|
73
|
+
async start() {
|
|
74
|
+
if (this.#running) return;
|
|
75
|
+
if (!this.deps) throw new Error("PlatformGitLabEventWorker: call init() before start()");
|
|
76
|
+
this.#startedAt = this.#now() - 1;
|
|
77
|
+
this.#settings = normalizeSettings(await this.#storage.settings.get(CURSOR_ORG_ID, CURSOR_USER_ID));
|
|
78
|
+
this.#running = true;
|
|
79
|
+
this.deps.logger.info("Platform GitLab event polling started", {
|
|
80
|
+
intervalMs: this.#intervalMs,
|
|
81
|
+
leaseTtlMs: this.#leaseTtlMs
|
|
82
|
+
});
|
|
83
|
+
this.#schedule(0);
|
|
84
|
+
}
|
|
85
|
+
async stop() {
|
|
86
|
+
if (!this.#running) return;
|
|
87
|
+
this.#running = false;
|
|
88
|
+
if (this.#timer) clearTimeout(this.#timer);
|
|
89
|
+
this.#timer = void 0;
|
|
90
|
+
this.#stopLeaseRenewal();
|
|
91
|
+
await this.#inFlight;
|
|
92
|
+
if (this.#hasLease) {
|
|
93
|
+
await this.#leaseProvider.releaseLease(this.#leaseKey(), this.#leaseOwner).catch(() => void 0);
|
|
94
|
+
this.#hasLease = false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
get isRunning() {
|
|
98
|
+
return this.#running;
|
|
99
|
+
}
|
|
100
|
+
#schedule(delayMs) {
|
|
101
|
+
if (!this.#running) return;
|
|
102
|
+
this.#timer = setTimeout(() => {
|
|
103
|
+
this.#timer = void 0;
|
|
104
|
+
const run = this.#tick();
|
|
105
|
+
this.#inFlight = run;
|
|
106
|
+
run.finally(() => {
|
|
107
|
+
if (this.#inFlight === run) this.#inFlight = void 0;
|
|
108
|
+
});
|
|
109
|
+
}, delayMs);
|
|
110
|
+
this.#timer.unref?.();
|
|
111
|
+
}
|
|
112
|
+
async #tick() {
|
|
113
|
+
let nextDelay = this.#intervalMs;
|
|
114
|
+
try {
|
|
115
|
+
if (!await this.#ensureLease()) return;
|
|
116
|
+
nextDelay = await this.#poll();
|
|
117
|
+
} catch (error) {
|
|
118
|
+
nextDelay = retryDelay(error, this.#intervalMs);
|
|
119
|
+
this.deps?.logger.error("Platform GitLab event polling cycle failed", {
|
|
120
|
+
error: error instanceof Error ? error.message : String(error),
|
|
121
|
+
retryInMs: nextDelay
|
|
122
|
+
});
|
|
123
|
+
} finally {
|
|
124
|
+
this.#schedule(nextDelay);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async #ensureLease() {
|
|
128
|
+
if (this.#hasLease) return true;
|
|
129
|
+
const result = await this.#leaseProvider.acquireLease(this.#leaseKey(), this.#leaseOwner, this.#leaseTtlMs);
|
|
130
|
+
this.#hasLease = result.acquired;
|
|
131
|
+
if (this.#hasLease) this.#startLeaseRenewal();
|
|
132
|
+
return this.#hasLease;
|
|
133
|
+
}
|
|
134
|
+
#startLeaseRenewal() {
|
|
135
|
+
if (this.#leaseRenewalTimer) return;
|
|
136
|
+
this.#leaseRenewalTimer = setInterval(() => {
|
|
137
|
+
this.#leaseProvider.renewLease(this.#leaseKey(), this.#leaseOwner, this.#leaseTtlMs).then((renewed) => {
|
|
138
|
+
if (!renewed) {
|
|
139
|
+
this.#hasLease = false;
|
|
140
|
+
this.#stopLeaseRenewal();
|
|
141
|
+
}
|
|
142
|
+
}).catch((error) => {
|
|
143
|
+
this.#hasLease = false;
|
|
144
|
+
this.#stopLeaseRenewal();
|
|
145
|
+
this.deps?.logger.warn("Platform GitLab event polling lease renewal failed", { error: error instanceof Error ? error.message : String(error) });
|
|
146
|
+
});
|
|
147
|
+
}, Math.floor(this.#leaseTtlMs / 3));
|
|
148
|
+
this.#leaseRenewalTimer.unref?.();
|
|
149
|
+
}
|
|
150
|
+
#stopLeaseRenewal() {
|
|
151
|
+
if (this.#leaseRenewalTimer) clearInterval(this.#leaseRenewalTimer);
|
|
152
|
+
this.#leaseRenewalTimer = void 0;
|
|
153
|
+
}
|
|
154
|
+
async #poll() {
|
|
155
|
+
const connections = (await this.#listConnections()).filter((connection) => connection.status === "active");
|
|
156
|
+
let retryInMs = this.#intervalMs;
|
|
157
|
+
for (const connection of connections) {
|
|
158
|
+
if (!this.#running || !this.#hasLease) break;
|
|
159
|
+
try {
|
|
160
|
+
await this.#pollConnection(connection.id);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (error instanceof PlatformApiError && (error.status === 401 || error.status === 404)) {
|
|
163
|
+
this.deps?.logger.warn("Platform GitLab connection needs reauthorization; skipped this cycle", {
|
|
164
|
+
connectionId: connection.id,
|
|
165
|
+
status: error.status
|
|
166
|
+
});
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const delay = retryDelay(error, this.#intervalMs);
|
|
170
|
+
retryInMs = Math.max(retryInMs, delay);
|
|
171
|
+
this.deps?.logger.error("Platform GitLab connection event polling failed", {
|
|
172
|
+
connectionId: connection.id,
|
|
173
|
+
error: error instanceof Error ? error.message : String(error),
|
|
174
|
+
retryInMs: delay
|
|
175
|
+
});
|
|
176
|
+
if (error instanceof PlatformApiError && error.status === 429) break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return retryInMs;
|
|
180
|
+
}
|
|
181
|
+
async #pollConnection(connectionId) {
|
|
182
|
+
if (!this.#settings.connections[connectionId]) {
|
|
183
|
+
this.#settings.connections[connectionId] = { afterEventId: `${this.#startedAt}-0` };
|
|
184
|
+
await this.#saveSettings();
|
|
185
|
+
}
|
|
186
|
+
for (let pageIndex = 0; this.#running && this.#hasLease; pageIndex += 1) {
|
|
187
|
+
if (pageIndex >= MAX_PAGES_PER_CONNECTION_PER_CYCLE) {
|
|
188
|
+
this.deps?.logger.debug("Platform GitLab connection page budget reached; resuming next cycle", {
|
|
189
|
+
connectionId,
|
|
190
|
+
pages: pageIndex
|
|
191
|
+
});
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const cursor = this.#settings.connections[connectionId];
|
|
195
|
+
const query = new URLSearchParams({
|
|
196
|
+
afterEventId: cursor.afterEventId,
|
|
197
|
+
limit: String(EVENT_PAGE_SIZE)
|
|
198
|
+
});
|
|
199
|
+
const pollStartedAt = performance.now();
|
|
200
|
+
const page = await this.#client.request("GET", `/v2/connections/${encodeURIComponent(connectionId)}/events?${query}`);
|
|
201
|
+
this.deps?.logger.debug("Platform GitLab connection event poll completed", {
|
|
202
|
+
connectionId,
|
|
203
|
+
eventCount: page.events.length,
|
|
204
|
+
latencyMs: Math.round(performance.now() - pollStartedAt)
|
|
205
|
+
});
|
|
206
|
+
if (page.events.length === 0 || !page.nextCursor) return;
|
|
207
|
+
for (const event of page.events) {
|
|
208
|
+
if (!this.#running || !this.#hasLease) return;
|
|
209
|
+
await this.#processEvent(connectionId, event);
|
|
210
|
+
}
|
|
211
|
+
if (page.nextCursor === cursor.afterEventId) return;
|
|
212
|
+
if (!this.#hasLease) return;
|
|
213
|
+
this.#settings.connections[connectionId] = { afterEventId: page.nextCursor };
|
|
214
|
+
await this.#saveSettings();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async #processEvent(connectionId, event) {
|
|
218
|
+
if (!event.id) {
|
|
219
|
+
this.deps?.logger.warn("Platform GitLab event log returned an event without an id", { connectionId });
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const deliveryId = `${DELIVERY_ID_PREFIX}:${connectionId}:${event.id}`;
|
|
223
|
+
const parsed = parseGitLabWebhookBody(event.payload, deliveryId);
|
|
224
|
+
if (!parsed) {
|
|
225
|
+
this.deps?.logger.debug("Platform GitLab event ignored: unsupported or malformed body", {
|
|
226
|
+
connectionId,
|
|
227
|
+
eventId: event.id,
|
|
228
|
+
eventType: event.eventType
|
|
229
|
+
});
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const result = await this.#process(parsed, {
|
|
233
|
+
ingestFactoryEvent: this.#ingestFactoryEvent,
|
|
234
|
+
controller: this.#controller,
|
|
235
|
+
gitlab: this.#gitlab,
|
|
236
|
+
sourceConnectionId: connectionId,
|
|
237
|
+
onConnectionMismatch: (subscription) => {
|
|
238
|
+
this.deps?.logger.debug("Platform GitLab event skipped: subscription belongs to another connection", {
|
|
239
|
+
deliveryId,
|
|
240
|
+
subscriptionId: subscription.id,
|
|
241
|
+
subscriptionConnectionId: subscription.data.installationExternalId
|
|
242
|
+
});
|
|
243
|
+
},
|
|
244
|
+
onTargetSkipped: (subscription) => {
|
|
245
|
+
this.deps?.logger.debug("Platform GitLab event skipped: thread is not held here", {
|
|
246
|
+
deliveryId,
|
|
247
|
+
subscriptionId: subscription.id,
|
|
248
|
+
threadId: subscription.threadId
|
|
249
|
+
});
|
|
250
|
+
},
|
|
251
|
+
onSenderRejected: (notification) => {
|
|
252
|
+
this.deps?.logger.debug("Platform GitLab event dropped: sender not authorized", {
|
|
253
|
+
deliveryId,
|
|
254
|
+
repository: notification.metadata.projectPath,
|
|
255
|
+
sender: notification.metadata.sender,
|
|
256
|
+
kind: notification.kind
|
|
257
|
+
});
|
|
258
|
+
},
|
|
259
|
+
onTargetError: (subscription, error) => {
|
|
260
|
+
this.deps?.logger.error("Platform GitLab event delivery failed for a subscription", {
|
|
261
|
+
deliveryId,
|
|
262
|
+
subscriptionId: subscription.id,
|
|
263
|
+
resourceId: subscription.resourceId,
|
|
264
|
+
threadId: subscription.threadId,
|
|
265
|
+
error: error instanceof Error ? error.message : String(error)
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
if (result.status !== 202) this.deps?.logger.warn("Platform GitLab event was not accepted by the webhook processor", {
|
|
270
|
+
connectionId,
|
|
271
|
+
deliveryId,
|
|
272
|
+
status: result.status
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
async #saveSettings() {
|
|
276
|
+
await this.#storage.settings.save(CURSOR_ORG_ID, CURSOR_USER_ID, this.#settings);
|
|
277
|
+
}
|
|
278
|
+
#leaseKey() {
|
|
279
|
+
return `${this.name}:${this.#storage.integrationId}`;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
function getLeaseProvider(pubsub) {
|
|
283
|
+
const getProvider = pubsub.getLeaseProvider;
|
|
284
|
+
if (typeof getProvider === "function") return getProvider.call(pubsub) ?? NoopLeaseProvider;
|
|
285
|
+
return isLeaseProvider(pubsub) ? pubsub : NoopLeaseProvider;
|
|
286
|
+
}
|
|
287
|
+
function normalizeSettings(value) {
|
|
288
|
+
if (!value || value.version !== 1 || !value.connections || typeof value.connections !== "object") return {
|
|
289
|
+
version: 1,
|
|
290
|
+
connections: {}
|
|
291
|
+
};
|
|
292
|
+
const connections = {};
|
|
293
|
+
for (const [connectionId, cursor] of Object.entries(value.connections)) if (typeof cursor?.afterEventId === "string" && cursor.afterEventId.length > 0) connections[connectionId] = { afterEventId: cursor.afterEventId };
|
|
294
|
+
return {
|
|
295
|
+
version: 1,
|
|
296
|
+
connections
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function retryDelay(error, fallbackMs) {
|
|
300
|
+
if (error instanceof PlatformApiError && error.status === 429 && error.retryAfterSeconds !== null) return Math.max(fallbackMs, error.retryAfterSeconds * 1e3);
|
|
301
|
+
return fallbackMs;
|
|
302
|
+
}
|
|
303
|
+
//#endregion
|
|
304
|
+
export { PlatformGitLabEventWorker };
|
|
305
|
+
|
|
306
|
+
//# sourceMappingURL=event-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-worker.js","names":["#client","#controller","#gitlab","#storage","#listConnections","#ingestFactoryEvent","#intervalMs","#now","#process","#leaseOwner","#leaseTtlMs","#leaseProvider","#running","#startedAt","#settings","#schedule","#timer","#stopLeaseRenewal","#inFlight","#hasLease","#leaseKey","#tick","#ensureLease","#poll","#startLeaseRenewal","#leaseRenewalTimer","#pollConnection","#saveSettings","#processEvent"],"sources":["../../../../src/integrations/platform/gitlab/event-worker.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { MountedMastraCode } from '@mastra/code-sdk';\nimport { isLeaseProvider, NoopLeaseProvider } from '@mastra/core/events';\nimport type { LeaseProvider, PubSub } from '@mastra/core/events';\nimport { MastraWorker } from '@mastra/core/worker';\nimport type { WorkerDeps } from '@mastra/core/worker';\n\nimport type { IntegrationStorageHandle } from '../../../storage/domains/integrations/base.js';\nimport type { GitLabWebhookDispatchIntegration } from '../../gitlab/webhook-dispatch.js';\nimport { parseGitLabWebhookBody, processGitLabWebhook } from '../../gitlab/webhook.js';\nimport type { ParsedGitLabWebhook } from '../../gitlab/webhook.js';\nimport type { PlatformApiClient } from '../api-client.js';\nimport { PlatformApiError } from '../api-client.js';\n\nconst DEFAULT_POLL_INTERVAL_MS = 20_000;\nconst EVENT_PAGE_SIZE = 500;\n/**\n * Pages drained per connection per cycle. A connection that never runs dry\n * (a busy group webhook, or a backlog after downtime) hands over to the next\n * connection after this many pages and resumes from its saved cursor next tick.\n */\nconst MAX_PAGES_PER_CONNECTION_PER_CYCLE = 10;\nconst MIN_LEASE_TTL_MS = 30_000;\nconst CURSOR_ORG_ID = '__platform_gitlab_event_worker__';\nconst CURSOR_USER_ID = 'worker';\nconst DELIVERY_ID_PREFIX = 'platform';\n\ntype PlatformGitLabEventWorkerSettings = {\n version: 1;\n connections: Record<string, { afterEventId: string }>;\n};\n\nexport type PlatformGitLabEventStorage = IntegrationStorageHandle<\n Record<string, unknown>,\n PlatformGitLabEventWorkerSettings,\n Record<string, unknown>\n>;\n\n/** One row of the Platform integration event log. */\nexport interface IntegrationEventEntry {\n id: string;\n timestamp: number;\n integrationId: string;\n eventType: string;\n sourceEventId: string | null;\n payload: unknown;\n}\n\nexport interface PlatformGitLabEventConnection {\n id: string;\n status: 'active' | 'needs_reauth';\n}\n\nexport type PlatformGitLabEventDispatchIntegration = GitLabWebhookDispatchIntegration;\n\nexport interface PlatformGitLabEventWorkerConfig {\n client: PlatformApiClient;\n controller: MountedMastraCode['controller'];\n gitlab: PlatformGitLabEventDispatchIntegration;\n storage: PlatformGitLabEventStorage;\n /** Platform connections to tail; only `active` ones are polled. */\n listConnections: () => Promise<PlatformGitLabEventConnection[]>;\n ingestFactoryEvent?: (event: ParsedGitLabWebhook) => Promise<unknown>;\n intervalMs?: number;\n now?: () => number;\n process?: typeof processGitLabWebhook;\n}\n\n/**\n * Tails the Platform event log for every active GitLab connection and feeds\n * each stored webhook body through the same ingress the direct\n * `/web/gitlab/webhook` route uses. A Platform-managed connection has no\n * project webhook pointing at Factory, so this is how issue, note, merge\n * request and push events reach the rules engine and subscribed sessions.\n *\n * One replica polls at a time (lease). Each connection keeps its own cursor,\n * persisted only after a whole page has been processed, so a crash mid-page\n * replays the page rather than losing it; replay handling in the rules ingress\n * and the delivery-id dedupe keys make that safe.\n */\nexport class PlatformGitLabEventWorker extends MastraWorker {\n readonly name = 'platform-gitlab-events';\n\n readonly #client: PlatformApiClient;\n readonly #controller: MountedMastraCode['controller'];\n readonly #gitlab: PlatformGitLabEventDispatchIntegration;\n readonly #storage: PlatformGitLabEventStorage;\n readonly #listConnections: () => Promise<PlatformGitLabEventConnection[]>;\n readonly #ingestFactoryEvent: ((event: ParsedGitLabWebhook) => Promise<unknown>) | undefined;\n readonly #intervalMs: number;\n readonly #now: () => number;\n readonly #process: typeof processGitLabWebhook;\n readonly #leaseOwner = randomUUID();\n\n #running = false;\n #timer: ReturnType<typeof setTimeout> | undefined;\n #leaseRenewalTimer: ReturnType<typeof setInterval> | undefined;\n #inFlight: Promise<void> | undefined;\n #leaseProvider: LeaseProvider = NoopLeaseProvider;\n #leaseTtlMs: number;\n #hasLease = false;\n #startedAt = 0;\n #settings: PlatformGitLabEventWorkerSettings = { version: 1, connections: {} };\n\n constructor(config: PlatformGitLabEventWorkerConfig) {\n super();\n this.#client = config.client;\n this.#controller = config.controller;\n this.#gitlab = config.gitlab;\n this.#storage = config.storage;\n this.#listConnections = config.listConnections;\n this.#ingestFactoryEvent = config.ingestFactoryEvent;\n this.#intervalMs = config.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n if (!Number.isFinite(this.#intervalMs) || this.#intervalMs <= 0) {\n throw new Error('Platform GitLab event polling interval must be a positive number.');\n }\n this.#leaseTtlMs = Math.max(MIN_LEASE_TTL_MS, this.#intervalMs * 3);\n this.#now = config.now ?? Date.now;\n this.#process = config.process ?? processGitLabWebhook;\n }\n\n async init(deps: WorkerDeps): Promise<void> {\n await super.init(deps);\n this.#leaseProvider = getLeaseProvider(deps.pubsub);\n }\n\n async start(): Promise<void> {\n if (this.#running) return;\n if (!this.deps) throw new Error('PlatformGitLabEventWorker: call init() before start()');\n\n this.#startedAt = this.#now() - 1;\n this.#settings = normalizeSettings(await this.#storage.settings.get(CURSOR_ORG_ID, CURSOR_USER_ID));\n this.#running = true;\n this.deps.logger.info('Platform GitLab event polling started', {\n intervalMs: this.#intervalMs,\n leaseTtlMs: this.#leaseTtlMs,\n });\n this.#schedule(0);\n }\n\n async stop(): Promise<void> {\n if (!this.#running) return;\n this.#running = false;\n if (this.#timer) clearTimeout(this.#timer);\n this.#timer = undefined;\n this.#stopLeaseRenewal();\n await this.#inFlight;\n if (this.#hasLease) {\n await this.#leaseProvider.releaseLease(this.#leaseKey(), this.#leaseOwner).catch(() => undefined);\n this.#hasLease = false;\n }\n }\n\n get isRunning(): boolean {\n return this.#running;\n }\n\n #schedule(delayMs: number): void {\n if (!this.#running) return;\n this.#timer = setTimeout(() => {\n this.#timer = undefined;\n const run = this.#tick();\n this.#inFlight = run;\n void run.finally(() => {\n if (this.#inFlight === run) this.#inFlight = undefined;\n });\n }, delayMs);\n this.#timer.unref?.();\n }\n\n async #tick(): Promise<void> {\n let nextDelay = this.#intervalMs;\n try {\n if (!(await this.#ensureLease())) return;\n nextDelay = await this.#poll();\n } catch (error) {\n nextDelay = retryDelay(error, this.#intervalMs);\n this.deps?.logger.error('Platform GitLab event polling cycle failed', {\n error: error instanceof Error ? error.message : String(error),\n retryInMs: nextDelay,\n });\n } finally {\n this.#schedule(nextDelay);\n }\n }\n\n async #ensureLease(): Promise<boolean> {\n if (this.#hasLease) return true;\n const result = await this.#leaseProvider.acquireLease(this.#leaseKey(), this.#leaseOwner, this.#leaseTtlMs);\n this.#hasLease = result.acquired;\n if (this.#hasLease) this.#startLeaseRenewal();\n return this.#hasLease;\n }\n\n #startLeaseRenewal(): void {\n if (this.#leaseRenewalTimer) return;\n this.#leaseRenewalTimer = setInterval(\n () => {\n void this.#leaseProvider\n .renewLease(this.#leaseKey(), this.#leaseOwner, this.#leaseTtlMs)\n .then(renewed => {\n if (!renewed) {\n this.#hasLease = false;\n this.#stopLeaseRenewal();\n }\n })\n .catch(error => {\n this.#hasLease = false;\n this.#stopLeaseRenewal();\n this.deps?.logger.warn('Platform GitLab event polling lease renewal failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n },\n Math.floor(this.#leaseTtlMs / 3),\n );\n this.#leaseRenewalTimer.unref?.();\n }\n\n #stopLeaseRenewal(): void {\n if (this.#leaseRenewalTimer) clearInterval(this.#leaseRenewalTimer);\n this.#leaseRenewalTimer = undefined;\n }\n\n async #poll(): Promise<number> {\n const connections = (await this.#listConnections()).filter(connection => connection.status === 'active');\n let retryInMs = this.#intervalMs;\n\n for (const connection of connections) {\n if (!this.#running || !this.#hasLease) break;\n try {\n await this.#pollConnection(connection.id);\n } catch (error) {\n if (error instanceof PlatformApiError && (error.status === 401 || error.status === 404)) {\n // Platform no longer serves this connection (revoked, expired or\n // deleted). Its status will read needs_reauth on the next listing;\n // nothing to retry here, so the other connections keep flowing.\n this.deps?.logger.warn('Platform GitLab connection needs reauthorization; skipped this cycle', {\n connectionId: connection.id,\n status: error.status,\n });\n continue;\n }\n const delay = retryDelay(error, this.#intervalMs);\n retryInMs = Math.max(retryInMs, delay);\n this.deps?.logger.error('Platform GitLab connection event polling failed', {\n connectionId: connection.id,\n error: error instanceof Error ? error.message : String(error),\n retryInMs: delay,\n });\n if (error instanceof PlatformApiError && error.status === 429) break;\n }\n }\n return retryInMs;\n }\n\n async #pollConnection(connectionId: string): Promise<void> {\n if (!this.#settings.connections[connectionId]) {\n // Event ids are Redis stream ids (`<unix ms>-<sequence>`), so a\n // synthesized id at start time skips history the same way the GitHub\n // worker's start timestamp does. Events that arrived before Factory was\n // deployed are not work Factory was asked to do.\n this.#settings.connections[connectionId] = { afterEventId: `${this.#startedAt}-0` };\n await this.#saveSettings();\n }\n\n for (let pageIndex = 0; this.#running && this.#hasLease; pageIndex += 1) {\n if (pageIndex >= MAX_PAGES_PER_CONNECTION_PER_CYCLE) {\n this.deps?.logger.debug('Platform GitLab connection page budget reached; resuming next cycle', {\n connectionId,\n pages: pageIndex,\n });\n return;\n }\n const cursor: { afterEventId: string } = this.#settings.connections[connectionId]!;\n const query = new URLSearchParams({ afterEventId: cursor.afterEventId, limit: String(EVENT_PAGE_SIZE) });\n const pollStartedAt = performance.now();\n const page = await this.#client.request<{ events: IntegrationEventEntry[]; nextCursor: string | null }>(\n 'GET',\n `/v2/connections/${encodeURIComponent(connectionId)}/events?${query}`,\n );\n this.deps?.logger.debug('Platform GitLab connection event poll completed', {\n connectionId,\n eventCount: page.events.length,\n latencyMs: Math.round(performance.now() - pollStartedAt),\n });\n if (page.events.length === 0 || !page.nextCursor) return;\n\n for (const event of page.events) {\n if (!this.#running || !this.#hasLease) return;\n await this.#processEvent(connectionId, event);\n }\n\n if (page.nextCursor === cursor.afterEventId) return;\n // A replica that lost its lease while processing this page must not\n // write a cursor over one the new holder has already advanced. The\n // page is replayed there; delivery is deduplicated by delivery id.\n if (!this.#hasLease) return;\n this.#settings.connections[connectionId] = { afterEventId: page.nextCursor };\n await this.#saveSettings();\n }\n }\n\n async #processEvent(connectionId: string, event: IntegrationEventEntry): Promise<void> {\n if (!event.id) {\n this.deps?.logger.warn('Platform GitLab event log returned an event without an id', { connectionId });\n return;\n }\n const deliveryId = `${DELIVERY_ID_PREFIX}:${connectionId}:${event.id}`;\n const parsed = parseGitLabWebhookBody(event.payload, deliveryId);\n if (!parsed) {\n // Pipelines, deployments, wiki edits and the like: acknowledged and\n // dropped, exactly as the direct route treats an unsupported event header.\n this.deps?.logger.debug('Platform GitLab event ignored: unsupported or malformed body', {\n connectionId,\n eventId: event.id,\n eventType: event.eventType,\n });\n return;\n }\n\n const result = await this.#process(parsed, {\n ingestFactoryEvent: this.#ingestFactoryEvent,\n controller: this.#controller,\n // The integration supplies subscription storage and the project-member\n // trust check, so the dispatcher's default author gate applies unchanged.\n gitlab: this.#gitlab,\n sourceConnectionId: connectionId,\n onConnectionMismatch: subscription => {\n // Expected whenever two connections reach the same project: the\n // event is delivered by the connection that created the subscription.\n this.deps?.logger.debug('Platform GitLab event skipped: subscription belongs to another connection', {\n deliveryId,\n subscriptionId: subscription.id,\n subscriptionConnectionId: subscription.data.installationExternalId,\n });\n },\n onTargetSkipped: subscription => {\n // Routine when a subscription's thread belongs to another deployment,\n // so this stays at debug rather than warning on a loop.\n this.deps?.logger.debug('Platform GitLab event skipped: thread is not held here', {\n deliveryId,\n subscriptionId: subscription.id,\n threadId: subscription.threadId,\n });\n },\n onSenderRejected: notification => {\n this.deps?.logger.debug('Platform GitLab event dropped: sender not authorized', {\n deliveryId,\n repository: notification.metadata.projectPath,\n sender: notification.metadata.sender,\n kind: notification.kind,\n });\n },\n onTargetError: (subscription, error) => {\n this.deps?.logger.error('Platform GitLab event delivery failed for a subscription', {\n deliveryId,\n subscriptionId: subscription.id,\n resourceId: subscription.resourceId,\n threadId: subscription.threadId,\n error: error instanceof Error ? error.message : String(error),\n });\n },\n });\n if (result.status !== 202) {\n this.deps?.logger.warn('Platform GitLab event was not accepted by the webhook processor', {\n connectionId,\n deliveryId,\n status: result.status,\n });\n }\n }\n\n async #saveSettings(): Promise<void> {\n await this.#storage.settings.save(CURSOR_ORG_ID, CURSOR_USER_ID, this.#settings);\n }\n\n #leaseKey(): string {\n return `${this.name}:${this.#storage.integrationId}`;\n }\n}\n\nfunction getLeaseProvider(pubsub: PubSub): LeaseProvider {\n const getProvider = (pubsub as PubSub & { getLeaseProvider?: () => LeaseProvider | undefined }).getLeaseProvider;\n if (typeof getProvider === 'function') return getProvider.call(pubsub) ?? NoopLeaseProvider;\n return isLeaseProvider(pubsub) ? pubsub : NoopLeaseProvider;\n}\n\nfunction normalizeSettings(value: PlatformGitLabEventWorkerSettings | null): PlatformGitLabEventWorkerSettings {\n if (!value || value.version !== 1 || !value.connections || typeof value.connections !== 'object') {\n return { version: 1, connections: {} };\n }\n const connections: PlatformGitLabEventWorkerSettings['connections'] = {};\n for (const [connectionId, cursor] of Object.entries(value.connections)) {\n if (typeof cursor?.afterEventId === 'string' && cursor.afterEventId.length > 0) {\n connections[connectionId] = { afterEventId: cursor.afterEventId };\n }\n }\n return { version: 1, connections };\n}\n\nfunction retryDelay(error: unknown, fallbackMs: number): number {\n if (error instanceof PlatformApiError && error.status === 429 && error.retryAfterSeconds !== null) {\n return Math.max(fallbackMs, error.retryAfterSeconds * 1_000);\n }\n return fallbackMs;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { MastraWorker } from '@mastra/core/worker';
|
|
2
|
+
import type { IntegrationConnection } from '../../../capabilities/connection.js';
|
|
3
|
+
import type { IntegrationContext } from '../../base.js';
|
|
4
|
+
import { GitLabApiClient } from '../../gitlab/api.js';
|
|
5
|
+
import type { GitLabRuleOverrides } from '../../gitlab/default-rules.js';
|
|
6
|
+
import { GitLabIntegrationBase } from '../../gitlab/integration.js';
|
|
7
|
+
import type { GitLabStatusConnection } from '../../gitlab/integration.js';
|
|
8
|
+
import type { PlatformApiClientConfig } from '../api-client.js';
|
|
9
|
+
interface PlatformIntegrationConnection {
|
|
10
|
+
id: string;
|
|
11
|
+
integrationId: string;
|
|
12
|
+
status: 'active' | 'needs_reauth';
|
|
13
|
+
accountLabel: string | null;
|
|
14
|
+
}
|
|
15
|
+
interface PlatformGitLabContext {
|
|
16
|
+
id: string;
|
|
17
|
+
label: string | null;
|
|
18
|
+
api: GitLabApiClient;
|
|
19
|
+
connection: IntegrationConnection;
|
|
20
|
+
host: string;
|
|
21
|
+
repositoryAccessToken: () => Promise<string>;
|
|
22
|
+
}
|
|
23
|
+
export interface PlatformGitLabIntegrationConfig {
|
|
24
|
+
rules?: GitLabRuleOverrides;
|
|
25
|
+
clientConfig?: PlatformApiClientConfig;
|
|
26
|
+
connectionId?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Optional. Only a direct project webhook to `/web/gitlab/webhook` needs it;
|
|
29
|
+
* a Platform-connected deployment receives events by polling instead.
|
|
30
|
+
*/
|
|
31
|
+
webhookSecret?: string;
|
|
32
|
+
/** Defaults to `MASTRA_PLATFORM_GITLAB_POLLING_ENABLED`, and to true when that is unset. */
|
|
33
|
+
pollingEnabled?: boolean;
|
|
34
|
+
/** Defaults to `MASTRA_PLATFORM_GITLAB_POLLING_INTERVAL_MS`, and to the worker's 20 s when unset. */
|
|
35
|
+
pollingIntervalMs?: number;
|
|
36
|
+
}
|
|
37
|
+
export declare class PlatformGitLabIntegration extends GitLabIntegrationBase {
|
|
38
|
+
#private;
|
|
39
|
+
constructor(config?: PlatformGitLabIntegrationConfig);
|
|
40
|
+
listConnections(): Promise<PlatformIntegrationConnection[]>;
|
|
41
|
+
statusConnections(): Promise<GitLabStatusConnection[]>;
|
|
42
|
+
hasActiveConnections(): Promise<boolean>;
|
|
43
|
+
authFailureMessage(): string;
|
|
44
|
+
protected get webhookSecret(): string | undefined;
|
|
45
|
+
protected activeContexts(): Promise<PlatformGitLabContext[]>;
|
|
46
|
+
protected contextById(connectionId: string): Promise<PlatformGitLabContext>;
|
|
47
|
+
/**
|
|
48
|
+
* The reconcile sweeps from the base class plus, when polling is on, the
|
|
49
|
+
* Platform event poller. Polling replaces the direct project webhook: the
|
|
50
|
+
* webhook route stays mounted for deployments that also configured a secret,
|
|
51
|
+
* but nothing in Platform mode requires one.
|
|
52
|
+
*/
|
|
53
|
+
workers(ctx: IntegrationContext): MastraWorker[];
|
|
54
|
+
diagnostics(): Record<string, unknown>;
|
|
55
|
+
}
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=integration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/gitlab/integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,eAAe,EAAkB,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAoB,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACtF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAG1E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAIhE,UAAU,6BAA6B;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,QAAQ,GAAG,cAAc,CAAC;IAClC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,UAAU,qBAAqB;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,EAAE,eAAe,CAAC;IACrB,UAAU,EAAE,qBAAqB,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9C;AAOD,MAAM,WAAW,+BAA+B;IAC9C,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,YAAY,CAAC,EAAE,uBAAuB,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4FAA4F;IAC5F,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qGAAqG;IACrG,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,qBAAa,yBAA0B,SAAQ,qBAAqB;;gBAQtD,MAAM,GAAE,+BAAoC;IAclD,eAAe,IAAI,OAAO,CAAC,6BAA6B,EAAE,CAAC;IA0BlD,iBAAiB,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC;IAI/D,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;IAI9C,kBAAkB,IAAI,MAAM;IAI5B,cAAuB,aAAa,IAAI,MAAM,GAAG,SAAS,CAEzD;cAEe,cAAc,IAAI,OAAO,CAAC,qBAAqB,EAAE,CAAC;cAIlD,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAMjF;;;;;OAKG;IACM,OAAO,CAAC,GAAG,EAAE,kBAAkB,GAAG,YAAY,EAAE;IAoBzD,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CA4CvC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { PlatformApiClient, platformApiClientConfigFromEnv } from "../api-client.js";
|
|
2
|
+
import { GitLabApiClient, GitLabApiError } from "../../gitlab/api.js";
|
|
3
|
+
import { attachGitLabRules } from "../../gitlab/rules.js";
|
|
4
|
+
import { GitLabIntegrationBase, gitlabConnection } from "../../gitlab/integration.js";
|
|
5
|
+
import { PlatformGitLabEventWorker } from "./event-worker.js";
|
|
6
|
+
//#region src/integrations/platform/gitlab/integration.ts
|
|
7
|
+
const GITLAB_INTEGRATION_IDS = /* @__PURE__ */ new Set([
|
|
8
|
+
"gitlab",
|
|
9
|
+
"gitlab-group",
|
|
10
|
+
"gitlab-group-token",
|
|
11
|
+
"gitlab-pat"
|
|
12
|
+
]);
|
|
13
|
+
var PlatformGitLabIntegration = class extends GitLabIntegrationBase {
|
|
14
|
+
#client;
|
|
15
|
+
#connectionId;
|
|
16
|
+
#endpointHost;
|
|
17
|
+
#webhookSecret;
|
|
18
|
+
#pollingEnabled;
|
|
19
|
+
#pollingIntervalMs;
|
|
20
|
+
constructor(config = {}) {
|
|
21
|
+
super(config.rules);
|
|
22
|
+
const connectionId = config.connectionId?.trim() || process.env.MASTRA_GITLAB_CONNECTION_ID?.trim() || void 0;
|
|
23
|
+
const clientConfig = config.clientConfig ?? platformApiClientConfigFromEnv();
|
|
24
|
+
this.#client = new PlatformApiClient(clientConfig);
|
|
25
|
+
this.#connectionId = connectionId;
|
|
26
|
+
this.#endpointHost = new URL(clientConfig.baseUrl).host;
|
|
27
|
+
this.#webhookSecret = config.webhookSecret?.trim() || process.env.MASTRA_GITLAB_WEBHOOK_SECRET?.trim() || void 0;
|
|
28
|
+
this.#pollingEnabled = config.pollingEnabled ?? process.env.MASTRA_PLATFORM_GITLAB_POLLING_ENABLED?.trim().toLowerCase() !== "false";
|
|
29
|
+
this.#pollingIntervalMs = config.pollingIntervalMs ?? optionalPositiveIntegerEnv("MASTRA_PLATFORM_GITLAB_POLLING_INTERVAL_MS");
|
|
30
|
+
}
|
|
31
|
+
async listConnections() {
|
|
32
|
+
const pages = await Promise.all([...GITLAB_INTEGRATION_IDS].map(async (integrationId) => {
|
|
33
|
+
return (await this.#client.request("GET", `/v2/connections?providerKey=${encodeURIComponent(integrationId)}`)).connections;
|
|
34
|
+
}));
|
|
35
|
+
const seen = /* @__PURE__ */ new Set();
|
|
36
|
+
return pages.flat().filter((connection) => {
|
|
37
|
+
if (this.#connectionId && connection.id !== this.#connectionId || !GITLAB_INTEGRATION_IDS.has(connection.integrationId) || seen.has(connection.id)) return false;
|
|
38
|
+
seen.add(connection.id);
|
|
39
|
+
return true;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async statusConnections() {
|
|
43
|
+
return this.listConnections();
|
|
44
|
+
}
|
|
45
|
+
async hasActiveConnections() {
|
|
46
|
+
return (await this.#activeConnections()).length > 0;
|
|
47
|
+
}
|
|
48
|
+
authFailureMessage() {
|
|
49
|
+
return "GitLab rejected the connected account. Reconnect it in Mastra Platform.";
|
|
50
|
+
}
|
|
51
|
+
get webhookSecret() {
|
|
52
|
+
return this.#webhookSecret;
|
|
53
|
+
}
|
|
54
|
+
async activeContexts() {
|
|
55
|
+
return (await this.#activeConnections()).map((connection) => this.#context(connection));
|
|
56
|
+
}
|
|
57
|
+
async contextById(connectionId) {
|
|
58
|
+
const connection = (await this.#activeConnections()).find((candidate) => candidate.id === connectionId);
|
|
59
|
+
if (!connection) throw new GitLabApiError("GitLab connection is unavailable or requires reauthentication.", 401);
|
|
60
|
+
return this.#context(connection);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The reconcile sweeps from the base class plus, when polling is on, the
|
|
64
|
+
* Platform event poller. Polling replaces the direct project webhook: the
|
|
65
|
+
* webhook route stays mounted for deployments that also configured a secret,
|
|
66
|
+
* but nothing in Platform mode requires one.
|
|
67
|
+
*/
|
|
68
|
+
workers(ctx) {
|
|
69
|
+
const workers = super.workers(ctx);
|
|
70
|
+
if (!this.#pollingEnabled) return workers;
|
|
71
|
+
if (!ctx.controller) throw new Error("Platform GitLab event polling requires the mounted Mastra Code controller.");
|
|
72
|
+
return [...workers, new PlatformGitLabEventWorker({
|
|
73
|
+
client: this.#client,
|
|
74
|
+
controller: ctx.controller,
|
|
75
|
+
gitlab: this,
|
|
76
|
+
storage: ctx.storage.generic,
|
|
77
|
+
listConnections: () => this.listConnections(),
|
|
78
|
+
ingestFactoryEvent: attachGitLabRules(this, ctx),
|
|
79
|
+
intervalMs: this.#pollingIntervalMs
|
|
80
|
+
})];
|
|
81
|
+
}
|
|
82
|
+
diagnostics() {
|
|
83
|
+
return {
|
|
84
|
+
configured: true,
|
|
85
|
+
mode: "platform",
|
|
86
|
+
endpointHost: this.#endpointHost,
|
|
87
|
+
connectionFilterConfigured: Boolean(this.#connectionId),
|
|
88
|
+
webhookConfigured: Boolean(this.#webhookSecret),
|
|
89
|
+
pollingEnabled: this.#pollingEnabled,
|
|
90
|
+
...this.#pollingIntervalMs === void 0 ? {} : { pollingIntervalMs: this.#pollingIntervalMs }
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
async #activeConnections() {
|
|
94
|
+
return (await this.listConnections()).filter((connection) => connection.status === "active");
|
|
95
|
+
}
|
|
96
|
+
#context(connection) {
|
|
97
|
+
return {
|
|
98
|
+
id: connection.id,
|
|
99
|
+
label: connection.accountLabel,
|
|
100
|
+
api: new GitLabApiClient({
|
|
101
|
+
client: this.#client,
|
|
102
|
+
connectionId: connection.id
|
|
103
|
+
}),
|
|
104
|
+
connection: gitlabConnection(connection.id),
|
|
105
|
+
host: "gitlab.com",
|
|
106
|
+
repositoryAccessToken: async () => {
|
|
107
|
+
const credential = await this.#client.request("GET", `/v2/connections/${encodeURIComponent(connection.id)}/credentials`);
|
|
108
|
+
const token = credential?.type === "oauth2" ? credential.accessToken : credential?.type === "api_key" ? credential.apiKey : void 0;
|
|
109
|
+
if (typeof token !== "string" || !token.trim()) throw new GitLabApiError("GitLab connection did not provide a repository credential.", 502);
|
|
110
|
+
return token;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
function optionalPositiveIntegerEnv(name) {
|
|
116
|
+
const value = process.env[name]?.trim();
|
|
117
|
+
if (!value) return void 0;
|
|
118
|
+
const parsed = Number(value);
|
|
119
|
+
if (!/^[1-9]\d*$/.test(value) || !Number.isSafeInteger(parsed)) throw new Error(`${name} must be a positive integer.`);
|
|
120
|
+
return parsed;
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
export { PlatformGitLabIntegration };
|
|
124
|
+
|
|
125
|
+
//# sourceMappingURL=integration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.js","names":["#client","#connectionId","#endpointHost","#webhookSecret","#pollingEnabled","#pollingIntervalMs","#activeConnections","#context"],"sources":["../../../../src/integrations/platform/gitlab/integration.ts"],"sourcesContent":["import type { MastraWorker } from '@mastra/core/worker';\n\nimport type { IntegrationConnection } from '../../../capabilities/connection.js';\nimport type { IntegrationContext } from '../../base.js';\nimport { GitLabApiClient, GitLabApiError } from '../../gitlab/api.js';\nimport type { GitLabRuleOverrides } from '../../gitlab/default-rules.js';\nimport { gitlabConnection, GitLabIntegrationBase } from '../../gitlab/integration.js';\nimport type { GitLabStatusConnection } from '../../gitlab/integration.js';\nimport { attachGitLabRules } from '../../gitlab/rules.js';\nimport { PlatformApiClient, platformApiClientConfigFromEnv } from '../api-client.js';\nimport type { PlatformApiClientConfig } from '../api-client.js';\nimport { PlatformGitLabEventWorker } from './event-worker.js';\nimport type { PlatformGitLabEventStorage } from './event-worker.js';\n\ninterface PlatformIntegrationConnection {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n}\n\ninterface PlatformGitLabContext {\n id: string;\n label: string | null;\n api: GitLabApiClient;\n connection: IntegrationConnection;\n host: string;\n repositoryAccessToken: () => Promise<string>;\n}\n\ntype PlatformGitLabCredential =\n { type: 'oauth2'; accessToken: string; expiresAt: string | null } | { type: 'api_key'; apiKey: string };\n\nconst GITLAB_INTEGRATION_IDS = new Set(['gitlab', 'gitlab-group', 'gitlab-group-token', 'gitlab-pat']);\n\nexport interface PlatformGitLabIntegrationConfig {\n rules?: GitLabRuleOverrides;\n clientConfig?: PlatformApiClientConfig;\n connectionId?: string;\n /**\n * Optional. Only a direct project webhook to `/web/gitlab/webhook` needs it;\n * a Platform-connected deployment receives events by polling instead.\n */\n webhookSecret?: string;\n /** Defaults to `MASTRA_PLATFORM_GITLAB_POLLING_ENABLED`, and to true when that is unset. */\n pollingEnabled?: boolean;\n /** Defaults to `MASTRA_PLATFORM_GITLAB_POLLING_INTERVAL_MS`, and to the worker's 20 s when unset. */\n pollingIntervalMs?: number;\n}\n\nexport class PlatformGitLabIntegration extends GitLabIntegrationBase {\n readonly #client: PlatformApiClient;\n readonly #connectionId: string | undefined;\n readonly #endpointHost: string;\n readonly #webhookSecret: string | undefined;\n readonly #pollingEnabled: boolean;\n readonly #pollingIntervalMs: number | undefined;\n\n constructor(config: PlatformGitLabIntegrationConfig = {}) {\n super(config.rules);\n const connectionId = config.connectionId?.trim() || process.env.MASTRA_GITLAB_CONNECTION_ID?.trim() || undefined;\n const clientConfig = config.clientConfig ?? platformApiClientConfigFromEnv();\n this.#client = new PlatformApiClient(clientConfig);\n this.#connectionId = connectionId;\n this.#endpointHost = new URL(clientConfig.baseUrl).host;\n this.#webhookSecret = config.webhookSecret?.trim() || process.env.MASTRA_GITLAB_WEBHOOK_SECRET?.trim() || undefined;\n this.#pollingEnabled =\n config.pollingEnabled ?? process.env.MASTRA_PLATFORM_GITLAB_POLLING_ENABLED?.trim().toLowerCase() !== 'false';\n this.#pollingIntervalMs =\n config.pollingIntervalMs ?? optionalPositiveIntegerEnv('MASTRA_PLATFORM_GITLAB_POLLING_INTERVAL_MS');\n }\n\n async listConnections(): Promise<PlatformIntegrationConnection[]> {\n // Platform's providerKey filter matches a single integration ID, so query\n // each supported GitLab credential flow before applying the exact ID filter.\n const pages = await Promise.all(\n [...GITLAB_INTEGRATION_IDS].map(async integrationId => {\n const result = await this.#client.request<{ connections: PlatformIntegrationConnection[] }>(\n 'GET',\n `/v2/connections?providerKey=${encodeURIComponent(integrationId)}`,\n );\n return result.connections;\n }),\n );\n const seen = new Set<string>();\n return pages.flat().filter(connection => {\n if (\n (this.#connectionId && connection.id !== this.#connectionId) ||\n !GITLAB_INTEGRATION_IDS.has(connection.integrationId) ||\n seen.has(connection.id)\n ) {\n return false;\n }\n seen.add(connection.id);\n return true;\n });\n }\n\n override async statusConnections(): Promise<GitLabStatusConnection[]> {\n return this.listConnections();\n }\n\n async hasActiveConnections(): Promise<boolean> {\n return (await this.#activeConnections()).length > 0;\n }\n\n authFailureMessage(): string {\n return 'GitLab rejected the connected account. Reconnect it in Mastra Platform.';\n }\n\n protected override get webhookSecret(): string | undefined {\n return this.#webhookSecret;\n }\n\n protected async activeContexts(): Promise<PlatformGitLabContext[]> {\n return (await this.#activeConnections()).map(connection => this.#context(connection));\n }\n\n protected async contextById(connectionId: string): Promise<PlatformGitLabContext> {\n const connection = (await this.#activeConnections()).find(candidate => candidate.id === connectionId);\n if (!connection) throw new GitLabApiError('GitLab connection is unavailable or requires reauthentication.', 401);\n return this.#context(connection);\n }\n\n /**\n * The reconcile sweeps from the base class plus, when polling is on, the\n * Platform event poller. Polling replaces the direct project webhook: the\n * webhook route stays mounted for deployments that also configured a secret,\n * but nothing in Platform mode requires one.\n */\n override workers(ctx: IntegrationContext): MastraWorker[] {\n const workers = super.workers(ctx);\n if (!this.#pollingEnabled) return workers;\n if (!ctx.controller) {\n throw new Error('Platform GitLab event polling requires the mounted Mastra Code controller.');\n }\n return [\n ...workers,\n new PlatformGitLabEventWorker({\n client: this.#client,\n controller: ctx.controller,\n gitlab: this,\n storage: ctx.storage.generic as unknown as PlatformGitLabEventStorage,\n listConnections: () => this.listConnections(),\n ingestFactoryEvent: attachGitLabRules(this, ctx),\n intervalMs: this.#pollingIntervalMs,\n }),\n ];\n }\n\n diagnostics(): Record<string, unknown> {\n return {\n configured: true,\n mode: 'platform',\n endpointHost: this.#endpointHost,\n connectionFilterConfigured: Boolean(this.#connectionId),\n webhookConfigured: Boolean(this.#webhookSecret),\n pollingEnabled: this.#pollingEnabled,\n ...(this.#pollingIntervalMs === undefined ? {} : { pollingIntervalMs: this.#pollingIntervalMs }),\n };\n }\n\n async #activeConnections(): Promise<PlatformIntegrationConnection[]> {\n return (await this.listConnections()).filter(connection => connection.status === 'active');\n }\n\n #context(connection: PlatformIntegrationConnection): PlatformGitLabContext {\n return {\n id: connection.id,\n label: connection.accountLabel,\n api: new GitLabApiClient({ client: this.#client, connectionId: connection.id }),\n connection: gitlabConnection(connection.id),\n // Platform does not currently expose the connected GitLab instance host.\n host: 'gitlab.com',\n repositoryAccessToken: async () => {\n // Fetch for each git operation so an expiring OAuth token is refreshed by Platform.\n // Never persist or log the returned credential.\n const credential = await this.#client.request<PlatformGitLabCredential>(\n 'GET',\n `/v2/connections/${encodeURIComponent(connection.id)}/credentials`,\n );\n const token =\n credential?.type === 'oauth2'\n ? credential.accessToken\n : credential?.type === 'api_key'\n ? credential.apiKey\n : undefined;\n if (typeof token !== 'string' || !token.trim()) {\n throw new GitLabApiError('GitLab connection did not provide a repository credential.', 502);\n }\n return token;\n },\n };\n }\n}\n\nfunction optionalPositiveIntegerEnv(name: 'MASTRA_PLATFORM_GITLAB_POLLING_INTERVAL_MS'): number | undefined {\n const value = process.env[name]?.trim();\n if (!value) return undefined;\n const parsed = Number(value);\n if (!/^[1-9]\\d*$/.test(value) || !Number.isSafeInteger(parsed)) {\n throw new Error(`${name} must be a positive integer.`);\n }\n return parsed;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PlatformApiClient, platformApiClientConfigFromEnv } from "../api-client.js";
|
|
2
|
+
import { IssueReconcileWorker } from "../../issue-reconcile-worker.js";
|
|
2
3
|
import { IncidentioApiClient, IncidentioApiError } from "../../incidentio/api.js";
|
|
3
4
|
import { buildIncidentioRoutes } from "../../incidentio/routes.js";
|
|
4
5
|
import { buildIncidentioAgentTools } from "../../incidentio/agent-tools.js";
|
|
@@ -7,7 +8,6 @@ import { INCIDENTIO_FOLLOW_UPS_SOURCE_ID, INCIDENTIO_INCIDENTS_SOURCE_ID, create
|
|
|
7
8
|
import { attachIncidentioRules } from "../../incidentio/rules.js";
|
|
8
9
|
import { attachIncidentioIssueReconciler } from "../../incidentio/issue-reconciler.js";
|
|
9
10
|
import { incidentioReconciliationEnabled, incidentioReconciliationInterval } from "../../incidentio/reconciliation-config.js";
|
|
10
|
-
import { IssueReconcileWorker } from "../../issue-reconcile-worker.js";
|
|
11
11
|
//#region src/integrations/platform/incidentio/integration.ts
|
|
12
12
|
/**
|
|
13
13
|
* Provider config key registered for incident.io in Mastra Platform's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACzF,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAMhG;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,yBAAyB,CAAC;CAC5C;AA8BD,qBAAa,gBAAiB,YAAW,kBAAkB;;IACzD,QAAQ,CAAC,EAAE,WAAW;IACtB;;;;OAIG;IACH,QAAQ,CAAC,yBAAyB,QAAQ;gBAU9B,MAAM,EAAE,sBAAsB;IAS1C,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACzF,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAMhG;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,yBAAyB,CAAC;CAC5C;AA8BD,qBAAa,gBAAiB,YAAW,kBAAkB;;IACzD,QAAQ,CAAC,EAAE,WAAW;IACtB;;;;OAIG;IACH,QAAQ,CAAC,yBAAyB,QAAQ;gBAU9B,MAAM,EAAE,sBAAsB;IAS1C,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IA2BxD,aAAa,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IAI7D,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAc3C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CASvC"}
|
|
@@ -41,8 +41,8 @@ var SlackIntegration = class {
|
|
|
41
41
|
this.#config = config;
|
|
42
42
|
}
|
|
43
43
|
channels(ctx) {
|
|
44
|
-
const
|
|
45
|
-
this.#repoBackedSessions =
|
|
44
|
+
const sourceControls = ctx.storage.sourceControls ?? (ctx.storage.sourceControlOwner ? [ctx.storage.sourceControlOwner] : []);
|
|
45
|
+
this.#repoBackedSessions = sourceControls.length > 0;
|
|
46
46
|
return createSlackChannelsConfig({
|
|
47
47
|
slack: {
|
|
48
48
|
clientId: this.#config.clientId,
|
|
@@ -52,7 +52,7 @@ var SlackIntegration = class {
|
|
|
52
52
|
},
|
|
53
53
|
accountLinks: ctx.storage.channelIdentity,
|
|
54
54
|
projects: ctx.storage.projects,
|
|
55
|
-
|
|
55
|
+
sourceControls,
|
|
56
56
|
memorySettings: ctx.storage.memorySettings,
|
|
57
57
|
workItems: ctx.runtime?.workItems,
|
|
58
58
|
feed: ctx.feed,
|