@urateam/core 0.1.42 → 0.1.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/dist/__tests__/agent-stream.test.js +40 -0
  2. package/dist/__tests__/agent-stream.test.js.map +1 -1
  3. package/dist/__tests__/bitbucket-webhook.test.d.ts +13 -0
  4. package/dist/__tests__/bitbucket-webhook.test.d.ts.map +1 -0
  5. package/dist/__tests__/bitbucket-webhook.test.js +379 -0
  6. package/dist/__tests__/bitbucket-webhook.test.js.map +1 -0
  7. package/dist/__tests__/bitbucket.test.d.ts +15 -0
  8. package/dist/__tests__/bitbucket.test.d.ts.map +1 -0
  9. package/dist/__tests__/bitbucket.test.js +237 -0
  10. package/dist/__tests__/bitbucket.test.js.map +1 -0
  11. package/dist/__tests__/gitlab-webhook.test.d.ts +13 -0
  12. package/dist/__tests__/gitlab-webhook.test.d.ts.map +1 -0
  13. package/dist/__tests__/gitlab-webhook.test.js +388 -0
  14. package/dist/__tests__/gitlab-webhook.test.js.map +1 -0
  15. package/dist/__tests__/pm-triage.test.js +47 -0
  16. package/dist/__tests__/pm-triage.test.js.map +1 -1
  17. package/dist/__tests__/runner-multi-vcs.test.d.ts +19 -0
  18. package/dist/__tests__/runner-multi-vcs.test.d.ts.map +1 -0
  19. package/dist/__tests__/runner-multi-vcs.test.js +346 -0
  20. package/dist/__tests__/runner-multi-vcs.test.js.map +1 -0
  21. package/dist/__tests__/triage-v2-prediction.test.d.ts +2 -0
  22. package/dist/__tests__/triage-v2-prediction.test.d.ts.map +1 -0
  23. package/dist/__tests__/triage-v2-prediction.test.js +70 -0
  24. package/dist/__tests__/triage-v2-prediction.test.js.map +1 -0
  25. package/dist/__tests__/triage-v2-prompt.test.d.ts +2 -0
  26. package/dist/__tests__/triage-v2-prompt.test.d.ts.map +1 -0
  27. package/dist/__tests__/triage-v2-prompt.test.js +127 -0
  28. package/dist/__tests__/triage-v2-prompt.test.js.map +1 -0
  29. package/dist/__tests__/triage-v2-render.test.d.ts +2 -0
  30. package/dist/__tests__/triage-v2-render.test.d.ts.map +1 -0
  31. package/dist/__tests__/triage-v2-render.test.js +200 -0
  32. package/dist/__tests__/triage-v2-render.test.js.map +1 -0
  33. package/dist/__tests__/triage-v2-schema.test.d.ts +2 -0
  34. package/dist/__tests__/triage-v2-schema.test.d.ts.map +1 -0
  35. package/dist/__tests__/triage-v2-schema.test.js +115 -0
  36. package/dist/__tests__/triage-v2-schema.test.js.map +1 -0
  37. package/dist/executor/agent-stream.d.ts +11 -8
  38. package/dist/executor/agent-stream.d.ts.map +1 -1
  39. package/dist/executor/agent-stream.js +54 -9
  40. package/dist/executor/agent-stream.js.map +1 -1
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/pipeline/feedback-pipeline.d.ts +2 -0
  45. package/dist/pipeline/feedback-pipeline.d.ts.map +1 -1
  46. package/dist/pipeline/feedback-pipeline.js +4 -1
  47. package/dist/pipeline/feedback-pipeline.js.map +1 -1
  48. package/dist/pipeline/runner.d.ts +11 -0
  49. package/dist/pipeline/runner.d.ts.map +1 -1
  50. package/dist/pipeline/runner.js +314 -114
  51. package/dist/pipeline/runner.js.map +1 -1
  52. package/dist/pm/actions/triage-prompt.d.ts +42 -0
  53. package/dist/pm/actions/triage-prompt.d.ts.map +1 -0
  54. package/dist/pm/actions/triage-prompt.js +192 -0
  55. package/dist/pm/actions/triage-prompt.js.map +1 -0
  56. package/dist/pm/actions/triage-render.d.ts +39 -0
  57. package/dist/pm/actions/triage-render.d.ts.map +1 -0
  58. package/dist/pm/actions/triage-render.js +158 -0
  59. package/dist/pm/actions/triage-render.js.map +1 -0
  60. package/dist/pm/actions/triage.d.ts +2 -1
  61. package/dist/pm/actions/triage.d.ts.map +1 -1
  62. package/dist/pm/actions/triage.js +52 -58
  63. package/dist/pm/actions/triage.js.map +1 -1
  64. package/dist/pm/triage-prediction-quality.d.ts +26 -0
  65. package/dist/pm/triage-prediction-quality.d.ts.map +1 -0
  66. package/dist/pm/triage-prediction-quality.js +41 -0
  67. package/dist/pm/triage-prediction-quality.js.map +1 -0
  68. package/dist/pm/types.d.ts +60 -0
  69. package/dist/pm/types.d.ts.map +1 -1
  70. package/dist/pm/types.js +119 -0
  71. package/dist/pm/types.js.map +1 -1
  72. package/dist/repo/bitbucket.d.ts +136 -0
  73. package/dist/repo/bitbucket.d.ts.map +1 -0
  74. package/dist/repo/bitbucket.js +237 -0
  75. package/dist/repo/bitbucket.js.map +1 -0
  76. package/dist/repo/gitlab.d.ts +11 -0
  77. package/dist/repo/gitlab.d.ts.map +1 -1
  78. package/dist/repo/gitlab.js +37 -0
  79. package/dist/repo/gitlab.js.map +1 -1
  80. package/dist/repo/index.d.ts +3 -1
  81. package/dist/repo/index.d.ts.map +1 -1
  82. package/dist/repo/index.js +2 -1
  83. package/dist/repo/index.js.map +1 -1
  84. package/dist/server.d.ts +14 -0
  85. package/dist/server.d.ts.map +1 -1
  86. package/dist/server.js +32 -0
  87. package/dist/server.js.map +1 -1
  88. package/dist/types.d.ts +1 -0
  89. package/dist/types.d.ts.map +1 -1
  90. package/dist/types.js +2 -2
  91. package/dist/types.js.map +1 -1
  92. package/dist/webhook/bitbucket-handler.d.ts +65 -0
  93. package/dist/webhook/bitbucket-handler.d.ts.map +1 -0
  94. package/dist/webhook/bitbucket-handler.js +153 -0
  95. package/dist/webhook/bitbucket-handler.js.map +1 -0
  96. package/dist/webhook/gitlab-handler.d.ts +66 -0
  97. package/dist/webhook/gitlab-handler.d.ts.map +1 -0
  98. package/dist/webhook/gitlab-handler.js +159 -0
  99. package/dist/webhook/gitlab-handler.js.map +1 -0
  100. package/dist/webhook/index.d.ts +3 -0
  101. package/dist/webhook/index.d.ts.map +1 -1
  102. package/dist/webhook/index.js +3 -0
  103. package/dist/webhook/index.js.map +1 -1
  104. package/dist/webhook/shared-handlers.d.ts +110 -0
  105. package/dist/webhook/shared-handlers.d.ts.map +1 -0
  106. package/dist/webhook/shared-handlers.js +251 -0
  107. package/dist/webhook/shared-handlers.js.map +1 -0
  108. package/package.json +1 -1
