openclaw-channel-github 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +578 -0
  2. package/config.example.json +33 -0
  3. package/dist/index.d.ts +14 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +44 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/src/auth/auth.d.ts +22 -0
  8. package/dist/src/auth/auth.d.ts.map +1 -0
  9. package/dist/src/auth/auth.js +112 -0
  10. package/dist/src/auth/auth.js.map +1 -0
  11. package/dist/src/config/config.d.ts +60 -0
  12. package/dist/src/config/config.d.ts.map +1 -0
  13. package/dist/src/config/config.js +184 -0
  14. package/dist/src/config/config.js.map +1 -0
  15. package/dist/src/events/events.d.ts +234 -0
  16. package/dist/src/events/events.d.ts.map +1 -0
  17. package/dist/src/events/events.js +53 -0
  18. package/dist/src/events/events.js.map +1 -0
  19. package/dist/src/main.d.ts +2 -0
  20. package/dist/src/main.d.ts.map +1 -0
  21. package/dist/src/main.js +115 -0
  22. package/dist/src/main.js.map +1 -0
  23. package/dist/src/normalizer/normalizer.d.ts +94 -0
  24. package/dist/src/normalizer/normalizer.d.ts.map +1 -0
  25. package/dist/src/normalizer/normalizer.js +486 -0
  26. package/dist/src/normalizer/normalizer.js.map +1 -0
  27. package/dist/src/outbound/outbound.d.ts +24 -0
  28. package/dist/src/outbound/outbound.d.ts.map +1 -0
  29. package/dist/src/outbound/outbound.js +125 -0
  30. package/dist/src/outbound/outbound.js.map +1 -0
  31. package/dist/src/routing/routing.d.ts +22 -0
  32. package/dist/src/routing/routing.d.ts.map +1 -0
  33. package/dist/src/routing/routing.js +97 -0
  34. package/dist/src/routing/routing.js.map +1 -0
  35. package/dist/src/server/server.d.ts +20 -0
  36. package/dist/src/server/server.d.ts.map +1 -0
  37. package/dist/src/server/server.js +283 -0
  38. package/dist/src/server/server.js.map +1 -0
  39. package/dist/src/state/state.d.ts +17 -0
  40. package/dist/src/state/state.d.ts.map +1 -0
  41. package/dist/src/state/state.js +49 -0
  42. package/dist/src/state/state.js.map +1 -0
  43. package/docs/api.md +450 -0
  44. package/docs/usage.md +393 -0
  45. package/openclaw.plugin.json +104 -0
  46. package/package.json +81 -0
