@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.
Files changed (274) hide show
  1. package/LICENSE +191 -0
  2. package/dist/auth/api-key.d.ts +63 -0
  3. package/dist/auth/api-key.d.ts.map +1 -0
  4. package/dist/auth/api-key.js +102 -0
  5. package/dist/auth/api-key.js.map +1 -0
  6. package/dist/auth/github-oauth.d.ts +29 -0
  7. package/dist/auth/github-oauth.d.ts.map +1 -0
  8. package/dist/auth/github-oauth.js +136 -0
  9. package/dist/auth/github-oauth.js.map +1 -0
  10. package/dist/auth/index.d.ts +5 -0
  11. package/dist/auth/index.d.ts.map +1 -0
  12. package/dist/auth/index.js +9 -0
  13. package/dist/auth/index.js.map +1 -0
  14. package/dist/auth/jwt.d.ts +34 -0
  15. package/dist/auth/jwt.d.ts.map +1 -0
  16. package/dist/auth/jwt.js +74 -0
  17. package/dist/auth/jwt.js.map +1 -0
  18. package/dist/auth/middleware.d.ts +43 -0
  19. package/dist/auth/middleware.d.ts.map +1 -0
  20. package/dist/auth/middleware.js +101 -0
  21. package/dist/auth/middleware.js.map +1 -0
  22. package/dist/config/index.d.ts +3 -0
  23. package/dist/config/index.d.ts.map +1 -0
  24. package/dist/config/index.js +5 -0
  25. package/dist/config/index.js.map +1 -0
  26. package/dist/config/loader.d.ts +21 -0
  27. package/dist/config/loader.d.ts.map +1 -0
  28. package/dist/config/loader.js +295 -0
  29. package/dist/config/loader.js.map +1 -0
  30. package/dist/config/schema.d.ts +733 -0
  31. package/dist/config/schema.d.ts.map +1 -0
  32. package/dist/config/schema.js +174 -0
  33. package/dist/config/schema.js.map +1 -0
  34. package/dist/index.d.ts +24 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +33 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/middleware/error-handler.d.ts +30 -0
  39. package/dist/middleware/error-handler.d.ts.map +1 -0
  40. package/dist/middleware/error-handler.js +173 -0
  41. package/dist/middleware/error-handler.js.map +1 -0
  42. package/dist/middleware/index.d.ts +4 -0
  43. package/dist/middleware/index.d.ts.map +1 -0
  44. package/dist/middleware/index.js +4 -0
  45. package/dist/middleware/index.js.map +1 -0
  46. package/dist/middleware/rate-limit.d.ts +32 -0
  47. package/dist/middleware/rate-limit.d.ts.map +1 -0
  48. package/dist/middleware/rate-limit.js +92 -0
  49. package/dist/middleware/rate-limit.js.map +1 -0
  50. package/dist/middleware/request-logger.d.ts +31 -0
  51. package/dist/middleware/request-logger.d.ts.map +1 -0
  52. package/dist/middleware/request-logger.js +89 -0
  53. package/dist/middleware/request-logger.js.map +1 -0
  54. package/dist/routes/agents.d.ts +7 -0
  55. package/dist/routes/agents.d.ts.map +1 -0
  56. package/dist/routes/agents.js +78 -0
  57. package/dist/routes/agents.js.map +1 -0
  58. package/dist/routes/dispatch.d.ts +8 -0
  59. package/dist/routes/dispatch.d.ts.map +1 -0
  60. package/dist/routes/dispatch.js +46 -0
  61. package/dist/routes/dispatch.js.map +1 -0
  62. package/dist/routes/events.d.ts +14 -0
  63. package/dist/routes/events.d.ts.map +1 -0
  64. package/dist/routes/events.js +240 -0
  65. package/dist/routes/events.js.map +1 -0
  66. package/dist/routes/health.d.ts +14 -0
  67. package/dist/routes/health.d.ts.map +1 -0
  68. package/dist/routes/health.js +132 -0
  69. package/dist/routes/health.js.map +1 -0
  70. package/dist/routes/index.d.ts +36 -0
  71. package/dist/routes/index.d.ts.map +1 -0
  72. package/dist/routes/index.js +34 -0
  73. package/dist/routes/index.js.map +1 -0
  74. package/dist/routes/integrations.d.ts +34 -0
  75. package/dist/routes/integrations.d.ts.map +1 -0
  76. package/dist/routes/integrations.js +81 -0
  77. package/dist/routes/integrations.js.map +1 -0
  78. package/dist/routes/metrics.d.ts +31 -0
  79. package/dist/routes/metrics.d.ts.map +1 -0
  80. package/dist/routes/metrics.js +157 -0
  81. package/dist/routes/metrics.js.map +1 -0
  82. package/dist/routes/pr-webhooks.d.ts +15 -0
  83. package/dist/routes/pr-webhooks.d.ts.map +1 -0
  84. package/dist/routes/pr-webhooks.js +114 -0
  85. package/dist/routes/pr-webhooks.js.map +1 -0
  86. package/dist/routes/queue.d.ts +7 -0
  87. package/dist/routes/queue.d.ts.map +1 -0
  88. package/dist/routes/queue.js +94 -0
  89. package/dist/routes/queue.js.map +1 -0
  90. package/dist/routes/webhooks.d.ts +15 -0
  91. package/dist/routes/webhooks.d.ts.map +1 -0
  92. package/dist/routes/webhooks.js +117 -0
  93. package/dist/routes/webhooks.js.map +1 -0
  94. package/dist/routes/workflows.d.ts +7 -0
  95. package/dist/routes/workflows.d.ts.map +1 -0
  96. package/dist/routes/workflows.js +138 -0
  97. package/dist/routes/workflows.js.map +1 -0
  98. package/dist/server.d.ts +35 -0
  99. package/dist/server.d.ts.map +1 -0
  100. package/dist/server.js +264 -0
  101. package/dist/server.js.map +1 -0
  102. package/dist/services/agent-registry.d.ts +89 -0
  103. package/dist/services/agent-registry.d.ts.map +1 -0
  104. package/dist/services/agent-registry.js +173 -0
  105. package/dist/services/agent-registry.js.map +1 -0
  106. package/dist/services/epic-completion-monitor-service.d.ts +69 -0
  107. package/dist/services/epic-completion-monitor-service.d.ts.map +1 -0
  108. package/dist/services/epic-completion-monitor-service.js +200 -0
  109. package/dist/services/epic-completion-monitor-service.js.map +1 -0
  110. package/dist/services/identity.d.ts +41 -0
  111. package/dist/services/identity.d.ts.map +1 -0
  112. package/dist/services/identity.js +91 -0
  113. package/dist/services/identity.js.map +1 -0
  114. package/dist/services/index.d.ts +13 -0
  115. package/dist/services/index.d.ts.map +1 -0
  116. package/dist/services/index.js +13 -0
  117. package/dist/services/index.js.map +1 -0
  118. package/dist/services/label-monitor-service.d.ts +95 -0
  119. package/dist/services/label-monitor-service.d.ts.map +1 -0
  120. package/dist/services/label-monitor-service.js +414 -0
  121. package/dist/services/label-monitor-service.js.map +1 -0
  122. package/dist/services/label-sync-service.d.ts +76 -0
  123. package/dist/services/label-sync-service.d.ts.map +1 -0
  124. package/dist/services/label-sync-service.js +108 -0
  125. package/dist/services/label-sync-service.js.map +1 -0
  126. package/dist/services/phase-tracker-service.d.ts +32 -0
  127. package/dist/services/phase-tracker-service.d.ts.map +1 -0
  128. package/dist/services/phase-tracker-service.js +82 -0
  129. package/dist/services/phase-tracker-service.js.map +1 -0
  130. package/dist/services/pr-feedback-monitor-service.d.ts +86 -0
  131. package/dist/services/pr-feedback-monitor-service.d.ts.map +1 -0
  132. package/dist/services/pr-feedback-monitor-service.js +423 -0
  133. package/dist/services/pr-feedback-monitor-service.js.map +1 -0
  134. package/dist/services/queue-service.d.ts +62 -0
  135. package/dist/services/queue-service.d.ts.map +1 -0
  136. package/dist/services/queue-service.js +147 -0
  137. package/dist/services/queue-service.js.map +1 -0
  138. package/dist/services/redis-queue-adapter.d.ts +35 -0
  139. package/dist/services/redis-queue-adapter.d.ts.map +1 -0
  140. package/dist/services/redis-queue-adapter.js +218 -0
  141. package/dist/services/redis-queue-adapter.js.map +1 -0
  142. package/dist/services/smee-receiver.d.ts +87 -0
  143. package/dist/services/smee-receiver.d.ts.map +1 -0
  144. package/dist/services/smee-receiver.js +225 -0
  145. package/dist/services/smee-receiver.js.map +1 -0
  146. package/dist/services/webhook-setup-service.d.ts +253 -0
  147. package/dist/services/webhook-setup-service.d.ts.map +1 -0
  148. package/dist/services/webhook-setup-service.js +369 -0
  149. package/dist/services/webhook-setup-service.js.map +1 -0
  150. package/dist/services/worker-dispatcher.d.ts +57 -0
  151. package/dist/services/worker-dispatcher.d.ts.map +1 -0
  152. package/dist/services/worker-dispatcher.js +211 -0
  153. package/dist/services/worker-dispatcher.js.map +1 -0
  154. package/dist/services/workflow-service.d.ts +65 -0
  155. package/dist/services/workflow-service.d.ts.map +1 -0
  156. package/dist/services/workflow-service.js +164 -0
  157. package/dist/services/workflow-service.js.map +1 -0
  158. package/dist/sse/events.d.ts +50 -0
  159. package/dist/sse/events.d.ts.map +1 -0
  160. package/dist/sse/events.js +152 -0
  161. package/dist/sse/events.js.map +1 -0
  162. package/dist/sse/index.d.ts +4 -0
  163. package/dist/sse/index.d.ts.map +1 -0
  164. package/dist/sse/index.js +7 -0
  165. package/dist/sse/index.js.map +1 -0
  166. package/dist/sse/stream.d.ts +109 -0
  167. package/dist/sse/stream.d.ts.map +1 -0
  168. package/dist/sse/stream.js +239 -0
  169. package/dist/sse/stream.js.map +1 -0
  170. package/dist/sse/subscriptions.d.ts +91 -0
  171. package/dist/sse/subscriptions.d.ts.map +1 -0
  172. package/dist/sse/subscriptions.js +299 -0
  173. package/dist/sse/subscriptions.js.map +1 -0
  174. package/dist/types/api.d.ts +651 -0
  175. package/dist/types/api.d.ts.map +1 -0
  176. package/dist/types/api.js +169 -0
  177. package/dist/types/api.js.map +1 -0
  178. package/dist/types/index.d.ts +6 -0
  179. package/dist/types/index.d.ts.map +1 -0
  180. package/dist/types/index.js +27 -0
  181. package/dist/types/index.js.map +1 -0
  182. package/dist/types/monitor.d.ts +243 -0
  183. package/dist/types/monitor.d.ts.map +1 -0
  184. package/dist/types/monitor.js +2 -0
  185. package/dist/types/monitor.js.map +1 -0
  186. package/dist/types/problem-details.d.ts +63 -0
  187. package/dist/types/problem-details.d.ts.map +1 -0
  188. package/dist/types/problem-details.js +37 -0
  189. package/dist/types/problem-details.js.map +1 -0
  190. package/dist/types/sse.d.ts +224 -0
  191. package/dist/types/sse.d.ts.map +1 -0
  192. package/dist/types/sse.js +103 -0
  193. package/dist/types/sse.js.map +1 -0
  194. package/dist/types/webhook.d.ts +59 -0
  195. package/dist/types/webhook.d.ts.map +1 -0
  196. package/dist/types/webhook.js +2 -0
  197. package/dist/types/webhook.js.map +1 -0
  198. package/dist/utils/correlation.d.ts +30 -0
  199. package/dist/utils/correlation.d.ts.map +1 -0
  200. package/dist/utils/correlation.js +34 -0
  201. package/dist/utils/correlation.js.map +1 -0
  202. package/dist/utils/index.d.ts +3 -0
  203. package/dist/utils/index.d.ts.map +1 -0
  204. package/dist/utils/index.js +3 -0
  205. package/dist/utils/index.js.map +1 -0
  206. package/dist/utils/shutdown.d.ts +28 -0
  207. package/dist/utils/shutdown.d.ts.map +1 -0
  208. package/dist/utils/shutdown.js +86 -0
  209. package/dist/utils/shutdown.js.map +1 -0
  210. package/dist/worker/claude-cli-worker.d.ts +61 -0
  211. package/dist/worker/claude-cli-worker.d.ts.map +1 -0
  212. package/dist/worker/claude-cli-worker.js +330 -0
  213. package/dist/worker/claude-cli-worker.js.map +1 -0
  214. package/dist/worker/cli-spawner.d.ts +42 -0
  215. package/dist/worker/cli-spawner.d.ts.map +1 -0
  216. package/dist/worker/cli-spawner.js +183 -0
  217. package/dist/worker/cli-spawner.js.map +1 -0
  218. package/dist/worker/config.d.ts +76 -0
  219. package/dist/worker/config.d.ts.map +1 -0
  220. package/dist/worker/config.js +39 -0
  221. package/dist/worker/config.js.map +1 -0
  222. package/dist/worker/epic-post-tasks.d.ts +73 -0
  223. package/dist/worker/epic-post-tasks.d.ts.map +1 -0
  224. package/dist/worker/epic-post-tasks.js +266 -0
  225. package/dist/worker/epic-post-tasks.js.map +1 -0
  226. package/dist/worker/gate-checker.d.ts +22 -0
  227. package/dist/worker/gate-checker.d.ts.map +1 -0
  228. package/dist/worker/gate-checker.js +41 -0
  229. package/dist/worker/gate-checker.js.map +1 -0
  230. package/dist/worker/index.d.ts +13 -0
  231. package/dist/worker/index.d.ts.map +1 -0
  232. package/dist/worker/index.js +19 -0
  233. package/dist/worker/index.js.map +1 -0
  234. package/dist/worker/label-manager.d.ts +89 -0
  235. package/dist/worker/label-manager.d.ts.map +1 -0
  236. package/dist/worker/label-manager.js +200 -0
  237. package/dist/worker/label-manager.js.map +1 -0
  238. package/dist/worker/output-capture.d.ts +58 -0
  239. package/dist/worker/output-capture.d.ts.map +1 -0
  240. package/dist/worker/output-capture.js +153 -0
  241. package/dist/worker/output-capture.js.map +1 -0
  242. package/dist/worker/phase-loop.d.ts +64 -0
  243. package/dist/worker/phase-loop.d.ts.map +1 -0
  244. package/dist/worker/phase-loop.js +185 -0
  245. package/dist/worker/phase-loop.js.map +1 -0
  246. package/dist/worker/phase-resolver.d.ts +62 -0
  247. package/dist/worker/phase-resolver.d.ts.map +1 -0
  248. package/dist/worker/phase-resolver.js +144 -0
  249. package/dist/worker/phase-resolver.js.map +1 -0
  250. package/dist/worker/pr-feedback-handler.d.ts +105 -0
  251. package/dist/worker/pr-feedback-handler.d.ts.map +1 -0
  252. package/dist/worker/pr-feedback-handler.js +391 -0
  253. package/dist/worker/pr-feedback-handler.js.map +1 -0
  254. package/dist/worker/pr-linker.d.ts +69 -0
  255. package/dist/worker/pr-linker.d.ts.map +1 -0
  256. package/dist/worker/pr-linker.js +108 -0
  257. package/dist/worker/pr-linker.js.map +1 -0
  258. package/dist/worker/pr-manager.d.ts +61 -0
  259. package/dist/worker/pr-manager.d.ts.map +1 -0
  260. package/dist/worker/pr-manager.js +140 -0
  261. package/dist/worker/pr-manager.js.map +1 -0
  262. package/dist/worker/repo-checkout.d.ts +63 -0
  263. package/dist/worker/repo-checkout.d.ts.map +1 -0
  264. package/dist/worker/repo-checkout.js +160 -0
  265. package/dist/worker/repo-checkout.js.map +1 -0
  266. package/dist/worker/stage-comment-manager.d.ts +34 -0
  267. package/dist/worker/stage-comment-manager.d.ts.map +1 -0
  268. package/dist/worker/stage-comment-manager.js +114 -0
  269. package/dist/worker/stage-comment-manager.js.map +1 -0
  270. package/dist/worker/types.d.ts +192 -0
  271. package/dist/worker/types.d.ts.map +1 -0
  272. package/dist/worker/types.js +53 -0
  273. package/dist/worker/types.js.map +1 -0
  274. package/package.json +64 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase-tracker-service.d.ts","sourceRoot":"","sources":["../../src/services/phase-tracker-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAItD,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,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,YAAY;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,mBAAmB;IAM9E,OAAO,CAAC,QAAQ;IAIV,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAuBxF,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB/E,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAyB7F,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAkB9F"}