@@ -0,0 +1,159 @@
1
+ /**
2
+ * GitLab webhook handler.
3
+ *
4
+ * Mirrors `webhook/github-handler.ts` for GitLab MR events:
5
+ * - Signature verification via `X-Gitlab-Token` header (shared secret, not HMAC).
6
+ * - MR comment (Note Hook) events → `review-feedback` pipeline run triggers.
7
+ * - MR merged events → mark pipeline run as merged in DB.
8
+ *
9
+ * ## Setup
10
+ * In your GitLab project → Settings → Webhooks:
11
+ * 1. Set the URL to `https://<your-host>/webhooks/gitlab`.
12
+ * 2. Set a secret token in the "Secret token" field — this is the value you
13
+ * set as `gitlabWebhookToken` in your server config.
14
+ * 3. Enable: "Comments" and "Merge request events".
15
+ *
16
+ * The handler validates the `X-Gitlab-Token` header against `gitlabWebhookToken`.
17
+ *
18
+ * ## Environment variables
19
+ * No handler-specific env vars. Authentication is configured via `ServerConfig.gitlab.token`
20
+ * and `ServerConfig.gitlabWebhookToken` in your server config object.
21
+ */
22
+ import { Hono } from "hono";
23
+ import { createLogger } from "../logger.js";
24
+ import { WebhookDedupSet, buildRepoConfigMap, findPipelineRunByUrlOrBranch, handleMergedEvent, processCommentFeedback, } from "./shared-handlers.js";
25
+ const log = createLogger({ component: "GitLabWebhookHandler" });
26
+ // ---------------------------------------------------------------------------
27
+ // Signature / token verification
28
+ // ---------------------------------------------------------------------------
29
+ /**
30
+ * Verify a GitLab webhook token.
31
+ *
32
+ * GitLab sends the configured secret as a plain string in `X-Gitlab-Token`.
33
+ * This function performs a constant-time comparison to prevent timing attacks.
34
+ *
35
+ * Both inputs are padded to a fixed length (256 bytes) before comparison so
36
+ * the loop always runs for the same number of iterations regardless of the
37
+ * actual token length, preventing timing side-channels.
38
+ */
39
+ export function verifyGitLabToken(receivedToken, expectedToken) {
40
+ if (!receivedToken || !expectedToken)
41
+ return false;
42
+ // Pad both to the same length before comparing to maintain constant time
43
+ const a = Buffer.from(receivedToken.padEnd(256, "\0"));
44
+ const b = Buffer.from(expectedToken.padEnd(256, "\0"));
45
+ if (a.length !== b.length)
46
+ return false;
47
+ let result = 0;
48
+ for (let i = 0; i < a.length; i++) {
49
+ result |= a[i] ^ b[i];
50
+ }
51
+ return result === 0;
52
+ }
53
+ // ---------------------------------------------------------------------------
54
+ // Handler factory
55
+ // ---------------------------------------------------------------------------
56
+ /**
57
+ * Create a Hono app that handles GitLab webhook events at `/webhooks/gitlab`.
58
+ *
59
+ * Mount in your server: `app.route("/", createGitLabWebhookHandler(config))`.
60
+ *
61
+ * Shared DB helpers and comment-filtering logic are provided by
62
+ * `./shared-handlers.ts`. An O(1) Map of repo URL → RepoConfig is built
63
+ * once at initialisation to avoid per-request linear scans.
64
+ */
65
+ export function createGitLabWebhookHandler(config) {
66
+ const app = new Hono();
67
+ const dedup = new WebhookDedupSet();
68
+ // Build O(1) URL → RepoConfig map once at init (avoids per-request O(n) scan)
69
+ const repoConfigsByUrl = buildRepoConfigMap(config.repoConfigs);
70
+ // Periodic cleanup of expired dedup entries
71
+ const cleanupTimer = setInterval(() => dedup.cleanup(), 60_000);
72
+ cleanupTimer.unref();
73
+ app.post("/webhooks/gitlab", async (c) => {
74
+ const rawBody = await c.req.text();
75
+ // 1. Verify token
76
+ if (config.webhookToken) {
77
+ const receivedToken = c.req.header("X-Gitlab-Token") ?? "";
78
+ if (!verifyGitLabToken(receivedToken, config.webhookToken)) {
79
+ return c.json({ error: "Invalid X-Gitlab-Token" }, 401);
80
+ }
81
+ }
82
+ // 2. Parse payload
83
+ let payload;
84
+ try {
85
+ payload = JSON.parse(rawBody);
86
+ }
87
+ catch {
88
+ return c.json({ error: "Invalid JSON" }, 400);
89
+ }
90
+ const objectKind = payload.object_kind ?? "";
91
+ const attrs = payload.object_attributes ?? {};
92
+ // 3a. MR merged event
93
+ if (objectKind === "merge_request" &&
94
+ (attrs.action === "merge" || attrs.state === "merged")) {
95
+ await handleMergedEvent(attrs.url ?? "", attrs.source_branch ?? "", {
96
+ db: config.db,
97
+ notifier: config.notifier,
98
+ mergeReason: "merged via GitLab",
99
+ });
100
+ return c.json({ ok: true, action: "mr-merged" });
101
+ }
102
+ // 3b. Note (comment) on a merge request
103
+ // GitLab sends object_kind="note" with noteable_type="MergeRequest"
104
+ if (objectKind !== "note" || attrs.noteable_type !== "MergeRequest") {
105
+ return c.json({ ok: true, skipped: "unhandled event type" });
106
+ }
107
+ // 4. Extract comment metadata
108
+ const noteId = String(attrs.id ?? "");
109
+ const commentBody = attrs.note ?? "";
110
+ const commentAuthor = payload.user?.username ?? "";
111
+ const commentHtmlUrl = attrs.url ?? "";
112
+ if (!commentBody.trim()) {
113
+ return c.json({ ok: true, skipped: "empty comment body" });
114
+ }
115
+ // 5. Extract MR metadata
116
+ const mr = payload.merge_request ?? {};
117
+ const mrUrl = mr.url ?? "";
118
+ const mrBranch = mr.source_branch ?? "";
119
+ const mrIsDraft = mr.draft === true || mr.work_in_progress === true;
120
+ if (mrIsDraft) {
121
+ return c.json({ ok: true, skipped: "MR is a draft — feedback loop disabled" });
122
+ }
123
+ if (!mrUrl) {
124
+ return c.json({ ok: true, skipped: "no MR URL in payload" });
125
+ }
126
+ // 6. Find the original pipeline run for this MR
127
+ const originalRun = await findPipelineRunByUrlOrBranch(config.db, mrUrl, mrBranch);
128
+ if (!originalRun) {
129
+ return c.json({ ok: true, skipped: "not an agent-created MR" });
130
+ }
131
+ // Resolve branch from DB if not available in payload
132
+ const prBranch = mrBranch || originalRun.branch || "";
133
+ // 7. Resolve repoConfig — O(1) Map lookup (initialised once at handler creation)
134
+ const repoConfig = repoConfigsByUrl.get(originalRun.repoUrl ?? "");
135
+ if (!repoConfig) {
136
+ log.warn({ repoUrl: originalRun.repoUrl }, "no repoConfig found for MR's repo URL");
137
+ return c.json({ ok: true, skipped: "no repo config for this MR" });
138
+ }
139
+ // 8–15. Shared filter/dedup/rate-limit/fire logic
140
+ const result = await processCommentFeedback({
141
+ commentId: noteId,
142
+ commentBody,
143
+ commentAuthor,
144
+ commentHtmlUrl,
145
+ prUrl: mrUrl,
146
+ prBranch,
147
+ prNumber: mr.iid ?? 0,
148
+ originalRun,
149
+ repoConfig,
150
+ }, {
151
+ runner: config.runner,
152
+ pipelineConfigs: config.pipelineConfigs,
153
+ dedup,
154
+ });
155
+ return c.json(result);
156
+ });
157
+ return app;
158
+ }
159
+ //# sourceMappingURL=gitlab-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitlab-handler.js","sourceRoot":"","sources":["../../src/webhook/gitlab-handler.ts"],"names":[],"mappings":"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"}
@@ -2,4 +2,7 @@ export { verifyLinearSignature } from "./signature.js";
2
2
  export { parseStateChange, type ParsedStateChange } from "./parser.js";
