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.
- package/README.md +578 -0
- package/config.example.json +33 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/src/auth/auth.d.ts +22 -0
- package/dist/src/auth/auth.d.ts.map +1 -0
- package/dist/src/auth/auth.js +112 -0
- package/dist/src/auth/auth.js.map +1 -0
- package/dist/src/config/config.d.ts +60 -0
- package/dist/src/config/config.d.ts.map +1 -0
- package/dist/src/config/config.js +184 -0
- package/dist/src/config/config.js.map +1 -0
- package/dist/src/events/events.d.ts +234 -0
- package/dist/src/events/events.d.ts.map +1 -0
- package/dist/src/events/events.js +53 -0
- package/dist/src/events/events.js.map +1 -0
- package/dist/src/main.d.ts +2 -0
- package/dist/src/main.d.ts.map +1 -0
- package/dist/src/main.js +115 -0
- package/dist/src/main.js.map +1 -0
- package/dist/src/normalizer/normalizer.d.ts +94 -0
- package/dist/src/normalizer/normalizer.d.ts.map +1 -0
- package/dist/src/normalizer/normalizer.js +486 -0
- package/dist/src/normalizer/normalizer.js.map +1 -0
- package/dist/src/outbound/outbound.d.ts +24 -0
- package/dist/src/outbound/outbound.d.ts.map +1 -0
- package/dist/src/outbound/outbound.js +125 -0
- package/dist/src/outbound/outbound.js.map +1 -0
- package/dist/src/routing/routing.d.ts +22 -0
- package/dist/src/routing/routing.d.ts.map +1 -0
- package/dist/src/routing/routing.js +97 -0
- package/dist/src/routing/routing.js.map +1 -0
- package/dist/src/server/server.d.ts +20 -0
- package/dist/src/server/server.d.ts.map +1 -0
- package/dist/src/server/server.js +283 -0
- package/dist/src/server/server.js.map +1 -0
- package/dist/src/state/state.d.ts +17 -0
- package/dist/src/state/state.d.ts.map +1 -0
- package/dist/src/state/state.js +49 -0
- package/dist/src/state/state.js.map +1 -0
- package/docs/api.md +450 -0
- package/docs/usage.md +393 -0
- package/openclaw.plugin.json +104 -0
- 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"}
|