@@ -0,0 +1,82 @@
1
+ const DEFAULT_TTL_SECONDS = 86400; // 24 hours
2
+ /**
3
+ * Redis-backed phase tracker for deduplication.
4
+ * Uses SET with TTL to prevent duplicate processing of the same issue+phase.
5
+ * Gracefully degrades when Redis is unavailable (treats as "not duplicate").
6
+ */
7
+ export class PhaseTrackerService {
8
+ redis;
9
+ logger;
10
+ ttlSeconds;
11
+ constructor(logger, redis, options) {
12
+ this.logger = logger;
13
+ this.redis = redis;
14
+ this.ttlSeconds = options?.ttlSeconds ?? DEFAULT_TTL_SECONDS;
15
+ }
16
+ buildKey(owner, repo, issue, phase) {
17
+ return `phase-tracker:${owner}:${repo}:${issue}:${phase}`;
18
+ }
19
+ async isDuplicate(owner, repo, issue, phase) {
20
+ if (!this.redis) {
21
+ this.logger.warn('Redis unavailable for phase tracker, treating as not duplicate');
22
+ return false;
23
+ }
24
+ const key = this.buildKey(owner, repo, issue, phase);
25
+ try {
26
+ const exists = await this.redis.exists(key);
27
+ if (exists) {
28
+ this.logger.info({ key, owner, repo, issue, phase }, 'Duplicate event detected');
29
+ }
30
+ return exists === 1;
31
+ }
32
+ catch (error) {
33
+ this.logger.warn({ err: error, key }, 'Redis error in isDuplicate, treating as not duplicate');
34
+ return false;
35
+ }
36
+ }
37
+ async clear(owner, repo, issue, phase) {
38
+ if (!this.redis) {
39
+ return;
40
+ }
41
+ const key = this.buildKey(owner, repo, issue, phase);
42
+ try {
43
+ await this.redis.del(key);
44
+ this.logger.info({ key }, 'Cleared dedup key');
45
+ }
46
+ catch (error) {
47
+ this.logger.warn({ err: error, key }, 'Redis error in clear, deduplication may block this event');
48
+ }
49
+ }
50
+ async tryMarkProcessed(owner, repo, issue, phase) {
51
+ if (!this.redis) {
52
+ this.logger.warn('Redis unavailable for phase tracker, treating as not duplicate');
53
+ return true;
54
+ }
55
+ const key = this.buildKey(owner, repo, issue, phase);
56
+ try {
57
+ const result = await this.redis.set(key, '1', 'EX', this.ttlSeconds, 'NX');
58
+ const won = result === 'OK';
59
+ this.logger.info({ key, owner, repo, issue, phase, won }, won ? 'Atomically marked phase as processed' : 'Duplicate detected (atomic check)');
60
+ return won;
61
+ }
62
+ catch (error) {
63
+ this.logger.warn({ err: error, key }, 'Redis error in tryMarkProcessed, treating as not duplicate');
64
+ return true;
65
+ }
66
+ }
67
+ async markProcessed(owner, repo, issue, phase) {
68
+ if (!this.redis) {
69
+ this.logger.warn('Redis unavailable for phase tracker, skipping markProcessed');
70
+ return;
71
+ }
72
+ const key = this.buildKey(owner, repo, issue, phase);
73
+ try {
74
+ await this.redis.set(key, '1', 'EX', this.ttlSeconds);
75
+ this.logger.info({ key, ttl: this.ttlSeconds }, 'Marked phase as processed');
76
+ }
77
+ catch (error) {
78
+ this.logger.warn({ err: error, key }, 'Redis error in markProcessed, deduplication may not work for this event');
79
+ }
80
+ }
81
+ }
82
+ //# sourceMappingURL=phase-tracker-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase-tracker-service.js","sourceRoot":"","sources":["../../src/services/phase-tracker-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import type { GitHubClientFactory } from '@generacy-ai/workflow-engine';
2
+ import type { MonitorState, QueueAdapter, PhaseTracker, PrReviewEvent } from '../types/monitor.js';
3
+ import type { RepositoryConfig, PrMonitorConfig } from '../config/schema.js';
4
+ import type { Logger } from '../worker/types.js';
5
+ export interface PrFeedbackMonitorOptions {
6
+ repositories: RepositoryConfig[];
7
+ pollIntervalMs: number;
8
+ adaptivePolling: boolean;
9
+ maxConcurrentPolls: number;
10
+ }
11
+ /**
12
+ * PR feedback monitor service that detects unresolved review comments
13
+ * on PRs linked to orchestrated issues and triggers the feedback-addressing flow.
14
+ *
15
+ * Uses a hybrid webhook + polling architecture mirroring LabelMonitorService.
16
+ */
17
+ export declare class PrFeedbackMonitorService {
18
+ private readonly logger;
19
+ private readonly createClient;
20
+ private readonly phaseTracker;
21
+ private readonly queueAdapter;
22
+ private readonly options;
23
+ private readonly prLinker;
24
+ private readonly clusterGithubUsername;
25
+ private abortController;
26
+ private state;
27
+ constructor(logger: Logger, createClient: GitHubClientFactory, phaseTracker: PhaseTracker, queueAdapter: QueueAdapter, config: PrMonitorConfig, repositories: RepositoryConfig[], clusterGithubUsername?: string);
28
+ /**
29
+ * Process a PR review event: link PR to issue, check for unresolved threads,
30
+ * deduplicate, and enqueue feedback-addressing work.
31
+ *
32
+ * Shared by both webhook and polling paths.
33
+ *
34
+ * @returns true if feedback was enqueued, false if skipped or duplicate
35
+ */
36
+ processPrReviewEvent(event: PrReviewEvent): Promise<boolean>;
37
+ /**
38
+ * Start the polling loop.
39
+ */
40
+ startPolling(): Promise<void>;
41
+ /**
42
+ * Stop the polling loop.
43
+ */
44
+ stopPolling(): void;
45
+ /**
46
+ * Run a single poll cycle across all watched repositories.
47
+ * Lists open PRs in each repo, checks for unresolved review threads.
48
+ */
49
+ poll(): Promise<void>;
50
+ /**
51
+ * Poll a single repository for PRs with unresolved review threads.
52
+ * Lists open PRs and processes each through the standard event flow.
53
+ */
54
+ private pollRepo;
55
+ /**
56
+ * FR-015: Deduplicate PRs that link to the same issue, keeping only the
57
+ * most recently updated PR per issue. Uses lightweight body/branch parsing
58
+ * (no API calls) to determine candidate issue numbers.
59
+ *
60
+ * PRs that don't resolve to any issue number are kept as-is (they'll be
61
+ * filtered out later by `processPrReviewEvent` when linking fails).
62
+ */
63
+ private deduplicatePrsByIssue;
64
+ /**
65
+ * Check if an error is a GitHub API rate limit error.
66
+ */
67
+ private isRateLimitError;
68
+ /**
69
+ * Record that a webhook event was received, updating health tracking.
70
+ */
71
+ recordWebhookEvent(): void;
72
+ /**
73
+ * Update adaptive polling interval based on webhook health.
74
+ */
75
+ private updateAdaptivePolling;
76
+ getState(): Readonly<MonitorState>;
77
+ /**
78
+ * Resolve workflow name from the issue's labels.
79
+ * Checks workflow:* labels first (authoritative, set by label monitor on process events),
80
+ * then falls back to process:* / completed:* / agent:* for pre-migration issues.
81
+ * Falls back to 'unknown' if no workflow label is found.
82
+ */
83
+ private resolveWorkflowName;
84
+ private sleep;
85
+ }
86
+ //# sourceMappingURL=pr-feedback-monitor-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-feedback-monitor-service.d.ts","sourceRoot":"","sources":["../../src/services/pr-feedback-monitor-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EACZ,YAAY,EAEZ,aAAa,EAEd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE7E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAYD;;;;;GAKG;AACH,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAC3D,OAAO,CAAC,eAAe,CAAgC;IAEvD,OAAO,CAAC,KAAK,CAAe;gBAG1B,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,mBAAmB,EACjC,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,eAAe,EACvB,YAAY,EAAE,gBAAgB,EAAE,EAChC,qBAAqB,CAAC,EAAE,MAAM;IA4BhC;;;;;;;OAOG;IACG,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IA0IlE;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAoCnC;;OAEG;IACH,WAAW,IAAI,IAAI;IAQnB;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;OAGG;YACW,QAAQ;IAsDtB;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAwD7B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAYxB;;OAEG;IACH,kBAAkB,IAAI,IAAI;IAc1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA2B7B,QAAQ,IAAI,QAAQ,CAAC,YAAY,CAAC;IAQlC;;;;;OAKG;YACW,mBAAmB;IA0CjC,OAAO,CAAC,KAAK;CAkBd"}
@@ -0,0 +1,423 @@
1
+ import { PrLinker } from '../worker/pr-linker.js';
2
+ const WAITING_FOR_PR_FEEDBACK_LABEL = 'waiting-for:address-pr-feedback';
3
+ const DEDUP_PHASE = 'address-pr-feedback';
4
+ const MIN_POLL_INTERVAL_MS = 10000;
5
+ /**
6
+ * Adaptive polling divisor for PR feedback monitor.
7
+ * Per US4: "Polling interval decreases by 50%" → divide by 2.
8
+ * This differs from LabelMonitorService which uses ADAPTIVE_DIVISOR = 3.
9
+ */
10
+ const ADAPTIVE_DIVISOR = 2;
11
+ /**
12
+ * PR feedback monitor service that detects unresolved review comments
13
+ * on PRs linked to orchestrated issues and triggers the feedback-addressing flow.
14
+ *
15
+ * Uses a hybrid webhook + polling architecture mirroring LabelMonitorService.
16
+ */
17
+ export class PrFeedbackMonitorService {
18
+ logger;
19
+ createClient;
20
+ phaseTracker;
21
+ queueAdapter;
22
+ options;
23
+ prLinker;
24
+ clusterGithubUsername;
25
+ abortController = null;
26
+ state;
27
+ constructor(logger, createClient, phaseTracker, queueAdapter, config, repositories, clusterGithubUsername) {
28
+ this.logger = logger;
29
+ this.createClient = createClient;
30
+ this.phaseTracker = phaseTracker;
31
+ this.queueAdapter = queueAdapter;
32
+ this.clusterGithubUsername = clusterGithubUsername;
33
+ this.options = {
34
+ repositories,
35
+ pollIntervalMs: config.pollIntervalMs,
36
+ adaptivePolling: config.adaptivePolling,
37
+ maxConcurrentPolls: config.maxConcurrentPolls,
38
+ };
39
+ this.prLinker = new PrLinker(logger);
40
+ this.state = {
41
+ isPolling: false,
42
+ webhookHealthy: true,
43
+ lastWebhookEvent: null,
44
+ currentPollIntervalMs: config.pollIntervalMs,
45
+ basePollIntervalMs: config.pollIntervalMs,
46
+ };
47
+ }
48
+ // ==========================================================================
49
+ // PR Review Event Processing
50
+ // ==========================================================================
51
+ /**
52
+ * Process a PR review event: link PR to issue, check for unresolved threads,
53
+ * deduplicate, and enqueue feedback-addressing work.
54
+ *
55
+ * Shared by both webhook and polling paths.
56
+ *
57
+ * @returns true if feedback was enqueued, false if skipped or duplicate
58
+ */
59
+ async processPrReviewEvent(event) {
60
+ const { owner, repo, prNumber, prBody, branchName, source } = event;
61
+ this.logger.info({ owner, repo, prNumber, source }, `Processing PR review event from ${source}`);
62
+ const client = this.createClient();
63
+ // 1. Link PR to orchestrated issue
64
+ const prInput = {
65
+ number: prNumber,
66
+ body: prBody,
67
+ head: { ref: branchName },
68
+ };
69
+ const link = await this.prLinker.linkPrToIssue(client, owner, repo, prInput);
70
+ if (!link) {
71
+ this.logger.debug({ owner, repo, prNumber }, 'PR not linked to an orchestrated issue — skipping');
72
+ return false;
73
+ }
74
+ const { issueNumber, linkMethod, assignees } = link;
75
+ // 2. Assignee check — skip PR feedback for issues not assigned to this cluster
76
+ // Uses assignees returned by PrLinker to avoid a duplicate getIssue() call
77
+ if (this.clusterGithubUsername) {
78
+ if (assignees.length === 0) {
79
+ this.logger.warn({ owner, repo, issueNumber, prNumber }, 'Skipping PR feedback: linked issue has no assignees');
80
+ return false;
81
+ }
82
+ if (!assignees.includes(this.clusterGithubUsername)) {
83
+ this.logger.debug({ owner, repo, issueNumber, prNumber, assignees }, 'Skipping PR feedback: linked issue not assigned to this cluster');
84
+ return false;
85
+ }
86
+ if (assignees.length > 1) {
87
+ this.logger.warn({ owner, repo, issueNumber, assignees }, 'Issue has multiple assignees — may be processed by multiple clusters');
88
+ }
89
+ }
90
+ // 3. Fetch review comments and filter for unresolved threads
91
+ let unresolvedThreadIds;
92
+ try {
93
+ const comments = await client.getPRComments(owner, repo, prNumber);
94
+ // Filter for root-level unresolved comments (not replies)
95
+ const unresolvedComments = comments.filter((c) => c.resolved === false && !c.in_reply_to_id);
96
+ unresolvedThreadIds = unresolvedComments.map((c) => c.id);
97
+ }
98
+ catch (error) {
99
+ this.logger.error({ err: error, owner, repo, prNumber }, 'Failed to fetch PR comments');
100
+ return false;
101
+ }
102
+ if (unresolvedThreadIds.length === 0) {
103
+ this.logger.debug({ owner, repo, prNumber, issueNumber }, 'No unresolved review threads — skipping');
104
+ return false;
105
+ }
106
+ this.logger.info({ owner, repo, prNumber, issueNumber, linkMethod, unresolvedCount: unresolvedThreadIds.length }, `Found ${unresolvedThreadIds.length} unresolved review thread(s)`);
107
+ // 4. Atomic deduplication via tryMarkProcessed (SET NX)
108
+ const isNew = await this.phaseTracker.tryMarkProcessed(owner, repo, issueNumber, DEDUP_PHASE);
109
+ if (!isNew) {
110
+ this.logger.info({ owner, repo, issueNumber, prNumber }, 'Skipping duplicate — PR feedback already enqueued for this issue');
111
+ return false;
112
+ }
113
+ // 5. Resolve workflow name from issue labels
114
+ const workflowName = await this.resolveWorkflowName(owner, repo, issueNumber);
115
+ // 6. Build and enqueue queue item
116
+ const metadata = {
117
+ prNumber,
118
+ reviewThreadIds: unresolvedThreadIds,
119
+ };
120
+ const queueItem = {
121
+ owner,
122
+ repo,
123
+ issueNumber,
124
+ workflowName,
125
+ command: 'address-pr-feedback',
126
+ priority: Date.now(),
127
+ enqueuedAt: new Date().toISOString(),
128
+ metadata: metadata,
129
+ };
130
+ await this.queueAdapter.enqueue(queueItem);
131
+ this.logger.info({ owner, repo, issueNumber, prNumber, command: queueItem.command }, 'PR feedback work enqueued');
132
+ // 7. Add waiting-for label to issue
133
+ try {
134
+ await client.addLabels(owner, repo, issueNumber, [WAITING_FOR_PR_FEEDBACK_LABEL]);
135
+ }
136
+ catch (error) {
137
+ this.logger.warn({ err: error, owner, repo, issueNumber }, 'Failed to add waiting-for:address-pr-feedback label');
138
+ }
139
+ return true;
140
+ }
141
+ // ==========================================================================
142
+ // Polling
143
+ // ==========================================================================
144
+ /**
145
+ * Start the polling loop.
146
+ */
147
+ async startPolling() {
148
+ if (this.state.isPolling) {
149
+ this.logger.warn('PR feedback polling already running');
150
+ return;
151
+ }
152
+ const ac = new AbortController();
153
+ this.abortController = ac;
154
+ this.state.isPolling = true;
155
+ this.logger.info({ intervalMs: this.state.currentPollIntervalMs, repos: this.options.repositories.length }, 'Starting PR feedback monitor polling');
156
+ while (!ac.signal.aborted) {
157
+ try {
158
+ await this.poll();
159
+ }
160
+ catch (error) {
161
+ this.logger.error({ err: error }, 'Error during PR feedback poll cycle');
162
+ }
163
+ // Update adaptive polling before sleeping
164
+ if (this.options.adaptivePolling) {
165
+ this.updateAdaptivePolling();
166
+ }
167
+ await this.sleep(this.state.currentPollIntervalMs, ac.signal);
168
+ }
169
+ this.state.isPolling = false;
170
+ this.logger.info('PR feedback polling loop stopped');
171
+ }
172
+ /**
173
+ * Stop the polling loop.
174
+ */
175
+ stopPolling() {
176
+ if (this.abortController) {
177
+ this.abortController.abort();
178
+ this.abortController = null;
179
+ this.logger.info('PR feedback polling stop requested');
180
+ }
181
+ }
182
+ /**
183
+ * Run a single poll cycle across all watched repositories.
184
+ * Lists open PRs in each repo, checks for unresolved review threads.
185
+ */
186
+ async poll() {
187
+ const repos = this.options.repositories;
188
+ if (repos.length === 0)
189
+ return;
190
+ // Use semaphore pattern for concurrency limiting
191
+ const semaphore = new Semaphore(this.options.maxConcurrentPolls);
192
+ const pollTasks = repos.map(({ owner, repo }) => semaphore.acquire().then(async (release) => {
193
+ try {
194
+ await this.pollRepo(owner, repo);
195
+ }
196
+ finally {
197
+ release();
198
+ }
199
+ }));
200
+ await Promise.allSettled(pollTasks);
201
+ }
202
+ /**
203
+ * Poll a single repository for PRs with unresolved review threads.
204
+ * Lists open PRs and processes each through the standard event flow.
205
+ */
206
+ async pollRepo(owner, repo) {
207
+ const client = this.createClient();
208
+ let openPRs;
209
+ try {
210
+ openPRs = await client.listOpenPullRequests(owner, repo);
211
+ }
212
+ catch (error) {
213
+ if (this.isRateLimitError(error)) {
214
+ this.logger.warn({ owner, repo }, 'GitHub API rate limit hit while listing open PRs — skipping repo this cycle');
215
+ return;
216
+ }
217
+ this.logger.error({ err: error, owner, repo }, 'Error polling repository for open PRs');
218
+ return;
219
+ }
220
+ // FR-015: When multiple PRs exist for the same issue, process only the
221
+ // most recently updated PR. Use a lightweight pre-link pass (body/branch
222
+ // parsing only, no API calls) to group PRs by candidate issue number.
223
+ const prsToProcess = this.deduplicatePrsByIssue(owner, repo, openPRs);
224
+ for (const pr of prsToProcess) {
225
+ const event = {
226
+ owner,
227
+ repo,
228
+ prNumber: pr.number,
229
+ prBody: pr.body ?? '',
230
+ branchName: pr.head.ref,
231
+ source: 'poll',
232
+ };
233
+ try {
234
+ await this.processPrReviewEvent(event);
235
+ }
236
+ catch (error) {
237
+ if (this.isRateLimitError(error)) {
238
+ this.logger.warn({ owner, repo, prNumber: pr.number }, 'GitHub API rate limit hit while processing PR — stopping repo poll');
239
+ return;
240
+ }
241
+ this.logger.error({ err: error, owner, repo, prNumber: pr.number }, 'Error processing PR during poll');
242
+ }
243
+ }
244
+ }
245
+ /**
246
+ * FR-015: Deduplicate PRs that link to the same issue, keeping only the
247
+ * most recently updated PR per issue. Uses lightweight body/branch parsing
248
+ * (no API calls) to determine candidate issue numbers.
249
+ *
250
+ * PRs that don't resolve to any issue number are kept as-is (they'll be
251
+ * filtered out later by `processPrReviewEvent` when linking fails).
252
+ */
253
+ deduplicatePrsByIssue(owner, repo, prs) {
254
+ // Group PRs by candidate issue number
255
+ const issueGroups = new Map();
256
+ const unlinked = [];
257
+ for (const pr of prs) {
258
+ const candidateIssue = this.prLinker.parsePrBody(pr.body) ??
259
+ this.prLinker.parseBranchName(pr.head.ref);
260
+ if (candidateIssue === null) {
261
+ unlinked.push(pr);
262
+ continue;
263
+ }
264
+ const group = issueGroups.get(candidateIssue);
265
+ if (group) {
266
+ group.push(pr);
267
+ }
268
+ else {
269
+ issueGroups.set(candidateIssue, [pr]);
270
+ }
271
+ }
272
+ const result = [...unlinked];
273
+ for (const [issueNumber, group] of issueGroups) {
274
+ if (group.length === 1) {
275
+ result.push(group[0]);
276
+ continue;
277
+ }
278
+ // Sort by updated_at descending — most recent first
279
+ group.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime());
280
+ const mostRecent = group[0];
281
+ result.push(mostRecent);
282
+ // Log warning for skipped older PRs
283
+ const skipped = group.slice(1);
284
+ for (const skippedPr of skipped) {
285
+ this.logger.warn({ owner, repo, issueNumber, skippedPrNumber: skippedPr.number, processedPrNumber: mostRecent.number }, `Skipping older PR #${skippedPr.number} for issue #${issueNumber} — processing most recent PR #${mostRecent.number}`);
286
+ }
287
+ }
288
+ return result;
289
+ }
290
+ /**
291
+ * Check if an error is a GitHub API rate limit error.
292
+ */
293
+ isRateLimitError(error) {
294
+ if (error instanceof Error) {
295
+ const msg = error.message.toLowerCase();
296
+ return msg.includes('rate limit') || msg.includes('api rate') || msg.includes('403');
297
+ }
298
+ return false;
299
+ }
300
+ // ==========================================================================
301
+ // Adaptive Polling
302
+ // ==========================================================================
303
+ /**
304
+ * Record that a webhook event was received, updating health tracking.
305
+ */
306
+ recordWebhookEvent() {
307
+ this.state.lastWebhookEvent = Date.now();
308
+ const wasUnhealthy = !this.state.webhookHealthy;
309
+ this.state.webhookHealthy = true;
310
+ if (wasUnhealthy) {
311
+ this.state.currentPollIntervalMs = this.state.basePollIntervalMs;
312
+ this.logger.info({ intervalMs: this.state.currentPollIntervalMs }, 'Webhook reconnected, restoring normal PR feedback poll interval');
313
+ }
314
+ }
315
+ /**
316
+ * Update adaptive polling interval based on webhook health.
317
+ */
318
+ updateAdaptivePolling() {
319
+ if (this.state.lastWebhookEvent === null) {
320
+ // No webhook events yet — treat as healthy (no data, not unhealthy)
321
+ return;
322
+ }
323
+ const timeSinceLastWebhook = Date.now() - this.state.lastWebhookEvent;
324
+ const unhealthyThreshold = this.state.basePollIntervalMs * 2;
325
+ if (timeSinceLastWebhook > unhealthyThreshold && this.state.webhookHealthy) {
326
+ // Webhooks went unhealthy — increase poll frequency
327
+ this.state.webhookHealthy = false;
328
+ this.state.currentPollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, Math.floor(this.state.basePollIntervalMs / ADAPTIVE_DIVISOR));
329
+ this.logger.info({ intervalMs: this.state.currentPollIntervalMs, timeSinceLastWebhook }, 'Webhooks appear unhealthy, increasing PR feedback poll frequency');
330
+ }
331
+ }
332
+ // ==========================================================================
333
+ // State Access
334
+ // ==========================================================================
335
+ getState() {
336
+ return { ...this.state };
337
+ }
338
+ // ==========================================================================
339
+ // Utilities
340
+ // ==========================================================================
341
+ /**
342
+ * Resolve workflow name from the issue's labels.
343
+ * Checks workflow:* labels first (authoritative, set by label monitor on process events),
344
+ * then falls back to process:* / completed:* / agent:* for pre-migration issues.
345
+ * Falls back to 'unknown' if no workflow label is found.
346
+ */
347
+ async resolveWorkflowName(owner, repo, issueNumber) {
348
+ try {
349
+ const client = this.createClient();
350
+ const issue = await client.getIssue(owner, repo, issueNumber);
351
+ // Primary: persistent workflow:* label (authoritative)
352
+ for (const label of issue.labels) {
353
+ if (label.name.startsWith('workflow:')) {
354
+ return label.name.slice('workflow:'.length);
355
+ }
356
+ }
357
+ // Fallback: existing logic for pre-migration issues
358
+ for (const label of issue.labels) {
359
+ if (label.name.startsWith('process:')) {
360
+ return label.name.slice('process:'.length);
361
+ }
362
+ if (label.name.startsWith('completed:')) {
363
+ return label.name.slice('completed:'.length);
364
+ }
365
+ }
366
+ // Check agent:* labels for workflow name fallback
367
+ for (const label of issue.labels) {
368
+ if (label.name.startsWith('agent:') && label.name !== 'agent:in-progress' && label.name !== 'agent:error' && label.name !== 'agent:dispatched' && label.name !== 'agent:paused') {
369
+ return label.name.slice('agent:'.length);
370
+ }
371
+ }
372
+ }
373
+ catch (error) {
374
+ this.logger.warn({ err: error, owner, repo, issueNumber }, 'Failed to resolve workflow name from issue labels');
375
+ }
376
+ return 'unknown';
377
+ }
378
+ sleep(ms, signal) {
379
+ return new Promise((resolve) => {
380
+ if (signal.aborted) {
381
+ resolve();
382
+ return;
383
+ }
384
+ const timer = setTimeout(resolve, ms);
385
+ // Clean up timer if abort is signaled
386
+ const onAbort = () => {
387
+ clearTimeout(timer);
388
+ resolve();
389
+ };
390
+ signal.addEventListener('abort', onAbort, { once: true });
391
+ });
392
+ }
393
+ }
394
+ /**
395
+ * Simple semaphore for concurrency limiting.
396
+ */
397
+ class Semaphore {
398
+ count;
399
+ waiting = [];
400
+ constructor(max) {
401
+ this.count = max;
402
+ }
403
+ async acquire() {
404
+ if (this.count > 0) {
405
+ this.count--;
406
+ return () => this.release();
407
+ }
408
+ return new Promise((resolve) => {
409
+ this.waiting.push(() => {
410
+ this.count--;
411
+ resolve(() => this.release());
412
+ });
413
+ });
414
+ }
415
+ release() {
416
+ this.count++;
417
+ const next = this.waiting.shift();
418
+ if (next) {
419
+ next();
420
+ }
421
+ }
422
+ }
423
+ //# sourceMappingURL=pr-feedback-monitor-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-feedback-monitor-service.js","sourceRoot":"","sources":["../../src/services/pr-feedback-monitor-service.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,QAAQ,EAAoB,MAAM,wBAAwB,CAAC;AAUpE,MAAM,6BAA6B,GAAG,iCAAiC,CAAC;AACxE,MAAM,WAAW,GAAG,qBAAqB,CAAC;AAC1C,MAAM,oBAAoB,GAAG,KAAK,CAAC;AACnC;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B;;;;;GAKG;AACH,MAAM,OAAO,wBAAwB;IAClB,MAAM,CAAS;IACf,YAAY,CAAsB;IAClC,YAAY,CAAe;IAC3B,YAAY,CAAe;IAC3B,OAAO,CAA2B;IAClC,QAAQ,CAAW;IACnB,qBAAqB,CAAqB;IACnD,eAAe,GAA2B,IAAI,CAAC;IAE/C,KAAK,CAAe;IAE5B,YACE,MAAc,EACd,YAAiC,EACjC,YAA0B,EAC1B,YAA0B,EAC1B,MAAuB,EACvB,YAAgC,EAChC,qBAA8B;QAE9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,qBAAqB,GAAG,qBAAqB,CAAC;QACnD,IAAI,CAAC,OAAO,GAAG;YACb,YAAY;YACZ,cAAc,EAAE,MAAM,CAAC,cAAc;YACrC,eAAe,EAAE,MAAM,CAAC,eAAe;YACvC,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;SAC9C,CAAC;QACF,IAAI,CAAC,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;QAErC,IAAI,CAAC,KAAK,GAAG;YACX,SAAS,EAAE,KAAK;YAChB,cAAc,EAAE,IAAI;YACpB,gBAAgB,EAAE,IAAI;YACtB,qBAAqB,EAAE,MAAM,CAAC,cAAc;YAC5C,kBAAkB,EAAE,MAAM,CAAC,cAAc;SAC1C,CAAC;IACJ,CAAC;IAED,6EAA6E;IAC7E,6BAA6B;IAC7B,6EAA6E;IAE7E;;;;;;;OAOG;IACH,KAAK,CAAC,oBAAoB,CAAC,KAAoB;QAC7C,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;QAEpE,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EACjC,mCAAmC,MAAM,EAAE,CAC5C,CAAC;QAEF,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAEnC,mCAAmC;QACnC,MAAM,OAAO,GAAgB;YAC3B,MAAM,EAAE,QAAQ;YAChB,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE;SAC1B,CAAC;QAEF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAC7E,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EACzB,mDAAmD,CACpD,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;QAEpD,+EAA+E;QAC/E,8EAA8E;QAC9E,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC/B,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC3B,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,EACtC,qDAAqD,CACtD,CAAC;gBACF,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,qBAAqB,CAAC,EAAE,CAAC;gBACpD,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,EACjD,iEAAiE,CAClE,CAAC;gBACF,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzB,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,EACvC,sEAAsE,CACvE,CAAC;YACJ,CAAC;QACH,CAAC;QAED,6DAA6D;QAC7D,IAAI,mBAA6B,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;YACnE,0DAA0D;YAC1D,MAAM,kBAAkB,GAAG,QAAQ,CAAC,MAAM,CACxC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,cAAc,CACjD,CAAC;YACF,mBAAmB,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EACrC,6BAA6B,CAC9B,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,mBAAmB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,EACtC,yCAAyC,CAC1C,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,mBAAmB,CAAC,MAAM,EAAE,EAC/F,SAAS,mBAAmB,CAAC,MAAM,8BAA8B,CAClE,CAAC;QAEF,wDAAwD;QACxD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,gBAAgB,CACpD,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,CACtC,CAAC;QACF,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,EACtC,kEAAkE,CACnE,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,6CAA6C;QAC7C,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;QAE9E,kCAAkC;QAClC,MAAM,QAAQ,GAAuB;YACnC,QAAQ;YACR,eAAe,EAAE,mBAAmB;SACrC,CAAC;QAEF,MAAM,SAAS,GAAc;YAC3B,KAAK;YACL,IAAI;YACJ,WAAW;YACX,YAAY;YACZ,OAAO,EAAE,qBAAqB;YAC9B,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE;YACpB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACpC,QAAQ,EAAE,QAA8C;SACzD,CAAC;QAEF,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,EAClE,2BAA2B,CAC5B,CAAC;QAEF,oCAAoC;QACpC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,6BAA6B,CAAC,CAAC,CAAC;QACpF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,EACxC,qDAAqD,CACtD,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6EAA6E;IAC7E,UAAU;IACV,6EAA6E;IAE7E;;OAEG;IACH,KAAK,CAAC,YAAY;QAChB,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;YACxD,OAAO;QACT,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,CAAC;QACjC,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,EACzF,sCAAsC,CACvC,CAAC;QAEF,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YACpB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,EAAE,GAAG,EAAE,KAAK,EAAE,EACd,qCAAqC,CACtC,CAAC;YACJ,CAAC;YAED,0CAA0C;YAC1C,IAAI,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;gBACjC,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC/B,CAAC;YAED,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,qBAAqB,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;QAChE,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IACvD,CAAC;IAED;;OAEG;IACH,WAAW;QACT,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;YAC5B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI;QACR,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC;QACxC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAE/B,iDAAiD;QACjD,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QAEjE,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAC9C,SAAS,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YACzC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACnC,CAAC;oBAAS,CAAC;gBACT,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CACH,CAAC;QAEF,MAAM,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,QAAQ,CAAC,KAAa,EAAE,IAAY;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAEnC,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC3D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,KAAK,EAAE,IAAI,EAAE,EACf,6EAA6E,CAC9E,CAAC;gBACF,OAAO;YACT,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAC3B,uCAAuC,CACxC,CAAC;YACF,OAAO;QACT,CAAC;QAED,uEAAuE;QACvE,yEAAyE;QACzE,sEAAsE;QACtE,MAAM,YAAY,GAAG,IAAI,CAAC,qBAAqB,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAEtE,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;YAC9B,MAAM,KAAK,GAAkB;gBAC3B,KAAK;gBACL,IAAI;gBACJ,QAAQ,EAAE,EAAE,CAAC,MAAM;gBACnB,MAAM,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE;gBACrB,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG;gBACvB,MAAM,EAAE,MAAM;aACf,CAAC;YAEF,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;YACzC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;oBACjC,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,MAAM,EAAE,EACpC,oEAAoE,CACrE,CAAC;oBACF,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,MAAM,EAAE,EAChD,iCAAiC,CAClC,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACK,qBAAqB,CAC3B,KAAa,EACb,IAAY,EACZ,GAAuF;QAEvF,sCAAsC;QACtC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAsB,CAAC;QAClD,MAAM,QAAQ,GAAe,EAAE,CAAC;QAEhC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;YACrB,MAAM,cAAc,GAClB,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,IAAI,CAAC;gBAClC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAE7C,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;gBAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAClB,SAAS;YACX,CAAC;YAED,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YAC9C,IAAI,KAAK,EAAE,CAAC;gBACV,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjB,CAAC;iBAAM,CAAC;gBACN,WAAW,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAe,CAAC,GAAG,QAAQ,CAAC,CAAC;QAEzC,KAAK,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC;YAC/C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;gBACvB,SAAS;YACX,CAAC;YAED,oDAAoD;YACpD,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAClB,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CACpE,CAAC;YAEF,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAExB,oCAAoC;YACpC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC/B,KAAK,MAAM,SAAS,IAAI,OAAO,EAAE,CAAC;gBAChC,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,CAAC,MAAM,EAAE,iBAAiB,EAAE,UAAU,CAAC,MAAM,EAAE,EACrG,sBAAsB,SAAS,CAAC,MAAM,eAAe,WAAW,iCAAiC,UAAU,CAAC,MAAM,EAAE,CACrH,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,KAAc;QACrC,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACxC,OAAO,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,6EAA6E;IAC7E,mBAAmB;IACnB,6EAA6E;IAE7E;;OAEG;IACH,kBAAkB;QAChB,IAAI,CAAC,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzC,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC;QAChD,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;QAEjC,IAAI,YAAY,EAAE,CAAC;YACjB,IAAI,CAAC,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC;YACjE,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,qBAAqB,EAAE,EAChD,iEAAiE,CAClE,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACK,qBAAqB;QAC3B,IAAI,IAAI,CAAC,KAAK,CAAC,gBAAgB,KAAK,IAAI,EAAE,CAAC;YACzC,oEAAoE;YACpE,OAAO;QACT,CAAC;QAED,MAAM,oBAAoB,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC;QACtE,MAAM,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,GAAG,CAAC,CAAC;QAE7D,IAAI,oBAAoB,GAAG,kBAAkB,IAAI,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAC3E,oDAAoD;YACpD,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,KAAK,CAAC;YAClC,IAAI,CAAC,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC,GAAG,CACzC,oBAAoB,EACpB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,GAAG,gBAAgB,CAAC,CAC7D,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,qBAAqB,EAAE,oBAAoB,EAAE,EACtE,kEAAkE,CACnE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,eAAe;IACf,6EAA6E;IAE7E,QAAQ;QACN,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IAED,6EAA6E;IAC7E,YAAY;IACZ,6EAA6E;IAE7E;;;;;OAKG;IACK,KAAK,CAAC,mBAAmB,CAC/B,KAAa,EACb,IAAY,EACZ,WAAmB;QAEnB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;YAE9D,uDAAuD;YACvD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjC,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;oBACvC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;gBAC9C,CAAC;YACH,CAAC;YAED,oDAAoD;YACpD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjC,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;oBACtC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;gBAC7C,CAAC;gBACD,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;oBACxC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;gBAC/C,CAAC;YACH,CAAC;YAED,kDAAkD;YAClD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjC,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;oBAChL,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAC3C,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,EACxC,mDAAmD,CACpD,CAAC;QACJ,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAEO,KAAK,CAAC,EAAU,EAAE,MAAmB;QAC3C,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,OAAO,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YAED,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAEtC,sCAAsC;YACtC,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;YAEF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAED;;GAEG;AACH,MAAM,SAAS;IACL,KAAK,CAAS;IACd,OAAO,GAAsB,EAAE,CAAC;IAExC,YAAY,GAAW;QACrB,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAC9B,CAAC;QAED,OAAO,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,EAAE;YACzC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE;gBACrB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACb,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YAChC,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAClC,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,EAAE,CAAC;QACT,CAAC;IACH,CAAC;CACF"}