@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,62 @@
|
|
|
1
|
+
import type { CreateDecisionRequest, DecisionQueueItem, DecisionResponse, DecisionResponseRequest, DecisionPriority, QueueQuery } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Message router interface (facade over internal message router)
|
|
4
|
+
* This will be implemented by the actual MessageRouter from #5
|
|
5
|
+
*/
|
|
6
|
+
export interface MessageRouter {
|
|
7
|
+
getQueue(query?: QueueQuery): Promise<DecisionQueueItem[]>;
|
|
8
|
+
getDecision(id: string): Promise<DecisionQueueItem | null>;
|
|
9
|
+
createDecision(request: CreateDecisionRequest): Promise<DecisionQueueItem>;
|
|
10
|
+
respondToDecision(id: string, response: DecisionResponseRequest, respondedBy: string): Promise<DecisionResponse>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* In-memory queue store for development/testing
|
|
14
|
+
*/
|
|
15
|
+
export declare class InMemoryQueueStore implements MessageRouter {
|
|
16
|
+
private queue;
|
|
17
|
+
private responses;
|
|
18
|
+
getQueue(query?: QueueQuery): Promise<DecisionQueueItem[]>;
|
|
19
|
+
getDecision(id: string): Promise<DecisionQueueItem | null>;
|
|
20
|
+
createDecision(request: CreateDecisionRequest): Promise<DecisionQueueItem>;
|
|
21
|
+
respondToDecision(id: string, request: DecisionResponseRequest, respondedBy: string): Promise<DecisionResponse>;
|
|
22
|
+
/**
|
|
23
|
+
* Add a decision to the queue (for testing)
|
|
24
|
+
*/
|
|
25
|
+
addDecision(decision: DecisionQueueItem): void;
|
|
26
|
+
/**
|
|
27
|
+
* Clear all decisions (for testing)
|
|
28
|
+
*/
|
|
29
|
+
clear(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Get queue size (for testing)
|
|
32
|
+
*/
|
|
33
|
+
size(): number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Queue service - facade over message router
|
|
37
|
+
*/
|
|
38
|
+
export declare class QueueService {
|
|
39
|
+
private router;
|
|
40
|
+
constructor(router: MessageRouter);
|
|
41
|
+
/**
|
|
42
|
+
* Get all pending decisions
|
|
43
|
+
*/
|
|
44
|
+
getQueue(query?: QueueQuery): Promise<DecisionQueueItem[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Create a new decision in the queue
|
|
47
|
+
*/
|
|
48
|
+
createDecision(request: CreateDecisionRequest): Promise<DecisionQueueItem>;
|
|
49
|
+
/**
|
|
50
|
+
* Get a specific decision by ID
|
|
51
|
+
*/
|
|
52
|
+
getDecision(id: string): Promise<DecisionQueueItem>;
|
|
53
|
+
/**
|
|
54
|
+
* Respond to a decision
|
|
55
|
+
*/
|
|
56
|
+
respond(id: string, request: DecisionResponseRequest, respondedBy: string): Promise<DecisionResponse>;
|
|
57
|
+
/**
|
|
58
|
+
* Get count of pending decisions by priority
|
|
59
|
+
*/
|
|
60
|
+
getQueueStats(): Promise<Record<DecisionPriority, number>>;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=queue-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-service.d.ts","sourceRoot":"","sources":["../../src/services/queue-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,EAChB,UAAU,EACX,MAAM,mBAAmB,CAAC;AAG3B;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAC3D,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IAC3D,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC3E,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAClH;AAED;;GAEG;AACH,qBAAa,kBAAmB,YAAW,aAAa;IACtD,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,SAAS,CAA4C;IAEvD,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IA6B1D,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAI1D,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkB1E,iBAAiB,CACrB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,uBAAuB,EAChC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,gBAAgB,CAAC;IA8B5B;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAI9C;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,IAAI,IAAI,MAAM;CAGf;AAED;;GAEG;AACH,qBAAa,YAAY;IACX,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAEzC;;OAEG;IACG,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAIhE;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAIhF;;OAEG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAQzD;;OAEG;IACG,OAAO,CACX,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,uBAAuB,EAChC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,gBAAgB,CAAC;IAI5B;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;CAcjE"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Errors } from '../middleware/error-handler.js';
|
|
2
|
+
/**
|
|
3
|
+
* In-memory queue store for development/testing
|
|
4
|
+
*/
|
|
5
|
+
export class InMemoryQueueStore {
|
|
6
|
+
queue = new Map();
|
|
7
|
+
responses = new Map();
|
|
8
|
+
async getQueue(query) {
|
|
9
|
+
let items = Array.from(this.queue.values());
|
|
10
|
+
// Filter by priority
|
|
11
|
+
if (query?.priority) {
|
|
12
|
+
items = items.filter((item) => item.priority === query.priority);
|
|
13
|
+
}
|
|
14
|
+
// Filter by workflow
|
|
15
|
+
if (query?.workflowId) {
|
|
16
|
+
items = items.filter((item) => item.workflowId === query.workflowId);
|
|
17
|
+
}
|
|
18
|
+
// Sort by priority (blocking_now first) and creation date
|
|
19
|
+
const priorityOrder = {
|
|
20
|
+
blocking_now: 0,
|
|
21
|
+
blocking_soon: 1,
|
|
22
|
+
when_available: 2,
|
|
23
|
+
};
|
|
24
|
+
items.sort((a, b) => {
|
|
25
|
+
const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
26
|
+
if (priorityDiff !== 0)
|
|
27
|
+
return priorityDiff;
|
|
28
|
+
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
|
29
|
+
});
|
|
30
|
+
return items;
|
|
31
|
+
}
|
|
32
|
+
async getDecision(id) {
|
|
33
|
+
return this.queue.get(id) ?? null;
|
|
34
|
+
}
|
|
35
|
+
async createDecision(request) {
|
|
36
|
+
const item = {
|
|
37
|
+
id: crypto.randomUUID(),
|
|
38
|
+
workflowId: request.workflowId,
|
|
39
|
+
stepId: request.stepId,
|
|
40
|
+
type: request.type,
|
|
41
|
+
prompt: request.prompt,
|
|
42
|
+
options: request.options,
|
|
43
|
+
context: request.context,
|
|
44
|
+
priority: request.priority,
|
|
45
|
+
createdAt: new Date().toISOString(),
|
|
46
|
+
expiresAt: request.expiresAt ?? null,
|
|
47
|
+
};
|
|
48
|
+
this.queue.set(item.id, item);
|
|
49
|
+
return item;
|
|
50
|
+
}
|
|
51
|
+
async respondToDecision(id, request, respondedBy) {
|
|
52
|
+
const decision = this.queue.get(id);
|
|
53
|
+
if (!decision) {
|
|
54
|
+
throw Errors.notFound(`Decision ${id} not found`);
|
|
55
|
+
}
|
|
56
|
+
// Check if already responded
|
|
57
|
+
if (this.responses.has(id)) {
|
|
58
|
+
throw Errors.conflict(`Decision ${id} has already been responded to`);
|
|
59
|
+
}
|
|
60
|
+
// Check expiration
|
|
61
|
+
if (decision.expiresAt && new Date(decision.expiresAt) < new Date()) {
|
|
62
|
+
throw Errors.conflict(`Decision ${id} has expired`);
|
|
63
|
+
}
|
|
64
|
+
const response = {
|
|
65
|
+
id,
|
|
66
|
+
response: request.response,
|
|
67
|
+
comment: request.comment,
|
|
68
|
+
respondedBy,
|
|
69
|
+
respondedAt: new Date().toISOString(),
|
|
70
|
+
};
|
|
71
|
+
this.responses.set(id, response);
|
|
72
|
+
this.queue.delete(id);
|
|
73
|
+
return response;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Add a decision to the queue (for testing)
|
|
77
|
+
*/
|
|
78
|
+
addDecision(decision) {
|
|
79
|
+
this.queue.set(decision.id, decision);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Clear all decisions (for testing)
|
|
83
|
+
*/
|
|
84
|
+
clear() {
|
|
85
|
+
this.queue.clear();
|
|
86
|
+
this.responses.clear();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get queue size (for testing)
|
|
90
|
+
*/
|
|
91
|
+
size() {
|
|
92
|
+
return this.queue.size;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Queue service - facade over message router
|
|
97
|
+
*/
|
|
98
|
+
export class QueueService {
|
|
99
|
+
router;
|
|
100
|
+
constructor(router) {
|
|
101
|
+
this.router = router;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get all pending decisions
|
|
105
|
+
*/
|
|
106
|
+
async getQueue(query) {
|
|
107
|
+
return this.router.getQueue(query);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Create a new decision in the queue
|
|
111
|
+
*/
|
|
112
|
+
async createDecision(request) {
|
|
113
|
+
return this.router.createDecision(request);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get a specific decision by ID
|
|
117
|
+
*/
|
|
118
|
+
async getDecision(id) {
|
|
119
|
+
const decision = await this.router.getDecision(id);
|
|
120
|
+
if (!decision) {
|
|
121
|
+
throw Errors.notFound(`Decision ${id} not found`);
|
|
122
|
+
}
|
|
123
|
+
return decision;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Respond to a decision
|
|
127
|
+
*/
|
|
128
|
+
async respond(id, request, respondedBy) {
|
|
129
|
+
return this.router.respondToDecision(id, request, respondedBy);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get count of pending decisions by priority
|
|
133
|
+
*/
|
|
134
|
+
async getQueueStats() {
|
|
135
|
+
const items = await this.router.getQueue();
|
|
136
|
+
const stats = {
|
|
137
|
+
blocking_now: 0,
|
|
138
|
+
blocking_soon: 0,
|
|
139
|
+
when_available: 0,
|
|
140
|
+
};
|
|
141
|
+
for (const item of items) {
|
|
142
|
+
stats[item.priority]++;
|
|
143
|
+
}
|
|
144
|
+
return stats;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=queue-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-service.js","sourceRoot":"","sources":["../../src/services/queue-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Redis } from 'ioredis';
|
|
2
|
+
import type { QueueItem, QueueItemWithScore, QueueManager } from '../types/index.js';
|
|
3
|
+
import type { DispatchConfig } from '../config/index.js';
|
|
4
|
+
interface Logger {
|
|
5
|
+
info(msg: string): void;
|
|
6
|
+
info(obj: Record<string, unknown>, msg: string): void;
|
|
7
|
+
warn(msg: string): void;
|
|
8
|
+
warn(obj: Record<string, unknown>, msg: string): void;
|
|
9
|
+
error(msg: string): void;
|
|
10
|
+
error(obj: Record<string, unknown>, msg: string): void;
|
|
11
|
+
debug(msg: string): void;
|
|
12
|
+
debug(obj: Record<string, unknown>, msg: string): void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Redis sorted-set backed queue adapter implementing QueueManager.
|
|
16
|
+
* Uses ZADD for priority ordering and a Lua script for atomic claim.
|
|
17
|
+
* Gracefully degrades on Redis errors (logs warnings, doesn't crash).
|
|
18
|
+
*/
|
|
19
|
+
export declare class RedisQueueAdapter implements QueueManager {
|
|
20
|
+
private readonly redis;
|
|
21
|
+
private readonly logger;
|
|
22
|
+
private readonly maxRetries;
|
|
23
|
+
private claimCommandDefined;
|
|
24
|
+
constructor(redis: Redis, logger: Logger, config?: Pick<DispatchConfig, 'maxRetries'>);
|
|
25
|
+
private ensureClaimCommand;
|
|
26
|
+
enqueue(item: QueueItem): Promise<void>;
|
|
27
|
+
claim(workerId: string): Promise<QueueItem | null>;
|
|
28
|
+
release(workerId: string, item: QueueItem): Promise<void>;
|
|
29
|
+
complete(workerId: string, item: QueueItem): Promise<void>;
|
|
30
|
+
getQueueDepth(): Promise<number>;
|
|
31
|
+
getQueueItems(offset: number, limit: number): Promise<QueueItemWithScore[]>;
|
|
32
|
+
getActiveWorkerCount(): Promise<number>;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=redis-queue-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-queue-adapter.d.ts","sourceRoot":"","sources":["../../src/services/redis-queue-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAC1G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AA6BzD,UAAU,MAAM;IACd,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACtD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACvD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACxD;AAcD;;;;GAIG;AACH,qBAAa,iBAAkB,YAAW,YAAY;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,mBAAmB,CAAS;gBAExB,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC;IAMrF,OAAO,CAAC,kBAAkB;IASpB,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBvC,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA4ClD,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAmDzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB1D,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAShC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAsC3E,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;CA2B9C"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
const PENDING_KEY = 'orchestrator:queue:pending';
|
|
2
|
+
const CLAIMED_KEY_PREFIX = 'orchestrator:queue:claimed:';
|
|
3
|
+
const HEARTBEAT_KEY_PREFIX = 'orchestrator:worker:';
|
|
4
|
+
const DEAD_LETTER_KEY = 'orchestrator:queue:dead-letter';
|
|
5
|
+
/**
|
|
6
|
+
* Lua script for atomic claim: ZPOPMIN + HSET claimed + SET heartbeat.
|
|
7
|
+
* KEYS[1] = pending sorted set
|
|
8
|
+
* KEYS[2] = claimed hash for this worker
|
|
9
|
+
* KEYS[3] = heartbeat key for this worker
|
|
10
|
+
* ARGV[1] = heartbeat TTL in seconds
|
|
11
|
+
*
|
|
12
|
+
* Returns the serialized item string, or nil if queue is empty.
|
|
13
|
+
*/
|
|
14
|
+
const CLAIM_SCRIPT = `
|
|
15
|
+
local result = redis.call('ZPOPMIN', KEYS[1], 1)
|
|
16
|
+
if #result == 0 then
|
|
17
|
+
return nil
|
|
18
|
+
end
|
|
19
|
+
local member = result[1]
|
|
20
|
+
local parsed = cjson.decode(member)
|
|
21
|
+
local itemKey = parsed.itemKey
|
|
22
|
+
redis.call('HSET', KEYS[2], itemKey, member)
|
|
23
|
+
redis.call('SET', KEYS[3], '1', 'EX', tonumber(ARGV[1]))
|
|
24
|
+
return member
|
|
25
|
+
`;
|
|
26
|
+
function buildItemKey(item) {
|
|
27
|
+
return `${item.owner}/${item.repo}#${item.issueNumber}`;
|
|
28
|
+
}
|
|
29
|
+
function buildClaimedKey(workerId) {
|
|
30
|
+
return `${CLAIMED_KEY_PREFIX}${workerId}`;
|
|
31
|
+
}
|
|
32
|
+
function buildHeartbeatKey(workerId) {
|
|
33
|
+
return `${HEARTBEAT_KEY_PREFIX}${workerId}:heartbeat`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Redis sorted-set backed queue adapter implementing QueueManager.
|
|
37
|
+
* Uses ZADD for priority ordering and a Lua script for atomic claim.
|
|
38
|
+
* Gracefully degrades on Redis errors (logs warnings, doesn't crash).
|
|
39
|
+
*/
|
|
40
|
+
export class RedisQueueAdapter {
|
|
41
|
+
redis;
|
|
42
|
+
logger;
|
|
43
|
+
maxRetries;
|
|
44
|
+
claimCommandDefined = false;
|
|
45
|
+
constructor(redis, logger, config) {
|
|
46
|
+
this.redis = redis;
|
|
47
|
+
this.logger = logger;
|
|
48
|
+
this.maxRetries = config?.maxRetries ?? 3;
|
|
49
|
+
}
|
|
50
|
+
ensureClaimCommand() {
|
|
51
|
+
if (this.claimCommandDefined)
|
|
52
|
+
return;
|
|
53
|
+
this.redis.defineCommand('claimItem', {
|
|
54
|
+
numberOfKeys: 3,
|
|
55
|
+
lua: CLAIM_SCRIPT,
|
|
56
|
+
});
|
|
57
|
+
this.claimCommandDefined = true;
|
|
58
|
+
}
|
|
59
|
+
async enqueue(item) {
|
|
60
|
+
const itemKey = buildItemKey(item);
|
|
61
|
+
const serialized = {
|
|
62
|
+
...item,
|
|
63
|
+
attemptCount: 0,
|
|
64
|
+
itemKey,
|
|
65
|
+
};
|
|
66
|
+
try {
|
|
67
|
+
await this.redis.zadd(PENDING_KEY, item.priority, JSON.stringify(serialized));
|
|
68
|
+
this.logger.info({ owner: item.owner, repo: item.repo, issue: item.issueNumber, priority: item.priority }, 'Item enqueued to Redis sorted set');
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
this.logger.warn({ err: error, itemKey }, 'Redis error in enqueue, item not added to queue');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async claim(workerId) {
|
|
75
|
+
this.ensureClaimCommand();
|
|
76
|
+
const pendingKey = PENDING_KEY;
|
|
77
|
+
const claimedKey = buildClaimedKey(workerId);
|
|
78
|
+
const heartbeatKey = buildHeartbeatKey(workerId);
|
|
79
|
+
const ttlSeconds = Math.ceil(30000 / 1000); // Default; actual TTL managed by dispatcher's heartbeat refresh
|
|
80
|
+
try {
|
|
81
|
+
const result = await this.redis.claimItem(pendingKey, claimedKey, heartbeatKey, ttlSeconds);
|
|
82
|
+
if (!result) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const serialized = JSON.parse(result);
|
|
86
|
+
this.logger.info({ workerId, itemKey: serialized.itemKey, attempt: serialized.attemptCount }, 'Item claimed from queue');
|
|
87
|
+
return {
|
|
88
|
+
owner: serialized.owner,
|
|
89
|
+
repo: serialized.repo,
|
|
90
|
+
issueNumber: serialized.issueNumber,
|
|
91
|
+
workflowName: serialized.workflowName,
|
|
92
|
+
command: serialized.command,
|
|
93
|
+
priority: serialized.priority,
|
|
94
|
+
enqueuedAt: serialized.enqueuedAt,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
this.logger.warn({ err: error, workerId }, 'Redis error in claim, returning null');
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async release(workerId, item) {
|
|
103
|
+
const itemKey = buildItemKey(item);
|
|
104
|
+
const claimedKey = buildClaimedKey(workerId);
|
|
105
|
+
const heartbeatKey = buildHeartbeatKey(workerId);
|
|
106
|
+
try {
|
|
107
|
+
// Get the claimed item to check attempt count
|
|
108
|
+
const claimedRaw = await this.redis.hget(claimedKey, itemKey);
|
|
109
|
+
let attemptCount = 0;
|
|
110
|
+
if (claimedRaw) {
|
|
111
|
+
const parsed = JSON.parse(claimedRaw);
|
|
112
|
+
attemptCount = parsed.attemptCount + 1;
|
|
113
|
+
}
|
|
114
|
+
// Clean up claimed hash and heartbeat
|
|
115
|
+
await this.redis.hdel(claimedKey, itemKey);
|
|
116
|
+
await this.redis.del(heartbeatKey);
|
|
117
|
+
if (attemptCount >= this.maxRetries) {
|
|
118
|
+
// Dead-letter: too many retries
|
|
119
|
+
const deadLetterItem = {
|
|
120
|
+
...item,
|
|
121
|
+
attemptCount,
|
|
122
|
+
itemKey,
|
|
123
|
+
};
|
|
124
|
+
await this.redis.zadd(DEAD_LETTER_KEY, Date.now(), JSON.stringify(deadLetterItem));
|
|
125
|
+
this.logger.warn({ workerId, itemKey, attemptCount, maxRetries: this.maxRetries }, 'Item dead-lettered after max retries');
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
// Re-queue with original priority
|
|
129
|
+
const requeueItem = {
|
|
130
|
+
...item,
|
|
131
|
+
attemptCount,
|
|
132
|
+
itemKey,
|
|
133
|
+
};
|
|
134
|
+
await this.redis.zadd(PENDING_KEY, item.priority, JSON.stringify(requeueItem));
|
|
135
|
+
this.logger.info({ workerId, itemKey, attemptCount }, 'Item released back to pending queue');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
this.logger.warn({ err: error, workerId, itemKey }, 'Redis error in release');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async complete(workerId, item) {
|
|
143
|
+
const itemKey = buildItemKey(item);
|
|
144
|
+
const claimedKey = buildClaimedKey(workerId);
|
|
145
|
+
const heartbeatKey = buildHeartbeatKey(workerId);
|
|
146
|
+
try {
|
|
147
|
+
await this.redis.hdel(claimedKey, itemKey);
|
|
148
|
+
await this.redis.del(heartbeatKey);
|
|
149
|
+
this.logger.info({ workerId, itemKey }, 'Item completed and removed from claimed set');
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
this.logger.warn({ err: error, workerId, itemKey }, 'Redis error in complete');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async getQueueDepth() {
|
|
156
|
+
try {
|
|
157
|
+
return await this.redis.zcard(PENDING_KEY);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
this.logger.warn({ err: error }, 'Redis error in getQueueDepth');
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async getQueueItems(offset, limit) {
|
|
165
|
+
try {
|
|
166
|
+
const results = await this.redis.zrange(PENDING_KEY, offset, offset + limit - 1, 'WITHSCORES');
|
|
167
|
+
const items = [];
|
|
168
|
+
// Results come as [member, score, member, score, ...]
|
|
169
|
+
for (let i = 0; i + 1 < results.length; i += 2) {
|
|
170
|
+
const member = results[i];
|
|
171
|
+
const scoreStr = results[i + 1];
|
|
172
|
+
if (!member || !scoreStr)
|
|
173
|
+
continue;
|
|
174
|
+
const serialized = JSON.parse(member);
|
|
175
|
+
const score = parseFloat(scoreStr);
|
|
176
|
+
items.push({
|
|
177
|
+
item: {
|
|
178
|
+
owner: serialized.owner,
|
|
179
|
+
repo: serialized.repo,
|
|
180
|
+
issueNumber: serialized.issueNumber,
|
|
181
|
+
workflowName: serialized.workflowName,
|
|
182
|
+
command: serialized.command,
|
|
183
|
+
priority: serialized.priority,
|
|
184
|
+
enqueuedAt: serialized.enqueuedAt,
|
|
185
|
+
},
|
|
186
|
+
score,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return items;
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
this.logger.warn({ err: error }, 'Redis error in getQueueItems');
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async getActiveWorkerCount() {
|
|
197
|
+
try {
|
|
198
|
+
// Scan for all claimed hash keys and sum their lengths
|
|
199
|
+
const keys = [];
|
|
200
|
+
let cursor = '0';
|
|
201
|
+
do {
|
|
202
|
+
const [nextCursor, matchedKeys] = await this.redis.scan(cursor, 'MATCH', `${CLAIMED_KEY_PREFIX}*`, 'COUNT', 100);
|
|
203
|
+
cursor = nextCursor;
|
|
204
|
+
keys.push(...matchedKeys);
|
|
205
|
+
} while (cursor !== '0');
|
|
206
|
+
let count = 0;
|
|
207
|
+
for (const key of keys) {
|
|
208
|
+
count += await this.redis.hlen(key);
|
|
209
|
+
}
|
|
210
|
+
return count;
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
this.logger.warn({ err: error }, 'Redis error in getActiveWorkerCount');
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=redis-queue-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-queue-adapter.js","sourceRoot":"","sources":["../../src/services/redis-queue-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smee.io webhook receiver for GitHub events.
|
|
3
|
+
*
|
|
4
|
+
* Connects to a smee.io channel URL via Server-Sent Events (SSE) using
|
|
5
|
+
* the native fetch API. Receives GitHub webhook payloads forwarded by
|
|
6
|
+
* smee.io and feeds them into the LabelMonitorService for processing.
|
|
7
|
+
*
|
|
8
|
+
* This eliminates the need for polling when a smee channel is configured,
|
|
9
|
+
* providing near-instant label event detection with zero GitHub API calls.
|
|
10
|
+
*/
|
|
11
|
+
import type { LabelMonitorService } from './label-monitor-service.js';
|
|
12
|
+
interface Logger {
|
|
13
|
+
info(msg: string): void;
|
|
14
|
+
info(obj: Record<string, unknown>, msg: string): void;
|
|
15
|
+
warn(msg: string): void;
|
|
16
|
+
warn(obj: Record<string, unknown>, msg: string): void;
|
|
17
|
+
error(msg: string): void;
|
|
18
|
+
error(obj: Record<string, unknown>, msg: string): void;
|
|
19
|
+
}
|
|
20
|
+
export interface SmeeReceiverOptions {
|
|
21
|
+
/** The smee.io channel URL */
|
|
22
|
+
channelUrl: string;
|
|
23
|
+
/** Set of "owner/repo" strings to filter events */
|
|
24
|
+
watchedRepos: Set<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Base delay before reconnecting after disconnect (ms).
|
|
27
|
+
* Uses exponential backoff: 5s → 10s → 20s → 40s → 80s → 160s → 300s (capped).
|
|
28
|
+
* Resets to base delay on successful connection.
|
|
29
|
+
* @default 5000
|
|
30
|
+
*/
|
|
31
|
+
baseReconnectDelayMs?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Receives GitHub webhook events from a smee.io channel via SSE.
|
|
35
|
+
* Feeds label events directly into the LabelMonitorService.
|
|
36
|
+
*/
|
|
37
|
+
export declare class SmeeWebhookReceiver {
|
|
38
|
+
private readonly logger;
|
|
39
|
+
private readonly monitorService;
|
|
40
|
+
private static readonly BASE_RECONNECT_DELAY_MS;
|
|
41
|
+
private static readonly MAX_BACKOFF_MS;
|
|
42
|
+
private readonly channelUrl;
|
|
43
|
+
private readonly watchedRepos;
|
|
44
|
+
private readonly baseReconnectDelayMs;
|
|
45
|
+
private abortController;
|
|
46
|
+
private running;
|
|
47
|
+
private reconnectAttempt;
|
|
48
|
+
constructor(logger: Logger, monitorService: LabelMonitorService, options: SmeeReceiverOptions);
|
|
49
|
+
/**
|
|
50
|
+
* Start listening to the smee.io channel.
|
|
51
|
+
* Automatically reconnects on disconnect.
|
|
52
|
+
*/
|
|
53
|
+
start(): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Stop the receiver.
|
|
56
|
+
*/
|
|
57
|
+
stop(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Connect to the smee.io channel and process events.
|
|
60
|
+
*/
|
|
61
|
+
private connect;
|
|
62
|
+
/**
|
|
63
|
+
* Parse and process a single SSE event block from smee.io.
|
|
64
|
+
*
|
|
65
|
+
* Smee.io SSE events have the format:
|
|
66
|
+
* event: message
|
|
67
|
+
* data: {"body": <webhook-payload>, "x-github-event": "issues", ...}
|
|
68
|
+
*/
|
|
69
|
+
private processSSEEvent;
|
|
70
|
+
/**
|
|
71
|
+
* Get the current reconnect delay based on the number of attempts.
|
|
72
|
+
* Uses exponential backoff with the current attempt count.
|
|
73
|
+
*/
|
|
74
|
+
private get reconnectDelayMs();
|
|
75
|
+
/**
|
|
76
|
+
* Calculate exponential backoff delay for reconnection attempts.
|
|
77
|
+
* Formula: BASE_RECONNECT_DELAY_MS * 2^attempt, capped at MAX_BACKOFF_MS.
|
|
78
|
+
* Progression: 5s → 10s → 20s → 40s → 80s → 160s → 300s (capped).
|
|
79
|
+
*
|
|
80
|
+
* @param attempt - The current reconnection attempt number (0-indexed)
|
|
81
|
+
* @returns Delay in milliseconds before next reconnection attempt
|
|
82
|
+
*/
|
|
83
|
+
private calculateBackoffDelay;
|
|
84
|
+
private sleep;
|
|
85
|
+
}
|
|
86
|
+
export {};
|
|
87
|
+
//# sourceMappingURL=smee-receiver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smee-receiver.d.ts","sourceRoot":"","sources":["../../src/services/smee-receiver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGtE,UAAU,MAAM;IACd,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACtD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACxD;AAED,MAAM,WAAW,mBAAmB;IAClC,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;GAGG;AACH,qBAAa,mBAAmB;IAY5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAZjC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAQ;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAU;IAEhD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAAK;gBAGV,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,mBAAmB,EACpD,OAAO,EAAE,mBAAmB;IAO9B;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0C5B;;OAEG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;YACW,OAAO;IA0CrB;;;;;;OAMG;YACW,eAAe;IA2G7B;;;OAGG;IACH,OAAO,KAAK,gBAAgB,GAE3B;IAED;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,KAAK;CAcd"}
|