@generacy-ai/orchestrator 0.0.0-preview-20260304013206
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/LICENSE +191 -0
- package/dist/auth/api-key.d.ts +63 -0
- package/dist/auth/api-key.d.ts.map +1 -0
- package/dist/auth/api-key.js +102 -0
- package/dist/auth/api-key.js.map +1 -0
- package/dist/auth/github-oauth.d.ts +29 -0
- package/dist/auth/github-oauth.d.ts.map +1 -0
- package/dist/auth/github-oauth.js +136 -0
- package/dist/auth/github-oauth.js.map +1 -0
- package/dist/auth/index.d.ts +5 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +9 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/jwt.d.ts +34 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/auth/jwt.js +74 -0
- package/dist/auth/jwt.js.map +1 -0
- package/dist/auth/middleware.d.ts +43 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +101 -0
- package/dist/auth/middleware.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +5 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +21 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +295 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +733 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +174 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +30 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +173 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/index.d.ts +4 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +4 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +32 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +92 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/middleware/request-logger.d.ts +31 -0
- package/dist/middleware/request-logger.d.ts.map +1 -0
- package/dist/middleware/request-logger.js +89 -0
- package/dist/middleware/request-logger.js.map +1 -0
- package/dist/routes/agents.d.ts +7 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +78 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/dispatch.d.ts +8 -0
- package/dist/routes/dispatch.d.ts.map +1 -0
- package/dist/routes/dispatch.js +46 -0
- package/dist/routes/dispatch.js.map +1 -0
- package/dist/routes/events.d.ts +14 -0
- package/dist/routes/events.d.ts.map +1 -0
- package/dist/routes/events.js +240 -0
- package/dist/routes/events.js.map +1 -0
- package/dist/routes/health.d.ts +14 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +132 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/index.d.ts +36 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +34 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/integrations.d.ts +34 -0
- package/dist/routes/integrations.d.ts.map +1 -0
- package/dist/routes/integrations.js +81 -0
- package/dist/routes/integrations.js.map +1 -0
- package/dist/routes/metrics.d.ts +31 -0
- package/dist/routes/metrics.d.ts.map +1 -0
- package/dist/routes/metrics.js +157 -0
- package/dist/routes/metrics.js.map +1 -0
- package/dist/routes/pr-webhooks.d.ts +15 -0
- package/dist/routes/pr-webhooks.d.ts.map +1 -0
- package/dist/routes/pr-webhooks.js +114 -0
- package/dist/routes/pr-webhooks.js.map +1 -0
- package/dist/routes/queue.d.ts +7 -0
- package/dist/routes/queue.d.ts.map +1 -0
- package/dist/routes/queue.js +94 -0
- package/dist/routes/queue.js.map +1 -0
- package/dist/routes/webhooks.d.ts +15 -0
- package/dist/routes/webhooks.d.ts.map +1 -0
- package/dist/routes/webhooks.js +117 -0
- package/dist/routes/webhooks.js.map +1 -0
- package/dist/routes/workflows.d.ts +7 -0
- package/dist/routes/workflows.d.ts.map +1 -0
- package/dist/routes/workflows.js +138 -0
- package/dist/routes/workflows.js.map +1 -0
- package/dist/server.d.ts +35 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +264 -0
- package/dist/server.js.map +1 -0
- package/dist/services/agent-registry.d.ts +89 -0
- package/dist/services/agent-registry.d.ts.map +1 -0
- package/dist/services/agent-registry.js +173 -0
- package/dist/services/agent-registry.js.map +1 -0
- package/dist/services/epic-completion-monitor-service.d.ts +69 -0
- package/dist/services/epic-completion-monitor-service.d.ts.map +1 -0
- package/dist/services/epic-completion-monitor-service.js +200 -0
- package/dist/services/epic-completion-monitor-service.js.map +1 -0
- package/dist/services/identity.d.ts +41 -0
- package/dist/services/identity.d.ts.map +1 -0
- package/dist/services/identity.js +91 -0
- package/dist/services/identity.js.map +1 -0
- package/dist/services/index.d.ts +13 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +13 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/label-monitor-service.d.ts +95 -0
- package/dist/services/label-monitor-service.d.ts.map +1 -0
- package/dist/services/label-monitor-service.js +414 -0
- package/dist/services/label-monitor-service.js.map +1 -0
- package/dist/services/label-sync-service.d.ts +76 -0
- package/dist/services/label-sync-service.d.ts.map +1 -0
- package/dist/services/label-sync-service.js +108 -0
- package/dist/services/label-sync-service.js.map +1 -0
- package/dist/services/phase-tracker-service.d.ts +32 -0
- package/dist/services/phase-tracker-service.d.ts.map +1 -0
- package/dist/services/phase-tracker-service.js +82 -0
- package/dist/services/phase-tracker-service.js.map +1 -0
- package/dist/services/pr-feedback-monitor-service.d.ts +86 -0
- package/dist/services/pr-feedback-monitor-service.d.ts.map +1 -0
- package/dist/services/pr-feedback-monitor-service.js +423 -0
- package/dist/services/pr-feedback-monitor-service.js.map +1 -0
- package/dist/services/queue-service.d.ts +62 -0
- package/dist/services/queue-service.d.ts.map +1 -0
- package/dist/services/queue-service.js +147 -0
- package/dist/services/queue-service.js.map +1 -0
- package/dist/services/redis-queue-adapter.d.ts +35 -0
- package/dist/services/redis-queue-adapter.d.ts.map +1 -0
- package/dist/services/redis-queue-adapter.js +218 -0
- package/dist/services/redis-queue-adapter.js.map +1 -0
- package/dist/services/smee-receiver.d.ts +87 -0
- package/dist/services/smee-receiver.d.ts.map +1 -0
- package/dist/services/smee-receiver.js +225 -0
- package/dist/services/smee-receiver.js.map +1 -0
- package/dist/services/webhook-setup-service.d.ts +253 -0
- package/dist/services/webhook-setup-service.d.ts.map +1 -0
- package/dist/services/webhook-setup-service.js +369 -0
- package/dist/services/webhook-setup-service.js.map +1 -0
- package/dist/services/worker-dispatcher.d.ts +57 -0
- package/dist/services/worker-dispatcher.d.ts.map +1 -0
- package/dist/services/worker-dispatcher.js +211 -0
- package/dist/services/worker-dispatcher.js.map +1 -0
- package/dist/services/workflow-service.d.ts +65 -0
- package/dist/services/workflow-service.d.ts.map +1 -0
- package/dist/services/workflow-service.js +164 -0
- package/dist/services/workflow-service.js.map +1 -0
- package/dist/sse/events.d.ts +50 -0
- package/dist/sse/events.d.ts.map +1 -0
- package/dist/sse/events.js +152 -0
- package/dist/sse/events.js.map +1 -0
- package/dist/sse/index.d.ts +4 -0
- package/dist/sse/index.d.ts.map +1 -0
- package/dist/sse/index.js +7 -0
- package/dist/sse/index.js.map +1 -0
- package/dist/sse/stream.d.ts +109 -0
- package/dist/sse/stream.d.ts.map +1 -0
- package/dist/sse/stream.js +239 -0
- package/dist/sse/stream.js.map +1 -0
- package/dist/sse/subscriptions.d.ts +91 -0
- package/dist/sse/subscriptions.d.ts.map +1 -0
- package/dist/sse/subscriptions.js +299 -0
- package/dist/sse/subscriptions.js.map +1 -0
- package/dist/types/api.d.ts +651 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +169 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +27 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/monitor.d.ts +243 -0
- package/dist/types/monitor.d.ts.map +1 -0
- package/dist/types/monitor.js +2 -0
- package/dist/types/monitor.js.map +1 -0
- package/dist/types/problem-details.d.ts +63 -0
- package/dist/types/problem-details.d.ts.map +1 -0
- package/dist/types/problem-details.js +37 -0
- package/dist/types/problem-details.js.map +1 -0
- package/dist/types/sse.d.ts +224 -0
- package/dist/types/sse.d.ts.map +1 -0
- package/dist/types/sse.js +103 -0
- package/dist/types/sse.js.map +1 -0
- package/dist/types/webhook.d.ts +59 -0
- package/dist/types/webhook.d.ts.map +1 -0
- package/dist/types/webhook.js +2 -0
- package/dist/types/webhook.js.map +1 -0
- package/dist/utils/correlation.d.ts +30 -0
- package/dist/utils/correlation.d.ts.map +1 -0
- package/dist/utils/correlation.js +34 -0
- package/dist/utils/correlation.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/shutdown.d.ts +28 -0
- package/dist/utils/shutdown.d.ts.map +1 -0
- package/dist/utils/shutdown.js +86 -0
- package/dist/utils/shutdown.js.map +1 -0
- package/dist/worker/claude-cli-worker.d.ts +61 -0
- package/dist/worker/claude-cli-worker.d.ts.map +1 -0
- package/dist/worker/claude-cli-worker.js +330 -0
- package/dist/worker/claude-cli-worker.js.map +1 -0
- package/dist/worker/cli-spawner.d.ts +42 -0
- package/dist/worker/cli-spawner.d.ts.map +1 -0
- package/dist/worker/cli-spawner.js +183 -0
- package/dist/worker/cli-spawner.js.map +1 -0
- package/dist/worker/config.d.ts +76 -0
- package/dist/worker/config.d.ts.map +1 -0
- package/dist/worker/config.js +39 -0
- package/dist/worker/config.js.map +1 -0
- package/dist/worker/epic-post-tasks.d.ts +73 -0
- package/dist/worker/epic-post-tasks.d.ts.map +1 -0
- package/dist/worker/epic-post-tasks.js +266 -0
- package/dist/worker/epic-post-tasks.js.map +1 -0
- package/dist/worker/gate-checker.d.ts +22 -0
- package/dist/worker/gate-checker.d.ts.map +1 -0
- package/dist/worker/gate-checker.js +41 -0
- package/dist/worker/gate-checker.js.map +1 -0
- package/dist/worker/index.d.ts +13 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +19 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/label-manager.d.ts +89 -0
- package/dist/worker/label-manager.d.ts.map +1 -0
- package/dist/worker/label-manager.js +200 -0
- package/dist/worker/label-manager.js.map +1 -0
- package/dist/worker/output-capture.d.ts +58 -0
- package/dist/worker/output-capture.d.ts.map +1 -0
- package/dist/worker/output-capture.js +153 -0
- package/dist/worker/output-capture.js.map +1 -0
- package/dist/worker/phase-loop.d.ts +64 -0
- package/dist/worker/phase-loop.d.ts.map +1 -0
- package/dist/worker/phase-loop.js +185 -0
- package/dist/worker/phase-loop.js.map +1 -0
- package/dist/worker/phase-resolver.d.ts +62 -0
- package/dist/worker/phase-resolver.d.ts.map +1 -0
- package/dist/worker/phase-resolver.js +144 -0
- package/dist/worker/phase-resolver.js.map +1 -0
- package/dist/worker/pr-feedback-handler.d.ts +105 -0
- package/dist/worker/pr-feedback-handler.d.ts.map +1 -0
- package/dist/worker/pr-feedback-handler.js +391 -0
- package/dist/worker/pr-feedback-handler.js.map +1 -0
- package/dist/worker/pr-linker.d.ts +69 -0
- package/dist/worker/pr-linker.d.ts.map +1 -0
- package/dist/worker/pr-linker.js +108 -0
- package/dist/worker/pr-linker.js.map +1 -0
- package/dist/worker/pr-manager.d.ts +61 -0
- package/dist/worker/pr-manager.d.ts.map +1 -0
- package/dist/worker/pr-manager.js +140 -0
- package/dist/worker/pr-manager.js.map +1 -0
- package/dist/worker/repo-checkout.d.ts +63 -0
- package/dist/worker/repo-checkout.d.ts.map +1 -0
- package/dist/worker/repo-checkout.js +160 -0
- package/dist/worker/repo-checkout.js.map +1 -0
- package/dist/worker/stage-comment-manager.d.ts +34 -0
- package/dist/worker/stage-comment-manager.d.ts.map +1 -0
- package/dist/worker/stage-comment-manager.js +114 -0
- package/dist/worker/stage-comment-manager.js.map +1 -0
- package/dist/worker/types.d.ts +192 -0
- package/dist/worker/types.d.ts.map +1 -0
- package/dist/worker/types.js +53 -0
- package/dist/worker/types.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Receives GitHub webhook events from a smee.io channel via SSE.
|
|
3
|
+
* Feeds label events directly into the LabelMonitorService.
|
|
4
|
+
*/
|
|
5
|
+
export class SmeeWebhookReceiver {
|
|
6
|
+
logger;
|
|
7
|
+
monitorService;
|
|
8
|
+
static BASE_RECONNECT_DELAY_MS = 5000;
|
|
9
|
+
static MAX_BACKOFF_MS = 300000; // 5 minutes
|
|
10
|
+
channelUrl;
|
|
11
|
+
watchedRepos;
|
|
12
|
+
baseReconnectDelayMs;
|
|
13
|
+
abortController = null;
|
|
14
|
+
running = false;
|
|
15
|
+
reconnectAttempt = 0;
|
|
16
|
+
constructor(logger, monitorService, options) {
|
|
17
|
+
this.logger = logger;
|
|
18
|
+
this.monitorService = monitorService;
|
|
19
|
+
this.channelUrl = options.channelUrl;
|
|
20
|
+
this.watchedRepos = options.watchedRepos;
|
|
21
|
+
this.baseReconnectDelayMs = options.baseReconnectDelayMs ?? SmeeWebhookReceiver.BASE_RECONNECT_DELAY_MS;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Start listening to the smee.io channel.
|
|
25
|
+
* Automatically reconnects on disconnect.
|
|
26
|
+
*/
|
|
27
|
+
async start() {
|
|
28
|
+
if (this.running) {
|
|
29
|
+
this.logger.warn('Smee receiver already running');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
this.running = true;
|
|
33
|
+
this.abortController = new AbortController();
|
|
34
|
+
const { signal } = this.abortController;
|
|
35
|
+
this.logger.info({ channelUrl: this.channelUrl, watchedRepos: [...this.watchedRepos] }, 'Starting smee.io webhook receiver');
|
|
36
|
+
while (this.running && !signal.aborted) {
|
|
37
|
+
try {
|
|
38
|
+
await this.connect(signal);
|
|
39
|
+
// Reset backoff counter on successful connection
|
|
40
|
+
this.reconnectAttempt = 0;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (signal.aborted)
|
|
44
|
+
break;
|
|
45
|
+
const reconnectMs = this.reconnectDelayMs;
|
|
46
|
+
this.logger.warn({ err: String(error), reconnectMs, attempt: this.reconnectAttempt }, 'Smee connection lost, reconnecting...');
|
|
47
|
+
// Increment attempt counter for exponential backoff
|
|
48
|
+
this.reconnectAttempt++;
|
|
49
|
+
}
|
|
50
|
+
// Wait before reconnecting
|
|
51
|
+
if (this.running && !signal.aborted) {
|
|
52
|
+
await this.sleep(this.reconnectDelayMs, signal);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.running = false;
|
|
56
|
+
this.logger.info('Smee receiver stopped');
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Stop the receiver.
|
|
60
|
+
*/
|
|
61
|
+
stop() {
|
|
62
|
+
this.running = false;
|
|
63
|
+
if (this.abortController) {
|
|
64
|
+
this.abortController.abort();
|
|
65
|
+
this.abortController = null;
|
|
66
|
+
}
|
|
67
|
+
this.logger.info('Smee receiver stop requested');
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Connect to the smee.io channel and process events.
|
|
71
|
+
*/
|
|
72
|
+
async connect(signal) {
|
|
73
|
+
const response = await fetch(this.channelUrl, {
|
|
74
|
+
headers: { Accept: 'text/event-stream' },
|
|
75
|
+
signal,
|
|
76
|
+
});
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
throw new Error(`Smee connection failed: ${response.status} ${response.statusText}`);
|
|
79
|
+
}
|
|
80
|
+
if (!response.body) {
|
|
81
|
+
throw new Error('Smee response has no body');
|
|
82
|
+
}
|
|
83
|
+
this.logger.info('Connected to smee.io channel');
|
|
84
|
+
const reader = response.body.getReader();
|
|
85
|
+
const decoder = new TextDecoder();
|
|
86
|
+
let buffer = '';
|
|
87
|
+
try {
|
|
88
|
+
while (!signal.aborted) {
|
|
89
|
+
const { done, value } = await reader.read();
|
|
90
|
+
if (done)
|
|
91
|
+
break;
|
|
92
|
+
buffer += decoder.decode(value, { stream: true });
|
|
93
|
+
// Process complete SSE events (separated by double newline)
|
|
94
|
+
const events = buffer.split('\n\n');
|
|
95
|
+
// Keep the last incomplete chunk in the buffer
|
|
96
|
+
buffer = events.pop() ?? '';
|
|
97
|
+
for (const eventBlock of events) {
|
|
98
|
+
if (!eventBlock.trim())
|
|
99
|
+
continue;
|
|
100
|
+
await this.processSSEEvent(eventBlock);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
reader.releaseLock();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Parse and process a single SSE event block from smee.io.
|
|
110
|
+
*
|
|
111
|
+
* Smee.io SSE events have the format:
|
|
112
|
+
* event: message
|
|
113
|
+
* data: {"body": <webhook-payload>, "x-github-event": "issues", ...}
|
|
114
|
+
*/
|
|
115
|
+
async processSSEEvent(eventBlock) {
|
|
116
|
+
let eventType = '';
|
|
117
|
+
const dataLines = [];
|
|
118
|
+
for (const line of eventBlock.split('\n')) {
|
|
119
|
+
if (line.startsWith('event:')) {
|
|
120
|
+
eventType = line.slice(6).trim();
|
|
121
|
+
}
|
|
122
|
+
else if (line.startsWith('data:')) {
|
|
123
|
+
dataLines.push(line.slice(5).trim());
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Only process message events
|
|
127
|
+
if (eventType !== 'message' && eventType !== '') {
|
|
128
|
+
// Smee sends 'ready' and 'ping' events — ignore them
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const dataStr = dataLines.join('\n');
|
|
132
|
+
if (!dataStr)
|
|
133
|
+
return;
|
|
134
|
+
let data;
|
|
135
|
+
try {
|
|
136
|
+
data = JSON.parse(dataStr);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return; // Skip malformed events
|
|
140
|
+
}
|
|
141
|
+
// Smee wraps the webhook payload in a 'body' field
|
|
142
|
+
const githubEvent = data['x-github-event'];
|
|
143
|
+
const body = data['body'];
|
|
144
|
+
if (!body || githubEvent !== 'issues') {
|
|
145
|
+
return; // Only interested in issue events
|
|
146
|
+
}
|
|
147
|
+
const payload = body;
|
|
148
|
+
// Only handle "labeled" action
|
|
149
|
+
if (payload.action !== 'labeled')
|
|
150
|
+
return;
|
|
151
|
+
// Check if this is a watched repository
|
|
152
|
+
if (!payload.repository?.owner?.login || !payload.repository?.name)
|
|
153
|
+
return;
|
|
154
|
+
const repoKey = `${payload.repository.owner.login}/${payload.repository.name}`;
|
|
155
|
+
if (!this.watchedRepos.has(repoKey))
|
|
156
|
+
return;
|
|
157
|
+
// Parse and process the label event
|
|
158
|
+
const issueLabels = payload.issue?.labels?.map(l => l.name) ?? [];
|
|
159
|
+
const event = this.monitorService.parseLabelEvent(payload.label.name, payload.repository.owner.login, payload.repository.name, payload.issue.number, issueLabels, 'webhook');
|
|
160
|
+
if (!event) {
|
|
161
|
+
// If this was a completed:* label that didn't match a waiting-for:*
|
|
162
|
+
// in the webhook payload, re-fetch from GitHub to handle stale payloads.
|
|
163
|
+
if (payload.label.name.startsWith('completed:')) {
|
|
164
|
+
this.logger.info({ label: payload.label.name, repo: repoKey, issue: payload.issue.number }, 'Webhook completed:* label has no matching waiting-for:* in payload, attempting re-fetch');
|
|
165
|
+
this.monitorService.recordWebhookEvent();
|
|
166
|
+
try {
|
|
167
|
+
await this.monitorService.verifyAndProcessCompletedLabel(payload.repository.owner.login, payload.repository.name, payload.issue.number, payload.label.name);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
this.logger.error({ err: String(error), repo: repoKey, issue: payload.issue.number }, 'Error during completed:* label re-fetch verification');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
this.logger.info({
|
|
176
|
+
type: event.type,
|
|
177
|
+
repo: repoKey,
|
|
178
|
+
issue: event.issueNumber,
|
|
179
|
+
label: event.labelName,
|
|
180
|
+
}, 'Smee webhook received label event');
|
|
181
|
+
// Record webhook event for adaptive polling health tracking
|
|
182
|
+
this.monitorService.recordWebhookEvent();
|
|
183
|
+
// Process the event
|
|
184
|
+
try {
|
|
185
|
+
await this.monitorService.processLabelEvent(event);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
this.logger.error({ err: String(error), repo: repoKey, issue: event.issueNumber }, 'Error processing smee webhook event');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Get the current reconnect delay based on the number of attempts.
|
|
193
|
+
* Uses exponential backoff with the current attempt count.
|
|
194
|
+
*/
|
|
195
|
+
get reconnectDelayMs() {
|
|
196
|
+
return this.calculateBackoffDelay(this.reconnectAttempt);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Calculate exponential backoff delay for reconnection attempts.
|
|
200
|
+
* Formula: BASE_RECONNECT_DELAY_MS * 2^attempt, capped at MAX_BACKOFF_MS.
|
|
201
|
+
* Progression: 5s → 10s → 20s → 40s → 80s → 160s → 300s (capped).
|
|
202
|
+
*
|
|
203
|
+
* @param attempt - The current reconnection attempt number (0-indexed)
|
|
204
|
+
* @returns Delay in milliseconds before next reconnection attempt
|
|
205
|
+
*/
|
|
206
|
+
calculateBackoffDelay(attempt) {
|
|
207
|
+
const delay = this.baseReconnectDelayMs * Math.pow(2, attempt);
|
|
208
|
+
return Math.min(delay, SmeeWebhookReceiver.MAX_BACKOFF_MS);
|
|
209
|
+
}
|
|
210
|
+
sleep(ms, signal) {
|
|
211
|
+
return new Promise((resolve) => {
|
|
212
|
+
if (signal.aborted) {
|
|
213
|
+
resolve();
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const timer = setTimeout(resolve, ms);
|
|
217
|
+
const onAbort = () => {
|
|
218
|
+
clearTimeout(timer);
|
|
219
|
+
resolve();
|
|
220
|
+
};
|
|
221
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=smee-receiver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smee-receiver.js","sourceRoot":"","sources":["../../src/services/smee-receiver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger interface matching Pino/Fastify logger shape.
|
|
3
|
+
*
|
|
4
|
+
* Supports both simple string messages and structured logging with context objects.
|
|
5
|
+
*/
|
|
6
|
+
interface Logger {
|
|
7
|
+
/** Log informational message */
|
|
8
|
+
info(msg: string): void;
|
|
9
|
+
/** Log informational message with structured context */
|
|
10
|
+
info(obj: Record<string, unknown>, msg: string): void;
|
|
11
|
+
/** Log warning message */
|
|
12
|
+
warn(msg: string): void;
|
|
13
|
+
/** Log warning message with structured context */
|
|
14
|
+
warn(obj: Record<string, unknown>, msg: string): void;
|
|
15
|
+
/** Log error message */
|
|
16
|
+
error(msg: string): void;
|
|
17
|
+
/** Log error message with structured context */
|
|
18
|
+
error(obj: Record<string, unknown>, msg: string): void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Repository configuration for webhook setup.
|
|
22
|
+
*
|
|
23
|
+
* Identifies a GitHub repository by owner and name for webhook management.
|
|
24
|
+
*/
|
|
25
|
+
export interface RepositoryConfig {
|
|
26
|
+
/** Repository owner (organization or user) */
|
|
27
|
+
owner: string;
|
|
28
|
+
/** Repository name */
|
|
29
|
+
repo: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Minimal GitHub webhook response type.
|
|
33
|
+
*
|
|
34
|
+
* Only includes fields needed for matching and reactivation.
|
|
35
|
+
* See: https://docs.github.com/en/rest/webhooks/repos#list-repository-webhooks
|
|
36
|
+
*/
|
|
37
|
+
export interface GitHubWebhook {
|
|
38
|
+
/** Webhook ID (unique identifier) */
|
|
39
|
+
id: number;
|
|
40
|
+
/** Whether webhook is active (receiving events) */
|
|
41
|
+
active: boolean;
|
|
42
|
+
/** Webhook configuration */
|
|
43
|
+
config: {
|
|
44
|
+
/** Target URL where webhook events are sent */
|
|
45
|
+
url: string;
|
|
46
|
+
};
|
|
47
|
+
/** Event types webhook subscribes to (e.g., ["issues", "pull_request"]) */
|
|
48
|
+
events: string[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Result of webhook setup for a single repository.
|
|
52
|
+
*
|
|
53
|
+
* Captures the action taken and any relevant details or errors.
|
|
54
|
+
*/
|
|
55
|
+
export interface WebhookSetupResult {
|
|
56
|
+
/** Repository owner (organization or user) */
|
|
57
|
+
owner: string;
|
|
58
|
+
/** Repository name */
|
|
59
|
+
repo: string;
|
|
60
|
+
/**
|
|
61
|
+
* Action taken during webhook setup
|
|
62
|
+
* - `created`: New webhook was created
|
|
63
|
+
* - `skipped`: Active webhook already exists
|
|
64
|
+
* - `reactivated`: Inactive webhook was reactivated
|
|
65
|
+
* - `failed`: Setup failed (see `error` field)
|
|
66
|
+
*/
|
|
67
|
+
action: 'created' | 'skipped' | 'reactivated' | 'failed';
|
|
68
|
+
/** Webhook ID (present for created/skipped/reactivated, absent for failed) */
|
|
69
|
+
webhookId?: number;
|
|
70
|
+
/** Error message (only present when action === 'failed') */
|
|
71
|
+
error?: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Summary of webhook setup across all repositories.
|
|
75
|
+
*
|
|
76
|
+
* Provides aggregate counts and detailed per-repository results.
|
|
77
|
+
*/
|
|
78
|
+
export interface WebhookSetupSummary {
|
|
79
|
+
/** Total number of repositories checked */
|
|
80
|
+
total: number;
|
|
81
|
+
/** Number of webhooks created (new) */
|
|
82
|
+
created: number;
|
|
83
|
+
/** Number of webhooks skipped (already active) */
|
|
84
|
+
skipped: number;
|
|
85
|
+
/** Number of webhooks reactivated (was inactive) */
|
|
86
|
+
reactivated: number;
|
|
87
|
+
/** Number of repositories that failed webhook setup */
|
|
88
|
+
failed: number;
|
|
89
|
+
/** Detailed results for each repository */
|
|
90
|
+
results: WebhookSetupResult[];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Service for auto-configuring GitHub webhooks for monitored repositories.
|
|
94
|
+
*
|
|
95
|
+
* When SMEE_CHANNEL_URL is configured, this service verifies and creates
|
|
96
|
+
* GitHub webhooks for all monitored repositories on orchestrator startup,
|
|
97
|
+
* ensuring near-instant label detection via Smee instead of 15-minute polling.
|
|
98
|
+
*
|
|
99
|
+
* Features:
|
|
100
|
+
* - Creates missing webhooks pointing to Smee channel URL
|
|
101
|
+
* - Skips existing active webhooks (idempotent)
|
|
102
|
+
* - Reactivates inactive webhooks and merges events
|
|
103
|
+
* - Graceful error handling (per-repo failures don't block startup)
|
|
104
|
+
* - Warns on event mismatches without modifying
|
|
105
|
+
* - Validates Smee URL format (warns on non-smee.io URLs)
|
|
106
|
+
*
|
|
107
|
+
* Error Handling:
|
|
108
|
+
* - 403/404 errors: Logged as warnings, system falls back to polling
|
|
109
|
+
* - Network/API errors: Logged and continue with next repo
|
|
110
|
+
* - All errors are non-fatal and don't block orchestrator startup
|
|
111
|
+
*/
|
|
112
|
+
export declare class WebhookSetupService {
|
|
113
|
+
private readonly _logger;
|
|
114
|
+
/**
|
|
115
|
+
* Creates a new WebhookSetupService instance.
|
|
116
|
+
*
|
|
117
|
+
* @param logger - Logger instance for structured logging (Pino/Fastify compatible)
|
|
118
|
+
*/
|
|
119
|
+
constructor(logger: Logger);
|
|
120
|
+
/**
|
|
121
|
+
* Ensure webhooks exist for all repositories.
|
|
122
|
+
*
|
|
123
|
+
* This is the main entry point for webhook setup. For each repository:
|
|
124
|
+
* 1. Lists existing webhooks via GitHub API
|
|
125
|
+
* 2. Checks if a webhook already exists for the Smee channel URL
|
|
126
|
+
* 3. Creates missing webhooks, reactivates inactive ones, or skips active ones
|
|
127
|
+
* 4. Logs results and continues on errors (graceful degradation)
|
|
128
|
+
*
|
|
129
|
+
* Per-repository errors are logged but don't block the overall process,
|
|
130
|
+
* allowing partial success when some repositories fail (e.g., due to
|
|
131
|
+
* insufficient permissions).
|
|
132
|
+
*
|
|
133
|
+
* @param smeeChannelUrl - The Smee channel URL to configure webhooks for
|
|
134
|
+
* (e.g., 'https://smee.io/abc123'). URL is validated
|
|
135
|
+
* and a warning is logged if it doesn't point to smee.io.
|
|
136
|
+
* @param repositories - List of repositories to ensure webhooks for. Each
|
|
137
|
+
* repository is processed sequentially to avoid rate limits.
|
|
138
|
+
* @returns Promise resolving to a summary of webhook setup results, including
|
|
139
|
+
* aggregate counts and detailed per-repository results.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* const service = new WebhookSetupService(logger);
|
|
144
|
+
* const summary = await service.ensureWebhooks(
|
|
145
|
+
* 'https://smee.io/abc123',
|
|
146
|
+
* [
|
|
147
|
+
* { owner: 'myorg', repo: 'myrepo' },
|
|
148
|
+
* { owner: 'myorg', repo: 'another-repo' }
|
|
149
|
+
* ]
|
|
150
|
+
* );
|
|
151
|
+
* console.log(`Created: ${summary.created}, Skipped: ${summary.skipped}, Failed: ${summary.failed}`);
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* @see {@link WebhookSetupSummary} for the structure of the returned summary
|
|
155
|
+
*/
|
|
156
|
+
ensureWebhooks(smeeChannelUrl: string, repositories: RepositoryConfig[]): Promise<WebhookSetupSummary>;
|
|
157
|
+
/**
|
|
158
|
+
* Ensure webhook exists for a single repository.
|
|
159
|
+
*
|
|
160
|
+
* Internal method that handles webhook verification and setup for one repository.
|
|
161
|
+
* All errors are caught and converted to a 'failed' result to allow graceful
|
|
162
|
+
* degradation.
|
|
163
|
+
*
|
|
164
|
+
* Logic flow:
|
|
165
|
+
* 1. List existing webhooks
|
|
166
|
+
* 2. Find webhook matching the Smee URL (case-insensitive)
|
|
167
|
+
* 3. If not found: Create new webhook → 'created'
|
|
168
|
+
* 4. If found and active: Skip → 'skipped' (warn on event mismatch)
|
|
169
|
+
* 5. If found but inactive: Reactivate and merge events → 'reactivated'
|
|
170
|
+
*
|
|
171
|
+
* @param owner - Repository owner (organization or user)
|
|
172
|
+
* @param repo - Repository name
|
|
173
|
+
* @param smeeChannelUrl - The Smee channel URL to configure
|
|
174
|
+
* @returns Promise resolving to the result of webhook setup for this repository
|
|
175
|
+
*
|
|
176
|
+
* @throws Never throws - all errors are caught and returned as 'failed' results
|
|
177
|
+
*/
|
|
178
|
+
private _ensureWebhookForRepo;
|
|
179
|
+
/**
|
|
180
|
+
* Find a webhook matching the given URL.
|
|
181
|
+
*
|
|
182
|
+
* Uses case-insensitive URL comparison. No URL normalization is performed -
|
|
183
|
+
* simple string matching after lowercasing both URLs. This means that
|
|
184
|
+
* 'https://smee.io/ABC' and 'https://smee.io/abc' are considered equal,
|
|
185
|
+
* but trailing slashes or different protocols would not match.
|
|
186
|
+
*
|
|
187
|
+
* @param webhooks - Array of webhooks to search through
|
|
188
|
+
* @param targetUrl - The URL to match (typically the Smee channel URL)
|
|
189
|
+
* @returns The first matching webhook, or undefined if none found
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```typescript
|
|
193
|
+
* const webhooks = [
|
|
194
|
+
* { id: 1, active: true, config: { url: 'https://smee.io/ABC' }, events: ['issues'] }
|
|
195
|
+
* ];
|
|
196
|
+
* const match = this._findMatchingWebhook(webhooks, 'https://smee.io/abc');
|
|
197
|
+
* // Returns the webhook with id: 1
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
private _findMatchingWebhook;
|
|
201
|
+
/**
|
|
202
|
+
* List webhooks for a repository via GitHub API.
|
|
203
|
+
*
|
|
204
|
+
* Uses the `gh` CLI to call `GET /repos/{owner}/{repo}/hooks`.
|
|
205
|
+
* Errors are logged and an empty array is returned for graceful degradation.
|
|
206
|
+
*
|
|
207
|
+
* @param owner - Repository owner (organization or user)
|
|
208
|
+
* @param repo - Repository name
|
|
209
|
+
* @returns Promise resolving to an array of webhooks (empty array on error)
|
|
210
|
+
*
|
|
211
|
+
* @see {@link https://docs.github.com/en/rest/webhooks/repos#list-repository-webhooks}
|
|
212
|
+
*/
|
|
213
|
+
private _listRepoWebhooks;
|
|
214
|
+
/**
|
|
215
|
+
* Create a new webhook for a repository via GitHub API.
|
|
216
|
+
*
|
|
217
|
+
* Uses the `gh` CLI to call `POST /repos/{owner}/{repo}/hooks` with:
|
|
218
|
+
* - `config.url`: The Smee channel URL
|
|
219
|
+
* - `config.content_type`: 'json'
|
|
220
|
+
* - `events`: ['issues']
|
|
221
|
+
* - `active`: true
|
|
222
|
+
*
|
|
223
|
+
* @param owner - Repository owner (organization or user)
|
|
224
|
+
* @param repo - Repository name
|
|
225
|
+
* @param smeeChannelUrl - The Smee channel URL to configure
|
|
226
|
+
* @returns Promise resolving to the webhook ID on success
|
|
227
|
+
* @throws Error if webhook creation fails (caught and logged by caller)
|
|
228
|
+
*
|
|
229
|
+
* @see {@link https://docs.github.com/en/rest/webhooks/repos#create-a-repository-webhook}
|
|
230
|
+
*/
|
|
231
|
+
private _createRepoWebhook;
|
|
232
|
+
/**
|
|
233
|
+
* Update an existing webhook (reactivate and/or merge events) via GitHub API.
|
|
234
|
+
*
|
|
235
|
+
* Uses the `gh` CLI to call `PATCH /repos/{owner}/{repo}/hooks/{webhookId}`.
|
|
236
|
+
* When reactivating an inactive webhook, this method also merges the 'issues'
|
|
237
|
+
* event with any existing events to preserve the webhook's original configuration.
|
|
238
|
+
*
|
|
239
|
+
* @param owner - Repository owner (organization or user)
|
|
240
|
+
* @param repo - Repository name
|
|
241
|
+
* @param webhookId - Webhook ID to update
|
|
242
|
+
* @param updates - Updates to apply
|
|
243
|
+
* @param updates.active - Set webhook active status (true to reactivate)
|
|
244
|
+
* @param updates.events - Event types to subscribe to (merged with existing)
|
|
245
|
+
* @returns Promise resolving to true on success
|
|
246
|
+
* @throws Error if webhook update fails (caught and logged by caller)
|
|
247
|
+
*
|
|
248
|
+
* @see {@link https://docs.github.com/en/rest/webhooks/repos#update-a-repository-webhook}
|
|
249
|
+
*/
|
|
250
|
+
private _updateRepoWebhook;
|
|
251
|
+
}
|
|
252
|
+
export {};
|
|
253
|
+
//# sourceMappingURL=webhook-setup-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-setup-service.d.ts","sourceRoot":"","sources":["../../src/services/webhook-setup-service.ts"],"names":[],"mappings":"AAYA;;;;GAIG;AACH,UAAU,MAAM;IACd,gCAAgC;IAChC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,wDAAwD;IACxD,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACtD,0BAA0B;IAC1B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,kDAAkD;IAClD,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACtD,wBAAwB;IACxB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gDAAgD;IAChD,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACxD;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,mDAAmD;IACnD,MAAM,EAAE,OAAO,CAAC;IAChB,4BAA4B;IAC5B,MAAM,EAAE;QACN,+CAA+C;QAC/C,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,2EAA2E;IAC3E,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,QAAQ,CAAC;IACzD,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC;;;;OAIG;gBACS,MAAM,EAAE,MAAM;IAI1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACG,cAAc,CAClB,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,gBAAgB,EAAE,GAC/B,OAAO,CAAC,mBAAmB,CAAC;IAuD/B;;;;;;;;;;;;;;;;;;;;OAoBG;YACW,qBAAqB;IA6HnC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,oBAAoB;IAY5B;;;;;;;;;;;OAWG;YACW,iBAAiB;IA0C/B;;;;;;;;;;;;;;;;OAgBG;YACW,kBAAkB;IA0BhC;;;;;;;;;;;;;;;;;OAiBG;YACW,kBAAkB;CAoCjC"}
|