@@ -0,0 +1,22 @@
1
+ import type { TriggerConfig, AutoTriggerConfig } from '../config/config';
2
+ import { type NormalizedEvent, type TriggerKindValue } from '../normalizer/normalizer';
3
+ /**
4
+ * Generate a stable session key for a normalized event.
5
+ * Format: github:<owner>/<repo>:<threadType>:<number>
6
+ * For review threads: github:<owner>/<repo>:pull_request:<number>:review-thread:<threadId>
7
+ */
8
+ export declare function sessionKey(event: NormalizedEvent): string;
9
+ export interface TriggerResult {
10
+ triggered: boolean;
11
+ kind: TriggerKindValue;
12
+ command?: string;
13
+ }
14
+ /** Evaluate whether a normalized event matches the trigger configuration. */
15
+ export declare function evaluateTrigger(cfg: TriggerConfig, event: NormalizedEvent): TriggerResult;
16
+ /** Check if an event qualifies for auto-trigger based on config. */
17
+ export declare function evaluateAutoTrigger(cfg: AutoTriggerConfig | undefined, event: NormalizedEvent): TriggerResult;
18
+ /** Check if sender is a bot that should be ignored. */
19
+ export declare function isBotSender(event: NormalizedEvent, botUsername: string, ignoreBots: boolean): boolean;
20
+ /** Check if the event message contains the outbound marker. */
21
+ export declare function hasOutboundMarker(event: NormalizedEvent, marker: string): boolean;
22
+ //# sourceMappingURL=routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../../src/routing/routing.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EAItB,MAAM,0BAA0B,CAAC;AAElC;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAMzD;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,eAAe,GAAG,aAAa,CAmCzF;AAED,oEAAoE;AACpE,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,iBAAiB,GAAG,SAAS,EAClC,KAAK,EAAE,eAAe,GACrB,aAAa,CAmBf;AAED,uDAAuD;AACvD,wBAAgB,WAAW,CACzB,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,OAAO,GAClB,OAAO,CAIT;AAED,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGjF"}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ // Package routing provides session key generation and trigger matching.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.sessionKey = sessionKey;
5
+ exports.evaluateTrigger = evaluateTrigger;
6
+ exports.evaluateAutoTrigger = evaluateAutoTrigger;
7
+ exports.isBotSender = isBotSender;
8
+ exports.hasOutboundMarker = hasOutboundMarker;
9
+ const normalizer_1 = require("../normalizer/normalizer");
10
+ /**
11
+ * Generate a stable session key for a normalized event.
12
+ * Format: github:<owner>/<repo>:<threadType>:<number>
13
+ * For review threads: github:<owner>/<repo>:pull_request:<number>:review-thread:<threadId>
14
+ */
15
+ function sessionKey(event) {
16
+ const base = `github:${event.repository}:${event.thread.type}:${event.thread.number}`;
17
+ if (event.context?.reviewThreadId) {
18
+ return `${base}:review-thread:${event.context.reviewThreadId}`;
19
+ }
20
+ return base;
21
+ }
22
+ /** Evaluate whether a normalized event matches the trigger configuration. */
23
+ function evaluateTrigger(cfg, event) {
24
+ const text = event.message.text;
25
+ // Check @mention
26
+ if (cfg.requireMention && cfg.botUsername) {
27
+ const mention = '@' + cfg.botUsername;
28
+ if (containsIgnoreCase(text, mention)) {
29
+ return { triggered: true, kind: normalizer_1.TriggerKind.Mention };
30
+ }
31
+ }
32
+ // Check command prefixes
33
+ for (const cmd of cfg.commands ?? []) {
34
+ if (hasCommandPrefix(text, cmd)) {
35
+ return { triggered: true, kind: normalizer_1.TriggerKind.Command, command: cmd };
36
+ }
37
+ }
38
+ // Check label triggers from context
39
+ if ((cfg.labels?.length ?? 0) > 0 && event.context) {
40
+ for (const cfgLabel of cfg.labels) {
41
+ for (const evtLabel of event.context.labels ?? []) {
42
+ if (cfgLabel.toLowerCase() === evtLabel.toLowerCase()) {
43
+ return { triggered: true, kind: normalizer_1.TriggerKind.Label };
44
+ }
45
+ }
46
+ }
47
+ }
48
+ // If no trigger requirements set, treat as auto
49
+ if (!cfg.requireMention && (!cfg.commands || cfg.commands.length === 0) && (!cfg.labels || cfg.labels.length === 0)) {
50
+ return { triggered: true, kind: normalizer_1.TriggerKind.Auto };
51
+ }
52
+ return { triggered: false, kind: normalizer_1.TriggerKind.None };
53
+ }
54
+ /** Check if an event qualifies for auto-trigger based on config. */
55
+ function evaluateAutoTrigger(cfg, event) {
56
+ if (!cfg)
57
+ return { triggered: false, kind: normalizer_1.TriggerKind.None };
58
+ if (cfg.onPROpened &&
59
+ event.thread.type === normalizer_1.ThreadType.PullRequest &&
60
+ event.message.type === normalizer_1.MessageType.PRBody) {
61
+ return { triggered: true, kind: normalizer_1.TriggerKind.Auto };
62
+ }
63
+ if (cfg.onIssueOpened &&
64
+ event.thread.type === normalizer_1.ThreadType.Issue &&
65
+ event.message.type === normalizer_1.MessageType.IssueBody) {
66
+ return { triggered: true, kind: normalizer_1.TriggerKind.Auto };
67
+ }
68
+ return { triggered: false, kind: normalizer_1.TriggerKind.None };
69
+ }
70
+ /** Check if sender is a bot that should be ignored. */
71
+ function isBotSender(event, botUsername, ignoreBots) {
72
+ if (ignoreBots && event.sender.isBot)
73
+ return true;
74
+ if (botUsername && event.sender.login.toLowerCase() === botUsername.toLowerCase())
75
+ return true;
76
+ return false;
77
+ }
78
+ /** Check if the event message contains the outbound marker. */
79
+ function hasOutboundMarker(event, marker) {
80
+ if (!marker)
81
+ return false;
82
+ return event.message.text.includes(marker);
83
+ }
84
+ // --- Helpers ---
85
+ function containsIgnoreCase(s, substr) {
86
+ return s.toLowerCase().includes(substr.toLowerCase());
87
+ }
88
+ function hasCommandPrefix(text, cmd) {
89
+ const trimmed = text.trim().toLowerCase();
90
+ const cmdLower = cmd.toLowerCase();
91
+ if (trimmed.startsWith(cmdLower)) {
92
+ const rest = trimmed.slice(cmdLower.length);
93
+ return rest === '' || rest[0] === ' ' || rest[0] === '\n' || rest[0] === '\t';
94
+ }
95
+ return false;
96
+ }
97
+ //# sourceMappingURL=routing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.js","sourceRoot":"","sources":["../../../src/routing/routing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import * as http from 'http';
2
+ import type { Config } from '../config/config';
3
+ import { GitHubAuth } from '../auth/auth';
4
+ import { type NormalizedEvent } from '../normalizer/normalizer';
5
+ import { OutboundSender } from '../outbound/outbound';
6
+ import { Store } from '../state/state';
7
+ export type MessageHandler = (sessionKey: string, event: NormalizedEvent) => Promise<string> | string;
8
+ export declare class WebhookHandler {
9
+ private cfg;
10
+ private ghAuth;
11
+ private store;
12
+ private sender;
13
+ messageHandler: MessageHandler | null;
14
+ constructor(cfg: Config, ghAuth: GitHubAuth | null, store: Store, sender: OutboundSender);
15
+ /** Handle an incoming HTTP request. */
16
+ handleRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void>;
17
+ }
18
+ /** Create an HTTP server with /webhook and /health endpoints. */
19
+ export declare function createServer(handler: WebhookHandler): http.Server;
20
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/server/server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,UAAU,EAAiF,MAAM,cAAc,CAAC;AAczH,OAAO,EACL,KAAK,eAAe,EAoBrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAQtD,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAIvC,MAAM,MAAM,cAAc,GAAG,CAC3B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,eAAe,KACnB,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;AAE9B,qBAAa,cAAc;IACzB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,MAAM,CAAiB;IACxB,cAAc,EAAE,cAAc,GAAG,IAAI,CAAQ;gBAGlD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,UAAU,GAAG,IAAI,EACzB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,cAAc;IAQxB,uCAAuC;IACjC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CA0HxF;AAiGD,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC,MAAM,CAYjE"}
@@ -0,0 +1,283 @@
1
+ "use strict";
2
+ // Package server provides the HTTP webhook receiver for the OpenClaw GitHub Channel.
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.WebhookHandler = void 0;
38
+ exports.createServer = createServer;
39
+ const http = __importStar(require("http"));
40
+ const config_1 = require("../config/config");
41
+ const auth_1 = require("../auth/auth");
42
+ const events_1 = require("../events/events");
43
+ const normalizer_1 = require("../normalizer/normalizer");
44
+ const routing_1 = require("../routing/routing");
45
+ const MAX_PAYLOAD_SIZE = 10 * 1024 * 1024; // 10MB
46
+ class WebhookHandler {
47
+ cfg;
48
+ ghAuth;
49
+ store;
50
+ sender;
51
+ messageHandler = null;
52
+ constructor(cfg, ghAuth, store, sender) {
53
+ this.cfg = cfg;
54
+ this.ghAuth = ghAuth;
55
+ this.store = store;
56
+ this.sender = sender;
57
+ }
58
+ /** Handle an incoming HTTP request. */
59
+ async handleRequest(req, res) {
60
+ if (req.method !== 'POST') {
61
+ res.writeHead(405, { 'Content-Type': 'text/plain' });
62
+ res.end('Method not allowed');
63
+ return;
64
+ }
65
+ // Read body with size limit
66
+ let body;
67
+ try {
68
+ body = await readBody(req, MAX_PAYLOAD_SIZE);
69
+ }
70
+ catch {
71
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
72
+ res.end('Failed to read body');
73
+ return;
74
+ }
75
+ // Verify webhook signature
76
+ const headers = req.headers;
77
+ const signature = (0, auth_1.extractSignature)(headers);
78
+ if (!(0, auth_1.verifyWebhookSignature)(body, this.cfg.channel.webhookSecret, signature)) {
79
+ res.writeHead(401, { 'Content-Type': 'text/plain' });
80
+ res.end('Invalid signature');
81
+ return;
82
+ }
83
+ const deliveryID = (0, auth_1.extractDeliveryID)(headers);
84
+ const eventType = (0, auth_1.extractEventType)(headers);
85
+ // Check for duplicate delivery
86
+ if (this.store.isDuplicate(deliveryID)) {
87
+ respondJSON(res, 200, { status: 'duplicate' });
88
+ return;
89
+ }
90
+ // Parse the event
91
+ let parsed;
92
+ try {
93
+ parsed = (0, events_1.parseEvent)(eventType, body);
94
+ }
95
+ catch {
96
+ respondJSON(res, 200, { status: 'unsupported_event' });
97
+ return;
98
+ }
99
+ // Normalize the event
100
+ const normalized = normalizeEvent(eventType, parsed);
101
+ if (!normalized) {
102
+ respondJSON(res, 200, { status: 'skipped' });
103
+ return;
104
+ }
105
+ // Check repository allowlist
106
+ if (!(0, config_1.isRepoAllowed)(this.cfg, normalized.repository)) {
107
+ respondJSON(res, 200, { status: 'repo_not_allowed' });
108
+ return;
109
+ }
110
+ // Bot loop prevention
111
+ if ((0, routing_1.isBotSender)(normalized, this.cfg.channel.trigger.botUsername, this.cfg.channel.ignoreBots)) {
112
+ respondJSON(res, 200, { status: 'bot_ignored' });
113
+ return;
114
+ }
115
+ // Outbound marker detection
116
+ if ((0, routing_1.hasOutboundMarker)(normalized, this.cfg.channel.outbound?.outboundMarker ?? '')) {
117
+ respondJSON(res, 200, { status: 'outbound_marker_ignored' });
118
+ return;
119
+ }
120
+ // Evaluate trigger
121
+ let triggerResult = (0, routing_1.evaluateTrigger)(this.cfg.channel.trigger, normalized);
122
+ if (!triggerResult.triggered) {
123
+ triggerResult = (0, routing_1.evaluateAutoTrigger)(this.cfg.channel.autoTrigger, normalized);
124
+ }
125
+ if (!triggerResult.triggered) {
126
+ respondJSON(res, 200, { status: 'no_trigger' });
127
+ return;
128
+ }
129
+ // Set trigger info
130
+ normalized.trigger = {
131
+ kind: triggerResult.kind,
132
+ command: triggerResult.command,
133
+ };
134
+ // Generate session key
135
+ const sessKey = (0, routing_1.sessionKey)(normalized);
136
+ // Mark delivery as processed
137
+ this.store.markProcessed(deliveryID);
138
+ // Invoke message handler
139
+ if (this.messageHandler) {
140
+ let reply;
141
+ try {
142
+ reply = await this.messageHandler(sessKey, normalized);
143
+ }
144
+ catch {
145
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
146
+ res.end('Internal processing error');
147
+ return;
148
+ }
149
+ // Send reply back to GitHub if non-empty
150
+ if (reply) {
151
+ const token = this.ghAuth?.getInstallationToken() ?? '';
152
+ if (!token) {
153
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
154
+ res.end('Authentication error');
155
+ return;
156
+ }
157
+ try {
158
+ await this.sender.sendComment(token, normalized, reply);
159
+ }
160
+ catch {
161
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
162
+ res.end('Failed to send reply');
163
+ return;
164
+ }
165
+ }
166
+ }
167
+ respondJSON(res, 200, { status: 'processed', session_key: sessKey });
168
+ }
169
+ }
170
+ exports.WebhookHandler = WebhookHandler;
171
+ /** Normalize a parsed GitHub event by event type + action. */
172
+ function normalizeEvent(eventType, parsed) {
173
+ switch (eventType) {
174
+ case events_1.EventType.Issues: {
175
+ const e = parsed;
176
+ switch (e.action) {
177
+ case 'opened': return (0, normalizer_1.normalizeIssueOpened)(e);
178
+ case 'edited': return (0, normalizer_1.normalizeIssueEdited)(e);
179
+ case 'closed': return (0, normalizer_1.normalizeIssueClosed)(e);
180
+ case 'reopened': return (0, normalizer_1.normalizeIssueReopened)(e);
181
+ case 'labeled': return (0, normalizer_1.normalizeIssueLabeled)(e);
182
+ }
183
+ break;
184
+ }
185
+ case events_1.EventType.IssueComment: {
186
+ const e = parsed;
187
+ switch (e.action) {
188
+ case 'created': return (0, normalizer_1.normalizeIssueComment)(e);
189
+ case 'edited': return (0, normalizer_1.normalizeIssueCommentEdited)(e);
190
+ }
191
+ break;
192
+ }
193
+ case events_1.EventType.PullRequest: {
194
+ const e = parsed;
195
+ switch (e.action) {
196
+ case 'opened': return (0, normalizer_1.normalizePullRequestOpened)(e);
197
+ case 'edited': return (0, normalizer_1.normalizePullRequestEdited)(e);
198
+ case 'closed': return (0, normalizer_1.normalizePullRequestClosed)(e);
199
+ case 'synchronize': return (0, normalizer_1.normalizePullRequestSynchronize)(e);
200
+ case 'labeled': return (0, normalizer_1.normalizePullRequestLabeled)(e);
201
+ }
202
+ break;
203
+ }
204
+ case events_1.EventType.PullRequestReview: {
205
+ const e = parsed;
206
+ if (e.action === 'submitted')
207
+ return (0, normalizer_1.normalizePullRequestReview)(e);
208
+ break;
209
+ }
210
+ case events_1.EventType.PullRequestReviewComment: {
211
+ const e = parsed;
212
+ if (e.action === 'created')
213
+ return (0, normalizer_1.normalizePullRequestReviewComment)(e);
214
+ break;
215
+ }
216
+ case events_1.EventType.Discussion: {
217
+ const e = parsed;
218
+ if (e.action === 'created')
219
+ return (0, normalizer_1.normalizeDiscussionCreated)(e);
220
+ break;
221
+ }
222
+ case events_1.EventType.DiscussionComment: {
223
+ const e = parsed;
224
+ if (e.action === 'created')
225
+ return (0, normalizer_1.normalizeDiscussionComment)(e);
226
+ break;
227
+ }
228
+ case events_1.EventType.CheckRun: {
229
+ const e = parsed;
230
+ if (e.action === 'completed')
231
+ return (0, normalizer_1.normalizeCheckRun)(e);
232
+ break;
233
+ }
234
+ case events_1.EventType.WorkflowRun: {
235
+ const e = parsed;
236
+ if (e.action === 'completed')
237
+ return (0, normalizer_1.normalizeWorkflowRun)(e);
238
+ break;
239
+ }
240
+ }
241
+ return null;
242
+ }
243
+ /** Read the request body with a size limit. */
244
+ function readBody(req, maxSize) {
245
+ return new Promise((resolve, reject) => {
246
+ const chunks = [];
247
+ let size = 0;
248
+ req.on('data', (chunk) => {
249
+ size += chunk.length;
250
+ if (size > maxSize) {
251
+ req.destroy();
252
+ reject(new Error('Payload too large'));
253
+ return;
254
+ }
255
+ chunks.push(chunk);
256
+ });
257
+ req.on('end', () => resolve(Buffer.concat(chunks)));
258
+ req.on('error', (err) => reject(err));
259
+ });
260
+ }
261
+ /** Send a JSON response. */
262
+ function respondJSON(res, statusCode, body) {
263
+ const data = JSON.stringify(body);
264
+ res.writeHead(statusCode, { 'Content-Type': 'application/json' });
265
+ res.end(data + '\n');
266
+ }
267
+ /** Create an HTTP server with /webhook and /health endpoints. */
268
+ function createServer(handler) {
269
+ return http.createServer(async (req, res) => {
270
+ const url = req.url ?? '/';
271
+ if (url === '/webhook') {
272
+ await handler.handleRequest(req, res);
273
+ }
274
+ else if (url === '/health') {
275
+ respondJSON(res, 200, { status: 'ok' });
276
+ }
277
+ else {
278
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
279
+ res.end('Not found');
280
+ }
281
+ });
282
+ }
283
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/server/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export declare class Store {
2
+ private entries;
3
+ private ttl;
4
+ private cleanupTimer?;
5
+ constructor(ttlMs?: number);
6
+ /** Check whether the delivery ID has already been processed. */
7
+ isDuplicate(deliveryID: string): boolean;
8
+ /** Record a delivery ID as processed. */
9
+ markProcessed(deliveryID: string): void;
10
+ /** Remove expired entries. */
11
+ cleanup(): void;
12
+ /** Current number of entries. */
13
+ size(): number;
14
+ /** Start a background cleanup loop. Returns a stop function. */
15
+ startCleanupLoop(intervalMs: number): () => void;
16
+ }
17
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/state/state.ts"],"names":[],"mappings":"AAEA,qBAAa,KAAK;IAChB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,YAAY,CAAC,CAAiC;gBAE1C,KAAK,GAAE,MAAiB;IAIpC,gEAAgE;IAChE,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAKxC,yCAAyC;IACzC,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAKvC,8BAA8B;IAC9B,OAAO,IAAI,IAAI;IASf,iCAAiC;IACjC,IAAI,IAAI,MAAM;IAId,gEAAgE;IAChE,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,IAAI;CASjD"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ // Package state provides idempotency and deduplication for webhook event processing.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.Store = void 0;
5
+ class Store {
6
+ entries = new Map(); // deliveryID -> timestamp
7
+ ttl; // milliseconds
8
+ cleanupTimer;
9
+ constructor(ttlMs = 3600_000) {
10
+ this.ttl = ttlMs > 0 ? ttlMs : 3600_000;
11
+ }
12
+ /** Check whether the delivery ID has already been processed. */
13
+ isDuplicate(deliveryID) {
14
+ if (!deliveryID)
15
+ return false;
16
+ return this.entries.has(deliveryID);
17
+ }
18
+ /** Record a delivery ID as processed. */
19
+ markProcessed(deliveryID) {
20
+ if (!deliveryID)
21
+ return;
22
+ this.entries.set(deliveryID, Date.now());
23
+ }
24
+ /** Remove expired entries. */
25
+ cleanup() {
26
+ const cutoff = Date.now() - this.ttl;
27
+ for (const [id, ts] of this.entries) {
28
+ if (ts < cutoff) {
29
+ this.entries.delete(id);
30
+ }
31
+ }
32
+ }
33
+ /** Current number of entries. */
34
+ size() {
35
+ return this.entries.size;
36
+ }
37
+ /** Start a background cleanup loop. Returns a stop function. */
38
+ startCleanupLoop(intervalMs) {
39
+ this.cleanupTimer = setInterval(() => this.cleanup(), intervalMs);
40
+ return () => {
41
+ if (this.cleanupTimer) {
42
+ clearInterval(this.cleanupTimer);
43
+ this.cleanupTimer = undefined;
44
+ }
45
+ };
46
+ }
47
+ }
48
+ exports.Store = Store;
49
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/state/state.ts"],"names":[],"mappings":";AAAA,qFAAqF;;;AAErF,MAAa,KAAK;IACR,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,0BAA0B;IAC/D,GAAG,CAAS,CAAC,eAAe;IAC5B,YAAY,CAAkC;IAEtD,YAAY,QAAgB,QAAQ;QAClC,IAAI,CAAC,GAAG,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC1C,CAAC;IAED,gEAAgE;IAChE,WAAW,CAAC,UAAkB;QAC5B,IAAI,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACtC,CAAC;IAED,yCAAyC;IACzC,aAAa,CAAC,UAAkB;QAC9B,IAAI,CAAC,UAAU;YAAE,OAAO;QACxB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,8BAA8B;IAC9B,OAAO;QACL,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC;QACrC,KAAK,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;gBAChB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,IAAI;QACF,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;IAED,gEAAgE;IAChE,gBAAgB,CAAC,UAAkB;QACjC,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;QAClE,OAAO,GAAG,EAAE;YACV,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACjC,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;YAChC,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;CACF;AA9CD,sBA8CC"}