3
3
  export { createWebhookHandler, type WebhookHandlerConfig } from "./handler.js";
4
4
  export { createGitHubWebhookHandler, verifyGitHubSignature, type GitHubWebhookHandlerConfig, type ReviewFeedbackComment, } from "./github-handler.js";
5
+ export { createGitLabWebhookHandler, verifyGitLabToken, type GitLabWebhookHandlerConfig, } from "./gitlab-handler.js";
6
+ export { createBitbucketWebhookHandler, verifyBitbucketSignature, type BitbucketWebhookHandlerConfig, } from "./bitbucket-handler.js";
7
+ export { WebhookDedupSet, buildRepoConfigMap, findPipelineRunByUrlOrBranch, updatePipelineRunMerged, handleMergedEvent, processCommentFeedback, type MergedEventHandlerConfig, type CommentFeedbackInput, type CommentFeedbackResult, } from "./shared-handlers.js";
5
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAC/E,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAC/E,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,0BAA0B,EAC1B,iBAAiB,EACjB,KAAK,0BAA0B,GAChC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,EACxB,KAAK,6BAA6B,GACnC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,4BAA4B,EAC5B,uBAAuB,EACvB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC"}
@@ -2,4 +2,7 @@ export { verifyLinearSignature } from "./signature.js";
2
2
  export { parseStateChange } from "./parser.js";
3
3
  export { createWebhookHandler } from "./handler.js";
4
4
  export { createGitHubWebhookHandler, verifyGitHubSignature, } from "./github-handler.js";
5
+ export { createGitLabWebhookHandler, verifyGitLabToken, } from "./gitlab-handler.js";
6
+ export { createBitbucketWebhookHandler, verifyBitbucketSignature, } from "./bitbucket-handler.js";
7
+ export { WebhookDedupSet, buildRepoConfigMap, findPipelineRunByUrlOrBranch, updatePipelineRunMerged, handleMergedEvent, processCommentFeedback, } from "./shared-handlers.js";
5
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAA0B,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAA6B,MAAM,cAAc,CAAC;AAC/E,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,GAGtB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAA0B,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAA6B,MAAM,cAAc,CAAC;AAC/E,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,GAGtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,0BAA0B,EAC1B,iBAAiB,GAElB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,GAEzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,4BAA4B,EAC5B,uBAAuB,EACvB,iBAAiB,EACjB,sBAAsB,GAIvB,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Shared utilities for GitLab and Bitbucket webhook handlers.
3
+ *
4
+ * Extracted to avoid duplication between `gitlab-handler.ts` and
5
+ * `bitbucket-handler.ts`. Both providers follow the same general pattern for:
6
+ * - In-memory comment dedup (TTL-based expiry)
7
+ * - DB lookups for pipeline runs
8
+ * - Generic "PR/MR merged" event handling
9
+ * - Common comment-event filtering and feedback-run triggering
10
+ */
11
+ import { pipelineRuns } from "../db/schema.js";
12
+ import type { AnyDb } from "../db/client.js";
13
+ import type { PipelineRunner } from "../pipeline/runner.js";
14
+ import type { Notifier, PipelineConfig, RepoConfig } from "../types.js";
15
+ /**
16
+ * In-memory dedup set for processed webhook event IDs with TTL-based expiry.
17
+ * Used by GitLab (note IDs) and Bitbucket (comment IDs) webhook handlers to
18
+ * prevent double-processing when webhooks are delivered more than once.
19
+ */
20
+ export declare class WebhookDedupSet {
21
+ private entries;
22
+ has(id: string): boolean;
23
+ add(id: string, ttlMs?: number): void;
24
+ /** Purge entries whose TTL has elapsed. Call periodically (e.g. every minute). */
25
+ cleanup(): void;
26
+ }
27
+ /**
28
+ * Build an O(1) Map<repoUrl, RepoConfig> for fast webhook-time lookups.
29
+ *
30
+ * Call once at handler initialisation (inside `createXxxWebhookHandler()`),
31
+ * store as a closure variable, and replace linear `Object.values(repoConfigs)`
32
+ * scans with `repoConfigsByUrl.get(url)`.
33
+ */
34
+ export declare function buildRepoConfigMap(repoConfigs: Record<string, RepoConfig>): Map<string, RepoConfig>;
35
+ /**
36
+ * Look up a pipeline run by PR/MR URL (primary) then by branch name (fallback).
37
+ * Shared by GitLab and Bitbucket webhook handlers.
38
+ */
39
+ export declare function findPipelineRunByUrlOrBranch(db: AnyDb, url?: string, branch?: string): Promise<(typeof pipelineRuns.$inferSelect) | undefined>;
40
+ /**
41
+ * Mark a pipeline run as externally merged.
42
+ * Shared by GitLab and Bitbucket webhook handlers.
43
+ */
44
+ export declare function updatePipelineRunMerged(db: AnyDb, runId: string, merged: boolean | null, reason: string): Promise<void>;
45
+ export interface MergedEventHandlerConfig {
46
+ db: AnyDb;
47
+ notifier?: Notifier;
48
+ /** Human-readable reason stored in autoMergeReason, e.g. "merged via GitLab". */
49
+ mergeReason: string;
50
+ }
51
+ /**
52
+ * Generic handler for PR/MR merged events.
53
+ *
54
+ * Marks the pipeline run as merged in the DB and calls `notifier.onPRMerged`.
55
+ * The caller is responsible for extracting `url` and `branch` from the
56
+ * provider-specific webhook payload before calling this function.
57
+ *
58
+ * @param url The PR/MR web URL (e.g. https://gitlab.com/org/repo/-/merge_requests/7)
59
+ * @param branch The source branch of the PR/MR
60
+ * @param config DB, notifier, and merge-reason string
61
+ */
62
+ export declare function handleMergedEvent(url: string, branch: string, config: MergedEventHandlerConfig): Promise<void>;
63
+ export interface CommentFeedbackInput {
64
+ /** Dedup key for this comment (note ID on GitLab, comment ID on Bitbucket). */
65
+ commentId: string;
66
+ /** Raw comment body text (before sanitisation). */
67
+ commentBody: string;
68
+ /** Author login / username / nickname. */
69
+ commentAuthor: string;
70
+ /** Permalink URL to the comment. */
71
+ commentHtmlUrl: string;
72
+ /** Web URL of the PR or MR. */
73
+ prUrl: string;
74
+ /** Source branch of the PR or MR. */
75
+ prBranch: string;
76
+ /** Numeric PR/MR ID used for startFeedback (iid on GitLab, id on Bitbucket). */
77
+ prNumber: number;
78
+ /** Already-resolved pipeline run for this PR/MR (from findPipelineRunByUrlOrBranch). */
79
+ originalRun: typeof pipelineRuns.$inferSelect;
80
+ /** Already-resolved RepoConfig for this run's repo URL. */
81
+ repoConfig: RepoConfig;
82
+ }
83
+ export interface CommentFeedbackResult {
84
+ ok: boolean;
85
+ action?: string;
86
+ skipped?: string;
87
+ deduplicated?: boolean;
88
+ }
89
+ /**
90
+ * Apply all filters and trigger a review-feedback pipeline run for a PR/MR
91
+ * comment event. Returns a JSON-serializable result for the HTTP response.
92
+ *
93
+ * Shared by GitLab and Bitbucket webhook handlers. The caller is responsible
94
+ * for auth/signature verification, payload parsing, field extraction, and
95
+ * resolving `originalRun` + `repoConfig` before calling this function.
96
+ *
97
+ * Filters applied (in order):
98
+ * 1. Bot exclusion (`feedbackCfg.botLogins`)
99
+ * 2. Allowed-reviewer list (`feedbackCfg.allowedReviewers`)
100
+ * 3. Trigger-keyword check (`feedbackCfg.triggerKeyword`)
101
+ * 4. In-memory dedup (24h TTL via `WebhookDedupSet`)
102
+ * 5. Rate-limit (one active feedback run per PR/MR URL)
103
+ * 6. Pipeline config lookup
104
+ */
105
+ export declare function processCommentFeedback(input: CommentFeedbackInput, handlerConfig: {
106
+ runner: PipelineRunner;
107
+ pipelineConfigs: Record<string, PipelineConfig>;
108
+ dedup: WebhookDedupSet;
109
+ }): Promise<CommentFeedbackResult>;
110
+ //# sourceMappingURL=shared-handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-handlers.d.ts","sourceRoot":"","sources":["../../src/webhook/shared-handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAe,UAAU,EAAE,MAAM,aAAa,CAAC;AAUrF;;;;GAIG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAA6B;IAE5C,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAUxB,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,SAAa,GAAc,IAAI;IAIpD,kFAAkF;IAClF,OAAO,IAAI,IAAI;CAMhB;AAMD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GACtC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAMzB;AAMD;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,KAAK,EACT,GAAG,CAAC,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,OAAO,YAAY,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC,CAkBzD;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,KAAK,EACT,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,GAAG,IAAI,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAKf;AAMD,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,KAAK,CAAC;IACV,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAoCf;AAMD,MAAM,WAAW,oBAAoB;IACnC,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,wFAAwF;IACxF,WAAW,EAAE,OAAO,YAAY,CAAC,YAAY,CAAC;IAC9C,2DAA2D;IAC3D,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,oBAAoB,EAC3B,aAAa,EAAE;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAChD,KAAK,EAAE,eAAe,CAAC;CACxB,GACA,OAAO,CAAC,qBAAqB,CAAC,CA0HhC"}
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Shared utilities for GitLab and Bitbucket webhook handlers.
3
+ *
4
+ * Extracted to avoid duplication between `gitlab-handler.ts` and
5
+ * `bitbucket-handler.ts`. Both providers follow the same general pattern for:
6
+ * - In-memory comment dedup (TTL-based expiry)
7
+ * - DB lookups for pipeline runs
8
+ * - Generic "PR/MR merged" event handling
9
+ * - Common comment-event filtering and feedback-run triggering
10
+ */
11
+ import { eq } from "drizzle-orm";
12
+ import { pipelineRuns } from "../db/schema.js";
13
+ import { createLogger } from "../logger.js";
14
+ const log = createLogger({ component: "WebhookSharedHandlers" });
15
+ // ---------------------------------------------------------------------------
16
+ // WebhookDedupSet — in-memory dedup for processed event IDs (24h TTL)
17
+ // ---------------------------------------------------------------------------
18
+ /**
19
+ * In-memory dedup set for processed webhook event IDs with TTL-based expiry.
20
+ * Used by GitLab (note IDs) and Bitbucket (comment IDs) webhook handlers to
21
+ * prevent double-processing when webhooks are delivered more than once.
22
+ */
23
+ export class WebhookDedupSet {
24
+ entries = new Map(); // id -> expiry ms
25
+ has(id) {
26
+ const expiry = this.entries.get(id);
27
+ if (expiry === undefined)
28
+ return false;
29
+ if (Date.now() > expiry) {
30
+ this.entries.delete(id);
31
+ return false;
32
+ }
33
+ return true;
34
+ }
35
+ add(id, ttlMs = 86_400_000 /* 24 h */) {
36
+ this.entries.set(id, Date.now() + ttlMs);
37
+ }
38
+ /** Purge entries whose TTL has elapsed. Call periodically (e.g. every minute). */
39
+ cleanup() {
40
+ const now = Date.now();
41
+ for (const [id, expiry] of this.entries) {
42
+ if (now > expiry)
43
+ this.entries.delete(id);
44
+ }
45
+ }
46
+ }
47
+ // ---------------------------------------------------------------------------
48
+ // buildRepoConfigMap — O(1) URL → RepoConfig lookup
49
+ // ---------------------------------------------------------------------------
50
+ /**
51
+ * Build an O(1) Map<repoUrl, RepoConfig> for fast webhook-time lookups.
52
+ *
53
+ * Call once at handler initialisation (inside `createXxxWebhookHandler()`),
54
+ * store as a closure variable, and replace linear `Object.values(repoConfigs)`
55
+ * scans with `repoConfigsByUrl.get(url)`.
56
+ */
57
+ export function buildRepoConfigMap(repoConfigs) {
58
+ const map = new Map();
59
+ for (const rc of Object.values(repoConfigs)) {
60
+ if (rc.url)
61
+ map.set(rc.url, rc);
62
+ }
63
+ return map;
64
+ }
65
+ // ---------------------------------------------------------------------------
66
+ // DB helpers
67
+ // ---------------------------------------------------------------------------
68
+ /**
69
+ * Look up a pipeline run by PR/MR URL (primary) then by branch name (fallback).
70
+ * Shared by GitLab and Bitbucket webhook handlers.
71
+ */
72
+ export async function findPipelineRunByUrlOrBranch(db, url, branch) {
73
+ if (url) {
74
+ const rows = await db
75
+ .select()
76
+ .from(pipelineRuns)
77
+ .where(eq(pipelineRuns.prUrl, url))
78
+ .limit(1);
79
+ if (rows.length > 0)
80
+ return rows[0];
81
+ }
82
+ if (branch?.startsWith("agent/")) {
83
+ const rows = await db
84
+ .select()
85
+ .from(pipelineRuns)
86
+ .where(eq(pipelineRuns.branch, branch))
87
+ .limit(1);
88
+ if (rows.length > 0)
89
+ return rows[0];
90
+ }
91
+ return undefined;
92
+ }
93
+ /**
94
+ * Mark a pipeline run as externally merged.
95
+ * Shared by GitLab and Bitbucket webhook handlers.
96
+ */
97
+ export async function updatePipelineRunMerged(db, runId, merged, reason) {
98
+ await db
99
+ .update(pipelineRuns)
100
+ .set({ autoMerged: merged, autoMergeReason: reason })
101
+ .where(eq(pipelineRuns.id, runId));
102
+ }
103
+ /**
104
+ * Generic handler for PR/MR merged events.
105
+ *
106
+ * Marks the pipeline run as merged in the DB and calls `notifier.onPRMerged`.
107
+ * The caller is responsible for extracting `url` and `branch` from the
108
+ * provider-specific webhook payload before calling this function.
109
+ *
110
+ * @param url The PR/MR web URL (e.g. https://gitlab.com/org/repo/-/merge_requests/7)
111
+ * @param branch The source branch of the PR/MR
112
+ * @param config DB, notifier, and merge-reason string
113
+ */
114
+ export async function handleMergedEvent(url, branch, config) {
115
+ if (!url) {
116
+ log.warn("merged-event: no URL extracted from payload — skipping");
117
+ return;
118
+ }
119
+ const originalRun = await findPipelineRunByUrlOrBranch(config.db, url, branch);
120
+ if (!originalRun) {
121
+ log.debug({ url }, "merged-event: no pipeline run found — skipping");
122
+ return;
123
+ }
124
+ if (originalRun.autoMerged) {
125
+ log.debug({ runId: originalRun.id }, "merged-event: run already marked merged — skipping");
126
+ return;
127
+ }
128
+ await updatePipelineRunMerged(config.db, originalRun.id, true, config.mergeReason);
129
+ log.info({ runId: originalRun.id, url }, "merged-event: updated pipeline run auto_merged=true");
130
+ if (config.notifier?.onPRMerged) {
131
+ await config.notifier
132
+ .onPRMerged(originalRun)
133
+ .catch((err) => log.error({ err, runId: originalRun.id }, "merged-event: notifier.onPRMerged() failed"));
134
+ }
135
+ }
136
+ /**
137
+ * Apply all filters and trigger a review-feedback pipeline run for a PR/MR
138
+ * comment event. Returns a JSON-serializable result for the HTTP response.
139
+ *
140
+ * Shared by GitLab and Bitbucket webhook handlers. The caller is responsible
141
+ * for auth/signature verification, payload parsing, field extraction, and
142
+ * resolving `originalRun` + `repoConfig` before calling this function.
143
+ *
144
+ * Filters applied (in order):
145
+ * 1. Bot exclusion (`feedbackCfg.botLogins`)
146
+ * 2. Allowed-reviewer list (`feedbackCfg.allowedReviewers`)
147
+ * 3. Trigger-keyword check (`feedbackCfg.triggerKeyword`)
148
+ * 4. In-memory dedup (24h TTL via `WebhookDedupSet`)
149
+ * 5. Rate-limit (one active feedback run per PR/MR URL)
150
+ * 6. Pipeline config lookup
151
+ */
152
+ export async function processCommentFeedback(input, handlerConfig) {
153
+ const { commentId, commentBody, commentAuthor, commentHtmlUrl, prUrl, prBranch, prNumber, originalRun, repoConfig, } = input;
154
+ const { runner, pipelineConfigs, dedup } = handlerConfig;
155
+ // Feedback config — `githubFeedback` field applies to all providers
156
+ // (named for historical reasons; used by GitHub, GitLab, and Bitbucket)
157
+ const feedbackCfg = repoConfig.githubFeedback;
158
+ // 1. Bot exclusion
159
+ const botLogins = feedbackCfg?.botLogins ?? [];
160
+ if (botLogins.length > 0 && botLogins.includes(commentAuthor)) {
161
+ return { ok: true, skipped: "comment from bot login" };
162
+ }
163
+ // 2. Allowed-reviewer filter
164
+ const allowedReviewers = feedbackCfg?.allowedReviewers ?? [];
165
+ if (allowedReviewers.length > 0 && !allowedReviewers.includes(commentAuthor)) {
166
+ return { ok: true, skipped: "commenter not in allowedReviewers" };
167
+ }
168
+ // 3. Trigger-keyword check
169
+ const triggerKeyword = feedbackCfg?.triggerKeyword;
170
+ const autoTrigger = feedbackCfg?.autoTrigger !== false;
171
+ if (triggerKeyword) {
172
+ if (!commentBody.includes(triggerKeyword)) {
173
+ return { ok: true, skipped: "trigger keyword not found" };
174
+ }
175
+ }
176
+ else if (!autoTrigger) {
177
+ return { ok: true, skipped: "autoTrigger is disabled and no triggerKeyword configured" };
178
+ }
179
+ // 4. Dedup
180
+ if (dedup.has(commentId)) {
181
+ return { ok: true, deduplicated: true };
182
+ }
183
+ // 5. Rate-limit: one active feedback run per PR/MR URL
184
+ if (runner.isActiveFeedback(prUrl)) {
185
+ log.info({ prUrl }, "feedback run already in progress for this PR/MR — skipping");
186
+ return { ok: true, skipped: "feedback run already in progress" };
187
+ }
188
+ // 6. Pipeline config lookup
189
+ const pipelineKey = originalRun.pipelineKey;
190
+ const pipelineConfig = pipelineConfigs[pipelineKey];
191
+ if (!pipelineConfig) {
192
+ log.warn({ pipelineKey }, "no pipeline config found for original run's pipelineKey");
193
+ return { ok: true, skipped: "pipeline config not found" };
194
+ }
195
+ // Commit dedup entry now that we're going to trigger a run
196
+ dedup.add(commentId);
197
+ // Sanitise comment body before passing to agent pipeline
198
+ const MAX_COMMENT_LENGTH = 4000;
199
+ const sanitizedBody = commentBody
200
+ .slice(0, MAX_COMMENT_LENGTH)
201
+ .replace(/<\/review-comment>/gi, "[/review-comment]");
202
+ const feedbackComment = {
203
+ commentId,
204
+ author: commentAuthor,
205
+ body: sanitizedBody,
206
+ htmlUrl: commentHtmlUrl,
207
+ };
208
+ // Build minimal issue object from DB row
209
+ const issue = {
210
+ id: originalRun.issueId,
211
+ identifier: originalRun.issueId,
212
+ title: originalRun.issueTitle,
213
+ description: "",
214
+ labels: [],
215
+ priority: 0,
216
+ teamId: "",
217
+ };
218
+ // Resolve branch: caller may have pre-resolved via `|| originalRun.branch`,
219
+ // but we apply the same fallback here for safety.
220
+ const resolvedBranch = prBranch || originalRun.branch || "";
221
+ const branchSlug = resolvedBranch.startsWith("agent/")
222
+ ? resolvedBranch.slice("agent/".length)
223
+ : (originalRun.issueId ?? "feedback");
224
+ const sanitizedIssue = {
225
+ id: originalRun.issueId,
226
+ slug: branchSlug,
227
+ title: originalRun.issueTitle,
228
+ description: "",
229
+ acceptanceCriteria: [],
230
+ labels: [],
231
+ priority: 0,
232
+ };
233
+ // Fire-and-forget feedback run
234
+ runner
235
+ .startFeedback({
236
+ issue,
237
+ pipelineKey,
238
+ pipelineConfig,
239
+ repoConfig,
240
+ sanitizedIssue,
241
+ branch: resolvedBranch,
242
+ prUrl,
243
+ prNumber,
244
+ parentRunId: originalRun.id,
245
+ feedbackComments: [feedbackComment],
246
+ rerequestReview: false, // GitLab/Bitbucket don't have GitHub's re-request concept
247
+ })
248
+ .catch((err) => log.error({ err }, "runner.startFeedback() failed"));
249
+ return { ok: true, action: "feedback-triggered" };
250
+ }
251
+ //# sourceMappingURL=shared-handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-handlers.js","sourceRoot":"","sources":["../../src/webhook/shared-handlers.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@urateam/core",
3
- "version": "0.1.42",
3
+ "version": "0.1.44",
4
4
  "license": "BUSL-1.1",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",