@herdctl/core 0.0.1

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 (520) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test.log +219 -0
  3. package/.turbo/turbo-typecheck.log +4 -0
  4. package/coverage/base.css +224 -0
  5. package/coverage/block-navigation.js +87 -0
  6. package/coverage/coverage-final.json +51 -0
  7. package/coverage/favicon.png +0 -0
  8. package/coverage/index.html +251 -0
  9. package/coverage/prettify.css +1 -0
  10. package/coverage/prettify.js +2 -0
  11. package/coverage/sort-arrow-sprite.png +0 -0
  12. package/coverage/sorter.js +210 -0
  13. package/coverage/src/config/index.html +191 -0
  14. package/coverage/src/config/index.ts.html +442 -0
  15. package/coverage/src/config/interpolate.ts.html +652 -0
  16. package/coverage/src/config/loader.ts.html +1501 -0
  17. package/coverage/src/config/merge.ts.html +823 -0
  18. package/coverage/src/config/parser.ts.html +1213 -0
  19. package/coverage/src/config/schema.ts.html +1123 -0
  20. package/coverage/src/fleet-manager/errors.ts.html +2326 -0
  21. package/coverage/src/fleet-manager/event-types.ts.html +1219 -0
  22. package/coverage/src/fleet-manager/fleet-manager.ts.html +7030 -0
  23. package/coverage/src/fleet-manager/index.html +206 -0
  24. package/coverage/src/fleet-manager/index.ts.html +469 -0
  25. package/coverage/src/fleet-manager/job-manager.ts.html +2074 -0
  26. package/coverage/src/fleet-manager/job-queue.ts.html +2479 -0
  27. package/coverage/src/fleet-manager/types.ts.html +2602 -0
  28. package/coverage/src/index.html +116 -0
  29. package/coverage/src/index.ts.html +181 -0
  30. package/coverage/src/runner/errors.ts.html +1006 -0
  31. package/coverage/src/runner/index.html +191 -0
  32. package/coverage/src/runner/index.ts.html +256 -0
  33. package/coverage/src/runner/job-executor.ts.html +1429 -0
  34. package/coverage/src/runner/message-processor.ts.html +1150 -0
  35. package/coverage/src/runner/sdk-adapter.ts.html +658 -0
  36. package/coverage/src/runner/types.ts.html +559 -0
  37. package/coverage/src/scheduler/errors.ts.html +388 -0
  38. package/coverage/src/scheduler/index.html +206 -0
  39. package/coverage/src/scheduler/index.ts.html +244 -0
  40. package/coverage/src/scheduler/interval.ts.html +652 -0
  41. package/coverage/src/scheduler/schedule-runner.ts.html +1411 -0
  42. package/coverage/src/scheduler/schedule-state.ts.html +718 -0
  43. package/coverage/src/scheduler/scheduler.ts.html +1795 -0
  44. package/coverage/src/scheduler/types.ts.html +733 -0
  45. package/coverage/src/state/directory.ts.html +736 -0
  46. package/coverage/src/state/errors.ts.html +376 -0
  47. package/coverage/src/state/fleet-state.ts.html +937 -0
  48. package/coverage/src/state/index.html +221 -0
  49. package/coverage/src/state/index.ts.html +322 -0
  50. package/coverage/src/state/job-metadata.ts.html +1420 -0
  51. package/coverage/src/state/job-output.ts.html +1033 -0
  52. package/coverage/src/state/schemas/fleet-state.ts.html +445 -0
  53. package/coverage/src/state/schemas/index.html +176 -0
  54. package/coverage/src/state/schemas/index.ts.html +286 -0
  55. package/coverage/src/state/schemas/job-metadata.ts.html +628 -0
  56. package/coverage/src/state/schemas/job-output.ts.html +616 -0
  57. package/coverage/src/state/schemas/session-info.ts.html +361 -0
  58. package/coverage/src/state/session.ts.html +844 -0
  59. package/coverage/src/state/types.ts.html +262 -0
  60. package/coverage/src/state/utils/atomic.ts.html +748 -0
  61. package/coverage/src/state/utils/index.html +146 -0
  62. package/coverage/src/state/utils/index.ts.html +103 -0
  63. package/coverage/src/state/utils/reads.ts.html +1621 -0
  64. package/coverage/src/work-sources/adapters/github.ts.html +3583 -0
  65. package/coverage/src/work-sources/adapters/index.html +131 -0
  66. package/coverage/src/work-sources/adapters/index.ts.html +277 -0
  67. package/coverage/src/work-sources/errors.ts.html +298 -0
  68. package/coverage/src/work-sources/index.html +176 -0
  69. package/coverage/src/work-sources/index.ts.html +529 -0
  70. package/coverage/src/work-sources/manager.ts.html +1324 -0
  71. package/coverage/src/work-sources/registry.ts.html +619 -0
  72. package/coverage/src/work-sources/types.ts.html +568 -0
  73. package/dist/config/__tests__/agent.test.d.ts +2 -0
  74. package/dist/config/__tests__/agent.test.d.ts.map +1 -0
  75. package/dist/config/__tests__/agent.test.js +752 -0
  76. package/dist/config/__tests__/agent.test.js.map +1 -0
  77. package/dist/config/__tests__/interpolate.test.d.ts +2 -0
  78. package/dist/config/__tests__/interpolate.test.d.ts.map +1 -0
  79. package/dist/config/__tests__/interpolate.test.js +509 -0
  80. package/dist/config/__tests__/interpolate.test.js.map +1 -0
  81. package/dist/config/__tests__/loader.test.d.ts +2 -0
  82. package/dist/config/__tests__/loader.test.d.ts.map +1 -0
  83. package/dist/config/__tests__/loader.test.js +631 -0
  84. package/dist/config/__tests__/loader.test.js.map +1 -0
  85. package/dist/config/__tests__/merge.test.d.ts +2 -0
  86. package/dist/config/__tests__/merge.test.d.ts.map +1 -0
  87. package/dist/config/__tests__/merge.test.js +672 -0
  88. package/dist/config/__tests__/merge.test.js.map +1 -0
  89. package/dist/config/__tests__/parser.test.d.ts +2 -0
  90. package/dist/config/__tests__/parser.test.d.ts.map +1 -0
  91. package/dist/config/__tests__/parser.test.js +476 -0
  92. package/dist/config/__tests__/parser.test.js.map +1 -0
  93. package/dist/config/__tests__/schema.test.d.ts +2 -0
  94. package/dist/config/__tests__/schema.test.d.ts.map +1 -0
  95. package/dist/config/__tests__/schema.test.js +776 -0
  96. package/dist/config/__tests__/schema.test.js.map +1 -0
  97. package/dist/config/index.d.ts +11 -0
  98. package/dist/config/index.d.ts.map +1 -0
  99. package/dist/config/index.js +26 -0
  100. package/dist/config/index.js.map +1 -0
  101. package/dist/config/interpolate.d.ts +76 -0
  102. package/dist/config/interpolate.d.ts.map +1 -0
  103. package/dist/config/interpolate.js +143 -0
  104. package/dist/config/interpolate.js.map +1 -0
  105. package/dist/config/loader.d.ts +147 -0
  106. package/dist/config/loader.d.ts.map +1 -0
  107. package/dist/config/loader.js +336 -0
  108. package/dist/config/loader.js.map +1 -0
  109. package/dist/config/merge.d.ts +84 -0
  110. package/dist/config/merge.d.ts.map +1 -0
  111. package/dist/config/merge.js +138 -0
  112. package/dist/config/merge.js.map +1 -0
  113. package/dist/config/parser.d.ts +143 -0
  114. package/dist/config/parser.d.ts.map +1 -0
  115. package/dist/config/parser.js +316 -0
  116. package/dist/config/parser.js.map +1 -0
  117. package/dist/config/schema.d.ts +1906 -0
  118. package/dist/config/schema.d.ts.map +1 -0
  119. package/dist/config/schema.js +268 -0
  120. package/dist/config/schema.js.map +1 -0
  121. package/dist/fleet-manager/__tests__/coverage.test.d.ts +13 -0
  122. package/dist/fleet-manager/__tests__/coverage.test.d.ts.map +1 -0
  123. package/dist/fleet-manager/__tests__/coverage.test.js +2282 -0
  124. package/dist/fleet-manager/__tests__/coverage.test.js.map +1 -0
  125. package/dist/fleet-manager/__tests__/errors.test.d.ts +7 -0
  126. package/dist/fleet-manager/__tests__/errors.test.d.ts.map +1 -0
  127. package/dist/fleet-manager/__tests__/errors.test.js +557 -0
  128. package/dist/fleet-manager/__tests__/errors.test.js.map +1 -0
  129. package/dist/fleet-manager/__tests__/event-helpers.test.d.ts +7 -0
  130. package/dist/fleet-manager/__tests__/event-helpers.test.d.ts.map +1 -0
  131. package/dist/fleet-manager/__tests__/event-helpers.test.js +368 -0
  132. package/dist/fleet-manager/__tests__/event-helpers.test.js.map +1 -0
  133. package/dist/fleet-manager/__tests__/integration.test.d.ts +11 -0
  134. package/dist/fleet-manager/__tests__/integration.test.d.ts.map +1 -0
  135. package/dist/fleet-manager/__tests__/integration.test.js +949 -0
  136. package/dist/fleet-manager/__tests__/integration.test.js.map +1 -0
  137. package/dist/fleet-manager/__tests__/job-control.test.d.ts +7 -0
  138. package/dist/fleet-manager/__tests__/job-control.test.d.ts.map +1 -0
  139. package/dist/fleet-manager/__tests__/job-control.test.js +215 -0
  140. package/dist/fleet-manager/__tests__/job-control.test.js.map +1 -0
  141. package/dist/fleet-manager/__tests__/job-manager.test.d.ts +7 -0
  142. package/dist/fleet-manager/__tests__/job-manager.test.d.ts.map +1 -0
  143. package/dist/fleet-manager/__tests__/job-manager.test.js +659 -0
  144. package/dist/fleet-manager/__tests__/job-manager.test.js.map +1 -0
  145. package/dist/fleet-manager/__tests__/job-queue.test.d.ts +5 -0
  146. package/dist/fleet-manager/__tests__/job-queue.test.d.ts.map +1 -0
  147. package/dist/fleet-manager/__tests__/job-queue.test.js +315 -0
  148. package/dist/fleet-manager/__tests__/job-queue.test.js.map +1 -0
  149. package/dist/fleet-manager/__tests__/reload.test.d.ts +7 -0
  150. package/dist/fleet-manager/__tests__/reload.test.d.ts.map +1 -0
  151. package/dist/fleet-manager/__tests__/reload.test.js +609 -0
  152. package/dist/fleet-manager/__tests__/reload.test.js.map +1 -0
  153. package/dist/fleet-manager/__tests__/status-queries.test.d.ts +7 -0
  154. package/dist/fleet-manager/__tests__/status-queries.test.d.ts.map +1 -0
  155. package/dist/fleet-manager/__tests__/status-queries.test.js +488 -0
  156. package/dist/fleet-manager/__tests__/status-queries.test.js.map +1 -0
  157. package/dist/fleet-manager/__tests__/trigger.test.d.ts +7 -0
  158. package/dist/fleet-manager/__tests__/trigger.test.d.ts.map +1 -0
  159. package/dist/fleet-manager/__tests__/trigger.test.js +471 -0
  160. package/dist/fleet-manager/__tests__/trigger.test.js.map +1 -0
  161. package/dist/fleet-manager/errors.d.ts +407 -0
  162. package/dist/fleet-manager/errors.d.ts.map +1 -0
  163. package/dist/fleet-manager/errors.js +569 -0
  164. package/dist/fleet-manager/errors.js.map +1 -0
  165. package/dist/fleet-manager/event-types.d.ts +302 -0
  166. package/dist/fleet-manager/event-types.d.ts.map +1 -0
  167. package/dist/fleet-manager/event-types.js +9 -0
  168. package/dist/fleet-manager/event-types.js.map +1 -0
  169. package/dist/fleet-manager/fleet-manager.d.ts +699 -0
  170. package/dist/fleet-manager/fleet-manager.d.ts.map +1 -0
  171. package/dist/fleet-manager/fleet-manager.js +1906 -0
  172. package/dist/fleet-manager/fleet-manager.js.map +1 -0
  173. package/dist/fleet-manager/index.d.ts +17 -0
  174. package/dist/fleet-manager/index.d.ts.map +1 -0
  175. package/dist/fleet-manager/index.js +29 -0
  176. package/dist/fleet-manager/index.js.map +1 -0
  177. package/dist/fleet-manager/job-manager.d.ts +271 -0
  178. package/dist/fleet-manager/job-manager.d.ts.map +1 -0
  179. package/dist/fleet-manager/job-manager.js +443 -0
  180. package/dist/fleet-manager/job-manager.js.map +1 -0
  181. package/dist/fleet-manager/job-queue.d.ts +422 -0
  182. package/dist/fleet-manager/job-queue.d.ts.map +1 -0
  183. package/dist/fleet-manager/job-queue.js +448 -0
  184. package/dist/fleet-manager/job-queue.js.map +1 -0
  185. package/dist/fleet-manager/types.d.ts +680 -0
  186. package/dist/fleet-manager/types.d.ts.map +1 -0
  187. package/dist/fleet-manager/types.js +8 -0
  188. package/dist/fleet-manager/types.js.map +1 -0
  189. package/dist/index.d.ts +20 -0
  190. package/dist/index.d.ts.map +1 -0
  191. package/dist/index.js +26 -0
  192. package/dist/index.js.map +1 -0
  193. package/dist/runner/__tests__/errors.test.d.ts +2 -0
  194. package/dist/runner/__tests__/errors.test.d.ts.map +1 -0
  195. package/dist/runner/__tests__/errors.test.js +264 -0
  196. package/dist/runner/__tests__/errors.test.js.map +1 -0
  197. package/dist/runner/__tests__/job-executor.test.d.ts +2 -0
  198. package/dist/runner/__tests__/job-executor.test.d.ts.map +1 -0
  199. package/dist/runner/__tests__/job-executor.test.js +1345 -0
  200. package/dist/runner/__tests__/job-executor.test.js.map +1 -0
  201. package/dist/runner/__tests__/message-processor.test.d.ts +2 -0
  202. package/dist/runner/__tests__/message-processor.test.d.ts.map +1 -0
  203. package/dist/runner/__tests__/message-processor.test.js +768 -0
  204. package/dist/runner/__tests__/message-processor.test.js.map +1 -0
  205. package/dist/runner/__tests__/sdk-adapter.test.d.ts +2 -0
  206. package/dist/runner/__tests__/sdk-adapter.test.d.ts.map +1 -0
  207. package/dist/runner/__tests__/sdk-adapter.test.js +554 -0
  208. package/dist/runner/__tests__/sdk-adapter.test.js.map +1 -0
  209. package/dist/runner/errors.d.ts +121 -0
  210. package/dist/runner/errors.d.ts.map +1 -0
  211. package/dist/runner/errors.js +212 -0
  212. package/dist/runner/errors.js.map +1 -0
  213. package/dist/runner/index.d.ts +12 -0
  214. package/dist/runner/index.d.ts.map +1 -0
  215. package/dist/runner/index.js +15 -0
  216. package/dist/runner/index.js.map +1 -0
  217. package/dist/runner/job-executor.d.ts +98 -0
  218. package/dist/runner/job-executor.d.ts.map +1 -0
  219. package/dist/runner/job-executor.js +333 -0
  220. package/dist/runner/job-executor.js.map +1 -0
  221. package/dist/runner/message-processor.d.ts +45 -0
  222. package/dist/runner/message-processor.d.ts.map +1 -0
  223. package/dist/runner/message-processor.js +294 -0
  224. package/dist/runner/message-processor.js.map +1 -0
  225. package/dist/runner/sdk-adapter.d.ts +60 -0
  226. package/dist/runner/sdk-adapter.d.ts.map +1 -0
  227. package/dist/runner/sdk-adapter.js +138 -0
  228. package/dist/runner/sdk-adapter.js.map +1 -0
  229. package/dist/runner/types.d.ts +135 -0
  230. package/dist/runner/types.d.ts.map +1 -0
  231. package/dist/runner/types.js +7 -0
  232. package/dist/runner/types.js.map +1 -0
  233. package/dist/scheduler/__tests__/errors.test.d.ts +2 -0
  234. package/dist/scheduler/__tests__/errors.test.d.ts.map +1 -0
  235. package/dist/scheduler/__tests__/errors.test.js +101 -0
  236. package/dist/scheduler/__tests__/errors.test.js.map +1 -0
  237. package/dist/scheduler/__tests__/interval.test.d.ts +2 -0
  238. package/dist/scheduler/__tests__/interval.test.d.ts.map +1 -0
  239. package/dist/scheduler/__tests__/interval.test.js +419 -0
  240. package/dist/scheduler/__tests__/interval.test.js.map +1 -0
  241. package/dist/scheduler/__tests__/schedule-runner.test.d.ts +2 -0
  242. package/dist/scheduler/__tests__/schedule-runner.test.d.ts.map +1 -0
  243. package/dist/scheduler/__tests__/schedule-runner.test.js +634 -0
  244. package/dist/scheduler/__tests__/schedule-runner.test.js.map +1 -0
  245. package/dist/scheduler/__tests__/schedule-state.test.d.ts +2 -0
  246. package/dist/scheduler/__tests__/schedule-state.test.d.ts.map +1 -0
  247. package/dist/scheduler/__tests__/schedule-state.test.js +572 -0
  248. package/dist/scheduler/__tests__/schedule-state.test.js.map +1 -0
  249. package/dist/scheduler/__tests__/scheduler.test.d.ts +2 -0
  250. package/dist/scheduler/__tests__/scheduler.test.d.ts.map +1 -0
  251. package/dist/scheduler/__tests__/scheduler.test.js +987 -0
  252. package/dist/scheduler/__tests__/scheduler.test.js.map +1 -0
  253. package/dist/scheduler/errors.d.ts +61 -0
  254. package/dist/scheduler/errors.d.ts.map +1 -0
  255. package/dist/scheduler/errors.js +81 -0
  256. package/dist/scheduler/errors.js.map +1 -0
  257. package/dist/scheduler/index.d.ts +13 -0
  258. package/dist/scheduler/index.d.ts.map +1 -0
  259. package/dist/scheduler/index.js +17 -0
  260. package/dist/scheduler/index.js.map +1 -0
  261. package/dist/scheduler/interval.d.ts +64 -0
  262. package/dist/scheduler/interval.d.ts.map +1 -0
  263. package/dist/scheduler/interval.js +139 -0
  264. package/dist/scheduler/interval.js.map +1 -0
  265. package/dist/scheduler/schedule-runner.d.ts +149 -0
  266. package/dist/scheduler/schedule-runner.d.ts.map +1 -0
  267. package/dist/scheduler/schedule-runner.js +277 -0
  268. package/dist/scheduler/schedule-runner.js.map +1 -0
  269. package/dist/scheduler/schedule-state.d.ts +105 -0
  270. package/dist/scheduler/schedule-state.d.ts.map +1 -0
  271. package/dist/scheduler/schedule-state.js +151 -0
  272. package/dist/scheduler/schedule-state.js.map +1 -0
  273. package/dist/scheduler/scheduler.d.ts +138 -0
  274. package/dist/scheduler/scheduler.d.ts.map +1 -0
  275. package/dist/scheduler/scheduler.js +423 -0
  276. package/dist/scheduler/scheduler.js.map +1 -0
  277. package/dist/scheduler/types.d.ts +160 -0
  278. package/dist/scheduler/types.d.ts.map +1 -0
  279. package/dist/scheduler/types.js +8 -0
  280. package/dist/scheduler/types.js.map +1 -0
  281. package/dist/state/__tests__/directory.test.d.ts +2 -0
  282. package/dist/state/__tests__/directory.test.d.ts.map +1 -0
  283. package/dist/state/__tests__/directory.test.js +414 -0
  284. package/dist/state/__tests__/directory.test.js.map +1 -0
  285. package/dist/state/__tests__/fleet-state.test.d.ts +2 -0
  286. package/dist/state/__tests__/fleet-state.test.d.ts.map +1 -0
  287. package/dist/state/__tests__/fleet-state.test.js +696 -0
  288. package/dist/state/__tests__/fleet-state.test.js.map +1 -0
  289. package/dist/state/__tests__/job-metadata-schema.test.d.ts +2 -0
  290. package/dist/state/__tests__/job-metadata-schema.test.d.ts.map +1 -0
  291. package/dist/state/__tests__/job-metadata-schema.test.js +329 -0
  292. package/dist/state/__tests__/job-metadata-schema.test.js.map +1 -0
  293. package/dist/state/__tests__/job-metadata.test.d.ts +2 -0
  294. package/dist/state/__tests__/job-metadata.test.d.ts.map +1 -0
  295. package/dist/state/__tests__/job-metadata.test.js +667 -0
  296. package/dist/state/__tests__/job-metadata.test.js.map +1 -0
  297. package/dist/state/__tests__/job-output.test.d.ts +2 -0
  298. package/dist/state/__tests__/job-output.test.d.ts.map +1 -0
  299. package/dist/state/__tests__/job-output.test.js +672 -0
  300. package/dist/state/__tests__/job-output.test.js.map +1 -0
  301. package/dist/state/__tests__/session-schema.test.d.ts +2 -0
  302. package/dist/state/__tests__/session-schema.test.d.ts.map +1 -0
  303. package/dist/state/__tests__/session-schema.test.js +323 -0
  304. package/dist/state/__tests__/session-schema.test.js.map +1 -0
  305. package/dist/state/__tests__/session.test.d.ts +2 -0
  306. package/dist/state/__tests__/session.test.d.ts.map +1 -0
  307. package/dist/state/__tests__/session.test.js +468 -0
  308. package/dist/state/__tests__/session.test.js.map +1 -0
  309. package/dist/state/directory.d.ts +42 -0
  310. package/dist/state/directory.d.ts.map +1 -0
  311. package/dist/state/directory.js +170 -0
  312. package/dist/state/directory.js.map +1 -0
  313. package/dist/state/errors.d.ts +44 -0
  314. package/dist/state/errors.d.ts.map +1 -0
  315. package/dist/state/errors.js +82 -0
  316. package/dist/state/errors.js.map +1 -0
  317. package/dist/state/fleet-state.d.ts +126 -0
  318. package/dist/state/fleet-state.d.ts.map +1 -0
  319. package/dist/state/fleet-state.js +196 -0
  320. package/dist/state/fleet-state.js.map +1 -0
  321. package/dist/state/index.d.ts +21 -0
  322. package/dist/state/index.d.ts.map +1 -0
  323. package/dist/state/index.js +30 -0
  324. package/dist/state/index.js.map +1 -0
  325. package/dist/state/job-metadata.d.ts +151 -0
  326. package/dist/state/job-metadata.d.ts.map +1 -0
  327. package/dist/state/job-metadata.js +287 -0
  328. package/dist/state/job-metadata.js.map +1 -0
  329. package/dist/state/job-output.d.ts +116 -0
  330. package/dist/state/job-output.d.ts.map +1 -0
  331. package/dist/state/job-output.js +218 -0
  332. package/dist/state/job-output.js.map +1 -0
  333. package/dist/state/schemas/__tests__/job-output.test.d.ts +2 -0
  334. package/dist/state/schemas/__tests__/job-output.test.d.ts.map +1 -0
  335. package/dist/state/schemas/__tests__/job-output.test.js +279 -0
  336. package/dist/state/schemas/__tests__/job-output.test.js.map +1 -0
  337. package/dist/state/schemas/fleet-state.d.ts +249 -0
  338. package/dist/state/schemas/fleet-state.d.ts.map +1 -0
  339. package/dist/state/schemas/fleet-state.js +97 -0
  340. package/dist/state/schemas/fleet-state.js.map +1 -0
  341. package/dist/state/schemas/index.d.ts +10 -0
  342. package/dist/state/schemas/index.d.ts.map +1 -0
  343. package/dist/state/schemas/index.js +10 -0
  344. package/dist/state/schemas/index.js.map +1 -0
  345. package/dist/state/schemas/job-metadata.d.ts +118 -0
  346. package/dist/state/schemas/job-metadata.d.ts.map +1 -0
  347. package/dist/state/schemas/job-metadata.js +123 -0
  348. package/dist/state/schemas/job-metadata.js.map +1 -0
  349. package/dist/state/schemas/job-output.d.ts +291 -0
  350. package/dist/state/schemas/job-output.d.ts.map +1 -0
  351. package/dist/state/schemas/job-output.js +132 -0
  352. package/dist/state/schemas/job-output.js.map +1 -0
  353. package/dist/state/schemas/session-info.d.ts +65 -0
  354. package/dist/state/schemas/session-info.d.ts.map +1 -0
  355. package/dist/state/schemas/session-info.js +58 -0
  356. package/dist/state/schemas/session-info.js.map +1 -0
  357. package/dist/state/session.d.ts +92 -0
  358. package/dist/state/session.d.ts.map +1 -0
  359. package/dist/state/session.js +173 -0
  360. package/dist/state/session.js.map +1 -0
  361. package/dist/state/types.d.ts +54 -0
  362. package/dist/state/types.d.ts.map +1 -0
  363. package/dist/state/types.js +18 -0
  364. package/dist/state/types.js.map +1 -0
  365. package/dist/state/utils/__tests__/atomic.test.d.ts +2 -0
  366. package/dist/state/utils/__tests__/atomic.test.d.ts.map +1 -0
  367. package/dist/state/utils/__tests__/atomic.test.js +537 -0
  368. package/dist/state/utils/__tests__/atomic.test.js.map +1 -0
  369. package/dist/state/utils/__tests__/reads.test.d.ts +2 -0
  370. package/dist/state/utils/__tests__/reads.test.d.ts.map +1 -0
  371. package/dist/state/utils/__tests__/reads.test.js +792 -0
  372. package/dist/state/utils/__tests__/reads.test.js.map +1 -0
  373. package/dist/state/utils/atomic.d.ts +89 -0
  374. package/dist/state/utils/atomic.d.ts.map +1 -0
  375. package/dist/state/utils/atomic.js +157 -0
  376. package/dist/state/utils/atomic.js.map +1 -0
  377. package/dist/state/utils/index.d.ts +6 -0
  378. package/dist/state/utils/index.d.ts.map +1 -0
  379. package/dist/state/utils/index.js +6 -0
  380. package/dist/state/utils/index.js.map +1 -0
  381. package/dist/state/utils/reads.d.ts +196 -0
  382. package/dist/state/utils/reads.d.ts.map +1 -0
  383. package/dist/state/utils/reads.js +346 -0
  384. package/dist/state/utils/reads.js.map +1 -0
  385. package/dist/work-sources/__tests__/github.test.d.ts +2 -0
  386. package/dist/work-sources/__tests__/github.test.d.ts.map +1 -0
  387. package/dist/work-sources/__tests__/github.test.js +1334 -0
  388. package/dist/work-sources/__tests__/github.test.js.map +1 -0
  389. package/dist/work-sources/__tests__/manager.test.d.ts +2 -0
  390. package/dist/work-sources/__tests__/manager.test.d.ts.map +1 -0
  391. package/dist/work-sources/__tests__/manager.test.js +424 -0
  392. package/dist/work-sources/__tests__/manager.test.js.map +1 -0
  393. package/dist/work-sources/__tests__/registry.test.d.ts +2 -0
  394. package/dist/work-sources/__tests__/registry.test.d.ts.map +1 -0
  395. package/dist/work-sources/__tests__/registry.test.js +381 -0
  396. package/dist/work-sources/__tests__/registry.test.js.map +1 -0
  397. package/dist/work-sources/__tests__/types.test.d.ts +2 -0
  398. package/dist/work-sources/__tests__/types.test.d.ts.map +1 -0
  399. package/dist/work-sources/__tests__/types.test.js +406 -0
  400. package/dist/work-sources/__tests__/types.test.js.map +1 -0
  401. package/dist/work-sources/adapters/github.d.ts +290 -0
  402. package/dist/work-sources/adapters/github.d.ts.map +1 -0
  403. package/dist/work-sources/adapters/github.js +803 -0
  404. package/dist/work-sources/adapters/github.js.map +1 -0
  405. package/dist/work-sources/adapters/index.d.ts +10 -0
  406. package/dist/work-sources/adapters/index.d.ts.map +1 -0
  407. package/dist/work-sources/adapters/index.js +31 -0
  408. package/dist/work-sources/adapters/index.js.map +1 -0
  409. package/dist/work-sources/errors.d.ts +40 -0
  410. package/dist/work-sources/errors.d.ts.map +1 -0
  411. package/dist/work-sources/errors.js +54 -0
  412. package/dist/work-sources/errors.js.map +1 -0
  413. package/dist/work-sources/index.d.ts +105 -0
  414. package/dist/work-sources/index.d.ts.map +1 -0
  415. package/dist/work-sources/index.js +24 -0
  416. package/dist/work-sources/index.js.map +1 -0
  417. package/dist/work-sources/manager.d.ts +370 -0
  418. package/dist/work-sources/manager.d.ts.map +1 -0
  419. package/dist/work-sources/manager.js +61 -0
  420. package/dist/work-sources/manager.js.map +1 -0
  421. package/dist/work-sources/registry.d.ts +128 -0
  422. package/dist/work-sources/registry.d.ts.map +1 -0
  423. package/dist/work-sources/registry.js +132 -0
  424. package/dist/work-sources/registry.js.map +1 -0
  425. package/dist/work-sources/types.d.ts +127 -0
  426. package/dist/work-sources/types.d.ts.map +1 -0
  427. package/dist/work-sources/types.js +8 -0
  428. package/dist/work-sources/types.js.map +1 -0
  429. package/package.json +23 -0
  430. package/src/config/__tests__/agent.test.ts +864 -0
  431. package/src/config/__tests__/interpolate.test.ts +644 -0
  432. package/src/config/__tests__/loader.test.ts +784 -0
  433. package/src/config/__tests__/merge.test.ts +751 -0
  434. package/src/config/__tests__/parser.test.ts +533 -0
  435. package/src/config/__tests__/schema.test.ts +873 -0
  436. package/src/config/index.ts +119 -0
  437. package/src/config/interpolate.ts +189 -0
  438. package/src/config/loader.ts +472 -0
  439. package/src/config/merge.ts +246 -0
  440. package/src/config/parser.ts +376 -0
  441. package/src/config/schema.ts +346 -0
  442. package/src/fleet-manager/__tests__/coverage.test.ts +2869 -0
  443. package/src/fleet-manager/__tests__/errors.test.ts +660 -0
  444. package/src/fleet-manager/__tests__/event-helpers.test.ts +448 -0
  445. package/src/fleet-manager/__tests__/integration.test.ts +1209 -0
  446. package/src/fleet-manager/__tests__/job-control.test.ts +283 -0
  447. package/src/fleet-manager/__tests__/job-manager.test.ts +869 -0
  448. package/src/fleet-manager/__tests__/job-queue.test.ts +401 -0
  449. package/src/fleet-manager/__tests__/reload.test.ts +751 -0
  450. package/src/fleet-manager/__tests__/status-queries.test.ts +595 -0
  451. package/src/fleet-manager/__tests__/trigger.test.ts +601 -0
  452. package/src/fleet-manager/errors.ts +747 -0
  453. package/src/fleet-manager/event-types.ts +378 -0
  454. package/src/fleet-manager/fleet-manager.ts +2315 -0
  455. package/src/fleet-manager/index.ts +128 -0
  456. package/src/fleet-manager/job-manager.ts +663 -0
  457. package/src/fleet-manager/job-queue.ts +798 -0
  458. package/src/fleet-manager/types.ts +839 -0
  459. package/src/index.ts +32 -0
  460. package/src/runner/__tests__/errors.test.ts +382 -0
  461. package/src/runner/__tests__/job-executor.test.ts +1708 -0
  462. package/src/runner/__tests__/message-processor.test.ts +960 -0
  463. package/src/runner/__tests__/sdk-adapter.test.ts +626 -0
  464. package/src/runner/errors.ts +307 -0
  465. package/src/runner/index.ts +57 -0
  466. package/src/runner/job-executor.ts +448 -0
  467. package/src/runner/message-processor.ts +355 -0
  468. package/src/runner/sdk-adapter.ts +191 -0
  469. package/src/runner/types.ts +158 -0
  470. package/src/scheduler/__tests__/errors.test.ts +159 -0
  471. package/src/scheduler/__tests__/interval.test.ts +515 -0
  472. package/src/scheduler/__tests__/schedule-runner.test.ts +798 -0
  473. package/src/scheduler/__tests__/schedule-state.test.ts +671 -0
  474. package/src/scheduler/__tests__/scheduler.test.ts +1280 -0
  475. package/src/scheduler/errors.ts +101 -0
  476. package/src/scheduler/index.ts +53 -0
  477. package/src/scheduler/interval.ts +189 -0
  478. package/src/scheduler/schedule-runner.ts +442 -0
  479. package/src/scheduler/schedule-state.ts +211 -0
  480. package/src/scheduler/scheduler.ts +570 -0
  481. package/src/scheduler/types.ts +216 -0
  482. package/src/state/__tests__/directory.test.ts +595 -0
  483. package/src/state/__tests__/fleet-state.test.ts +868 -0
  484. package/src/state/__tests__/job-metadata-schema.test.ts +414 -0
  485. package/src/state/__tests__/job-metadata.test.ts +831 -0
  486. package/src/state/__tests__/job-output.test.ts +856 -0
  487. package/src/state/__tests__/session-schema.test.ts +378 -0
  488. package/src/state/__tests__/session.test.ts +604 -0
  489. package/src/state/directory.ts +217 -0
  490. package/src/state/errors.ts +97 -0
  491. package/src/state/fleet-state.ts +284 -0
  492. package/src/state/index.ts +79 -0
  493. package/src/state/job-metadata.ts +445 -0
  494. package/src/state/job-output.ts +316 -0
  495. package/src/state/schemas/__tests__/job-output.test.ts +338 -0
  496. package/src/state/schemas/fleet-state.ts +120 -0
  497. package/src/state/schemas/index.ts +67 -0
  498. package/src/state/schemas/job-metadata.ts +181 -0
  499. package/src/state/schemas/job-output.ts +177 -0
  500. package/src/state/schemas/session-info.ts +92 -0
  501. package/src/state/session.ts +253 -0
  502. package/src/state/types.ts +59 -0
  503. package/src/state/utils/__tests__/atomic.test.ts +723 -0
  504. package/src/state/utils/__tests__/reads.test.ts +1071 -0
  505. package/src/state/utils/atomic.ts +221 -0
  506. package/src/state/utils/index.ts +6 -0
  507. package/src/state/utils/reads.ts +512 -0
  508. package/src/work-sources/__tests__/github.test.ts +1800 -0
  509. package/src/work-sources/__tests__/manager.test.ts +529 -0
  510. package/src/work-sources/__tests__/registry.test.ts +477 -0
  511. package/src/work-sources/__tests__/types.test.ts +479 -0
  512. package/src/work-sources/adapters/github.ts +1166 -0
  513. package/src/work-sources/adapters/index.ts +64 -0
  514. package/src/work-sources/errors.ts +71 -0
  515. package/src/work-sources/index.ts +148 -0
  516. package/src/work-sources/manager.ts +413 -0
  517. package/src/work-sources/registry.ts +178 -0
  518. package/src/work-sources/types.ts +161 -0
  519. package/tsconfig.json +9 -0
  520. package/vitest.config.ts +19 -0
@@ -0,0 +1,803 @@
1
+ /**
2
+ * GitHub Work Source Adapter
3
+ *
4
+ * Provides work items from GitHub Issues. Uses labels to identify
5
+ * work that is ready for agents and to track in-progress work.
6
+ *
7
+ * Includes robust handling of GitHub API edge cases:
8
+ * - Exponential backoff for rate limit errors (HTTP 403 with X-RateLimit-Remaining: 0)
9
+ * - Rate limit status detection and surfacing
10
+ * - Network error retries (max 3 attempts)
11
+ * - Graceful handling of 404 errors (issue deleted/moved)
12
+ * - PAT scope validation
13
+ * - Warnings for approaching rate limit (< 100 remaining)
14
+ */
15
+ import { WorkSourceError } from "../errors.js";
16
+ // =============================================================================
17
+ // Errors
18
+ // =============================================================================
19
+ /**
20
+ * Error thrown when GitHub API requests fail
21
+ */
22
+ export class GitHubAPIError extends WorkSourceError {
23
+ /** HTTP status code from the API response */
24
+ statusCode;
25
+ /** The API endpoint that was called */
26
+ endpoint;
27
+ /** Rate limit information if available */
28
+ rateLimitInfo;
29
+ /** Whether this error was caused by rate limiting */
30
+ isRateLimitError;
31
+ /** Timestamp when rate limit resets (for rate limit errors) */
32
+ rateLimitResetAt;
33
+ constructor(message, options) {
34
+ super(message, options);
35
+ this.name = "GitHubAPIError";
36
+ this.statusCode = options?.statusCode;
37
+ this.endpoint = options?.endpoint;
38
+ this.rateLimitInfo = options?.rateLimitInfo;
39
+ this.isRateLimitError = options?.isRateLimitError ?? false;
40
+ if (options?.rateLimitInfo?.reset) {
41
+ this.rateLimitResetAt = new Date(options.rateLimitInfo.reset * 1000);
42
+ }
43
+ }
44
+ /**
45
+ * Check if this error is retryable (rate limit or network error)
46
+ */
47
+ isRetryable() {
48
+ // Rate limit errors are retryable after waiting
49
+ if (this.isRateLimitError) {
50
+ return true;
51
+ }
52
+ // Network errors (no status code) are retryable
53
+ if (this.statusCode === undefined) {
54
+ return true;
55
+ }
56
+ // Server errors (5xx) are retryable
57
+ if (this.statusCode >= 500 && this.statusCode < 600) {
58
+ return true;
59
+ }
60
+ // 408 Request Timeout is retryable
61
+ if (this.statusCode === 408) {
62
+ return true;
63
+ }
64
+ return false;
65
+ }
66
+ /**
67
+ * Check if this is a not found error (404)
68
+ */
69
+ isNotFound() {
70
+ return this.statusCode === 404;
71
+ }
72
+ /**
73
+ * Check if this is a permission error (403 without rate limit)
74
+ */
75
+ isPermissionDenied() {
76
+ return this.statusCode === 403 && !this.isRateLimitError;
77
+ }
78
+ /**
79
+ * Get time in milliseconds until rate limit resets
80
+ */
81
+ getTimeUntilReset() {
82
+ if (!this.rateLimitResetAt) {
83
+ return undefined;
84
+ }
85
+ const now = Date.now();
86
+ const resetTime = this.rateLimitResetAt.getTime();
87
+ return Math.max(0, resetTime - now);
88
+ }
89
+ }
90
+ /**
91
+ * Error thrown when PAT validation fails
92
+ */
93
+ export class GitHubAuthError extends WorkSourceError {
94
+ /** The scopes that were found */
95
+ foundScopes;
96
+ /** The scopes that were required */
97
+ requiredScopes;
98
+ /** The missing scopes */
99
+ missingScopes;
100
+ constructor(message, options) {
101
+ super(message, options);
102
+ this.name = "GitHubAuthError";
103
+ this.foundScopes = options.foundScopes;
104
+ this.requiredScopes = options.requiredScopes;
105
+ this.missingScopes = options.requiredScopes.filter((s) => !options.foundScopes.includes(s));
106
+ }
107
+ }
108
+ // =============================================================================
109
+ // Default Configuration
110
+ // =============================================================================
111
+ const DEFAULT_LABELS = {
112
+ ready: "ready",
113
+ in_progress: "agent-working",
114
+ };
115
+ const DEFAULT_EXCLUDE_LABELS = ["blocked", "wip"];
116
+ const DEFAULT_API_BASE_URL = "https://api.github.com";
117
+ /** Default retry options */
118
+ const DEFAULT_RETRY_OPTIONS = {
119
+ maxRetries: 3,
120
+ baseDelayMs: 1000,
121
+ maxDelayMs: 30000,
122
+ jitterFactor: 0.1,
123
+ };
124
+ /** Default rate limit warning threshold */
125
+ const DEFAULT_RATE_LIMIT_WARNING_THRESHOLD = 100;
126
+ /** Required scopes for full GitHub adapter functionality */
127
+ const REQUIRED_SCOPES = ["repo"];
128
+ // =============================================================================
129
+ // Utility Functions
130
+ // =============================================================================
131
+ /**
132
+ * Extract rate limit information from GitHub API response headers
133
+ */
134
+ export function extractRateLimitInfo(headers) {
135
+ const remaining = headers.get("X-RateLimit-Remaining");
136
+ const limit = headers.get("X-RateLimit-Limit");
137
+ const reset = headers.get("X-RateLimit-Reset");
138
+ const resource = headers.get("X-RateLimit-Resource");
139
+ if (remaining === null || limit === null || reset === null) {
140
+ return undefined;
141
+ }
142
+ return {
143
+ remaining: parseInt(remaining, 10),
144
+ limit: parseInt(limit, 10),
145
+ reset: parseInt(reset, 10),
146
+ resource: resource ?? "core",
147
+ };
148
+ }
149
+ /**
150
+ * Check if a response indicates rate limiting
151
+ */
152
+ export function isRateLimitResponse(response) {
153
+ // GitHub returns 403 with X-RateLimit-Remaining: 0 for rate limits
154
+ // Also check for 429 which some GitHub endpoints may return
155
+ if (response.status !== 403 && response.status !== 429) {
156
+ return false;
157
+ }
158
+ const remaining = response.headers.get("X-RateLimit-Remaining");
159
+ if (remaining === null) {
160
+ return response.status === 429;
161
+ }
162
+ return parseInt(remaining, 10) === 0;
163
+ }
164
+ /**
165
+ * Calculate delay with exponential backoff and jitter
166
+ */
167
+ export function calculateBackoffDelay(attempt, options, rateLimitResetMs) {
168
+ // If we have rate limit reset info, use that as the delay
169
+ if (rateLimitResetMs !== undefined && rateLimitResetMs > 0) {
170
+ // Add a small buffer (1 second) to ensure the limit has reset
171
+ return Math.min(rateLimitResetMs + 1000, options.maxDelayMs);
172
+ }
173
+ // Calculate exponential backoff: baseDelay * 2^attempt
174
+ const exponentialDelay = options.baseDelayMs * Math.pow(2, attempt);
175
+ // Cap at max delay
176
+ const cappedDelay = Math.min(exponentialDelay, options.maxDelayMs);
177
+ // Add jitter to prevent thundering herd
178
+ const jitter = cappedDelay * options.jitterFactor * Math.random();
179
+ return Math.floor(cappedDelay + jitter);
180
+ }
181
+ /**
182
+ * Sleep for a specified number of milliseconds
183
+ */
184
+ function sleep(ms) {
185
+ return new Promise((resolve) => setTimeout(resolve, ms));
186
+ }
187
+ // =============================================================================
188
+ // GitHub Adapter Implementation
189
+ // =============================================================================
190
+ /**
191
+ * Work source adapter for GitHub Issues
192
+ *
193
+ * Fetches issues with a specific label and allows claiming/completing them.
194
+ * Uses the GitHub REST API to manage issue labels and comments.
195
+ */
196
+ export class GitHubWorkSourceAdapter {
197
+ type = "github";
198
+ config;
199
+ labels;
200
+ excludeLabels;
201
+ apiBaseUrl;
202
+ retryOptions;
203
+ rateLimitWarningThreshold;
204
+ onRateLimitWarning;
205
+ /** Last known rate limit info, updated after each request */
206
+ _lastRateLimitInfo;
207
+ constructor(config) {
208
+ this.config = config;
209
+ this.labels = {
210
+ ready: config.labels?.ready ?? DEFAULT_LABELS.ready,
211
+ in_progress: config.labels?.in_progress ?? DEFAULT_LABELS.in_progress,
212
+ };
213
+ this.excludeLabels = config.exclude_labels ?? DEFAULT_EXCLUDE_LABELS;
214
+ this.apiBaseUrl = config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
215
+ this.retryOptions = {
216
+ ...DEFAULT_RETRY_OPTIONS,
217
+ ...config.retry,
218
+ };
219
+ this.rateLimitWarningThreshold =
220
+ config.rateLimitWarning?.warningThreshold ?? DEFAULT_RATE_LIMIT_WARNING_THRESHOLD;
221
+ this.onRateLimitWarning = config.rateLimitWarning?.onWarning;
222
+ }
223
+ /**
224
+ * Get the last known rate limit information
225
+ */
226
+ get lastRateLimitInfo() {
227
+ return this._lastRateLimitInfo;
228
+ }
229
+ /**
230
+ * Get the GitHub API token from config or environment
231
+ */
232
+ getToken() {
233
+ return this.config.token ?? process.env.GITHUB_TOKEN;
234
+ }
235
+ /**
236
+ * Get required owner and repo from config
237
+ */
238
+ getOwnerRepo() {
239
+ const { owner, repo } = this.config;
240
+ if (!owner || !repo) {
241
+ throw new GitHubAPIError("GitHub adapter requires 'owner' and 'repo' configuration");
242
+ }
243
+ return { owner, repo };
244
+ }
245
+ /**
246
+ * Validate PAT has required scopes for GitHub adapter functionality
247
+ *
248
+ * Makes a request to check the token scopes and verifies
249
+ * that the required scopes (repo) are present.
250
+ *
251
+ * @throws GitHubAuthError if required scopes are missing
252
+ * @throws GitHubAPIError if the validation request fails
253
+ */
254
+ async validateToken() {
255
+ const token = this.getToken();
256
+ if (!token) {
257
+ throw new GitHubAuthError("No GitHub token configured. Set GITHUB_TOKEN environment variable or provide token in config.", {
258
+ foundScopes: [],
259
+ requiredScopes: [...REQUIRED_SCOPES],
260
+ });
261
+ }
262
+ const headers = {
263
+ Accept: "application/vnd.github+json",
264
+ "X-GitHub-Api-Version": "2022-11-28",
265
+ Authorization: `Bearer ${token}`,
266
+ };
267
+ const url = `${this.apiBaseUrl}/user`;
268
+ try {
269
+ const response = await fetch(url, { method: "GET", headers });
270
+ // Extract scopes from response header
271
+ const scopesHeader = response.headers.get("X-OAuth-Scopes");
272
+ const scopes = scopesHeader
273
+ ? scopesHeader.split(",").map((s) => s.trim()).filter((s) => s.length > 0)
274
+ : [];
275
+ // Update rate limit info
276
+ const rateLimitInfo = extractRateLimitInfo(response.headers);
277
+ if (rateLimitInfo) {
278
+ this._lastRateLimitInfo = rateLimitInfo;
279
+ this.checkRateLimitWarning(rateLimitInfo);
280
+ }
281
+ if (!response.ok) {
282
+ if (response.status === 401) {
283
+ throw new GitHubAuthError("Invalid GitHub token. The token may be expired or revoked.", {
284
+ foundScopes: scopes,
285
+ requiredScopes: [...REQUIRED_SCOPES],
286
+ });
287
+ }
288
+ throw new GitHubAPIError(`GitHub API error: ${response.status} ${response.statusText}`, { statusCode: response.status, endpoint: "/user" });
289
+ }
290
+ // Check for required scopes
291
+ const hasRequiredScopes = REQUIRED_SCOPES.every((required) => scopes.some((scope) => scope === required || scope.startsWith(`${required}:`)));
292
+ if (!hasRequiredScopes) {
293
+ throw new GitHubAuthError(`GitHub token is missing required scopes. Found: [${scopes.join(", ")}], Required: [${REQUIRED_SCOPES.join(", ")}]`, {
294
+ foundScopes: scopes,
295
+ requiredScopes: [...REQUIRED_SCOPES],
296
+ });
297
+ }
298
+ return { scopes, valid: true };
299
+ }
300
+ catch (error) {
301
+ if (error instanceof GitHubAuthError || error instanceof GitHubAPIError) {
302
+ throw error;
303
+ }
304
+ throw new GitHubAPIError(`Failed to validate GitHub token: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined, endpoint: "/user" });
305
+ }
306
+ }
307
+ /**
308
+ * Check rate limit and invoke warning callback if below threshold
309
+ */
310
+ checkRateLimitWarning(info) {
311
+ if (info.remaining < this.rateLimitWarningThreshold && this.onRateLimitWarning) {
312
+ this.onRateLimitWarning(info);
313
+ }
314
+ }
315
+ /**
316
+ * Make an authenticated request to the GitHub API with retry logic
317
+ *
318
+ * Implements:
319
+ * - Exponential backoff for rate limit errors (HTTP 403 with X-RateLimit-Remaining: 0)
320
+ * - Network error retries (max 3 attempts by default)
321
+ * - Rate limit status tracking and warnings
322
+ */
323
+ async apiRequest(method, endpoint, body) {
324
+ const token = this.getToken();
325
+ const headers = {
326
+ Accept: "application/vnd.github+json",
327
+ "X-GitHub-Api-Version": "2022-11-28",
328
+ };
329
+ if (token) {
330
+ headers["Authorization"] = `Bearer ${token}`;
331
+ }
332
+ const url = `${this.apiBaseUrl}${endpoint}`;
333
+ let lastError;
334
+ for (let attempt = 0; attempt <= this.retryOptions.maxRetries; attempt++) {
335
+ try {
336
+ const response = await fetch(url, {
337
+ method,
338
+ headers,
339
+ body: body ? JSON.stringify(body) : undefined,
340
+ });
341
+ // Extract and store rate limit info from every response
342
+ const rateLimitInfo = extractRateLimitInfo(response.headers);
343
+ if (rateLimitInfo) {
344
+ this._lastRateLimitInfo = rateLimitInfo;
345
+ this.checkRateLimitWarning(rateLimitInfo);
346
+ }
347
+ if (!response.ok) {
348
+ // Check for rate limiting
349
+ const isRateLimit = isRateLimitResponse(response);
350
+ let errorMessage = `GitHub API error: ${response.status} ${response.statusText}`;
351
+ try {
352
+ const errorBody = (await response.json());
353
+ if (errorBody.message) {
354
+ errorMessage = `GitHub API error: ${errorBody.message}`;
355
+ }
356
+ }
357
+ catch {
358
+ // Ignore JSON parse errors
359
+ }
360
+ const error = new GitHubAPIError(errorMessage, {
361
+ statusCode: response.status,
362
+ endpoint,
363
+ rateLimitInfo,
364
+ isRateLimitError: isRateLimit,
365
+ });
366
+ // Only retry on retryable errors
367
+ if (error.isRetryable() && attempt < this.retryOptions.maxRetries) {
368
+ lastError = error;
369
+ const delay = calculateBackoffDelay(attempt, this.retryOptions, error.getTimeUntilReset());
370
+ await sleep(delay);
371
+ continue;
372
+ }
373
+ throw error;
374
+ }
375
+ // Handle 204 No Content responses
376
+ if (response.status === 204) {
377
+ return undefined;
378
+ }
379
+ return (await response.json());
380
+ }
381
+ catch (error) {
382
+ // Re-throw GitHubAPIError as-is (already handled above)
383
+ if (error instanceof GitHubAPIError) {
384
+ throw error;
385
+ }
386
+ // Network errors - wrap and potentially retry
387
+ const networkError = new GitHubAPIError(`Failed to connect to GitHub API: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined, endpoint });
388
+ if (networkError.isRetryable() && attempt < this.retryOptions.maxRetries) {
389
+ lastError = networkError;
390
+ const delay = calculateBackoffDelay(attempt, this.retryOptions);
391
+ await sleep(delay);
392
+ continue;
393
+ }
394
+ throw networkError;
395
+ }
396
+ }
397
+ // Should not reach here, but throw last error if we do
398
+ throw lastError ?? new GitHubAPIError("Unknown error during API request", { endpoint });
399
+ }
400
+ /**
401
+ * Make an authenticated request to the GitHub API with retry logic, returning both data and headers
402
+ *
403
+ * This variant returns the response headers along with the data, useful for
404
+ * pagination (Link header) and other response metadata.
405
+ */
406
+ async apiRequestWithHeaders(method, endpoint, body) {
407
+ const token = this.getToken();
408
+ const headers = {
409
+ Accept: "application/vnd.github+json",
410
+ "X-GitHub-Api-Version": "2022-11-28",
411
+ };
412
+ if (token) {
413
+ headers["Authorization"] = `Bearer ${token}`;
414
+ }
415
+ const url = `${this.apiBaseUrl}${endpoint}`;
416
+ let lastError;
417
+ for (let attempt = 0; attempt <= this.retryOptions.maxRetries; attempt++) {
418
+ try {
419
+ const response = await fetch(url, {
420
+ method,
421
+ headers,
422
+ body: body ? JSON.stringify(body) : undefined,
423
+ });
424
+ // Extract and store rate limit info from every response
425
+ const rateLimitInfo = extractRateLimitInfo(response.headers);
426
+ if (rateLimitInfo) {
427
+ this._lastRateLimitInfo = rateLimitInfo;
428
+ this.checkRateLimitWarning(rateLimitInfo);
429
+ }
430
+ if (!response.ok) {
431
+ // Check for rate limiting
432
+ const isRateLimit = isRateLimitResponse(response);
433
+ let errorMessage = `GitHub API error: ${response.status} ${response.statusText}`;
434
+ try {
435
+ const errorBody = (await response.json());
436
+ if (errorBody.message) {
437
+ errorMessage = `GitHub API error: ${errorBody.message}`;
438
+ }
439
+ }
440
+ catch {
441
+ // Ignore JSON parse errors
442
+ }
443
+ const error = new GitHubAPIError(errorMessage, {
444
+ statusCode: response.status,
445
+ endpoint,
446
+ rateLimitInfo,
447
+ isRateLimitError: isRateLimit,
448
+ });
449
+ // Only retry on retryable errors
450
+ if (error.isRetryable() && attempt < this.retryOptions.maxRetries) {
451
+ lastError = error;
452
+ const delay = calculateBackoffDelay(attempt, this.retryOptions, error.getTimeUntilReset());
453
+ await sleep(delay);
454
+ continue;
455
+ }
456
+ throw error;
457
+ }
458
+ // Handle 204 No Content responses
459
+ if (response.status === 204) {
460
+ return { data: undefined, headers: response.headers };
461
+ }
462
+ const data = (await response.json());
463
+ return { data, headers: response.headers };
464
+ }
465
+ catch (error) {
466
+ // Re-throw GitHubAPIError as-is (already handled above)
467
+ if (error instanceof GitHubAPIError) {
468
+ throw error;
469
+ }
470
+ // Network errors - wrap and potentially retry
471
+ const networkError = new GitHubAPIError(`Failed to connect to GitHub API: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined, endpoint });
472
+ if (networkError.isRetryable() && attempt < this.retryOptions.maxRetries) {
473
+ lastError = networkError;
474
+ const delay = calculateBackoffDelay(attempt, this.retryOptions);
475
+ await sleep(delay);
476
+ continue;
477
+ }
478
+ throw networkError;
479
+ }
480
+ }
481
+ // Should not reach here, but throw last error if we do
482
+ throw lastError ?? new GitHubAPIError("Unknown error during API request", { endpoint });
483
+ }
484
+ /**
485
+ * Extract the issue number from a work item ID
486
+ */
487
+ parseWorkItemId(workItemId) {
488
+ // Work item IDs are formatted as "github-{issueNumber}"
489
+ const match = workItemId.match(/^github-(\d+)$/);
490
+ if (!match) {
491
+ throw new GitHubAPIError(`Invalid work item ID format: "${workItemId}". Expected "github-{number}"`);
492
+ }
493
+ return parseInt(match[1], 10);
494
+ }
495
+ /**
496
+ * Convert a GitHub issue to a WorkItem
497
+ */
498
+ issueToWorkItem(issue) {
499
+ const labelNames = issue.labels.map((l) => l.name);
500
+ return {
501
+ id: `github-${issue.number}`,
502
+ source: "github",
503
+ externalId: String(issue.number),
504
+ title: issue.title,
505
+ description: issue.body ?? "",
506
+ priority: this.inferPriority(labelNames),
507
+ labels: labelNames,
508
+ metadata: {
509
+ state: issue.state,
510
+ assignee: issue.assignee?.login ?? null,
511
+ assignees: issue.assignees.map((a) => a.login),
512
+ milestone: issue.milestone
513
+ ? {
514
+ title: issue.milestone.title,
515
+ number: issue.milestone.number,
516
+ }
517
+ : null,
518
+ author: issue.user?.login ?? null,
519
+ },
520
+ url: issue.html_url,
521
+ createdAt: new Date(issue.created_at),
522
+ updatedAt: new Date(issue.updated_at),
523
+ };
524
+ }
525
+ /**
526
+ * Infer priority from issue labels
527
+ */
528
+ inferPriority(labels) {
529
+ const lowerLabels = labels.map((l) => l.toLowerCase());
530
+ if (lowerLabels.some((l) => l.includes("critical") || l.includes("p0") || l.includes("urgent"))) {
531
+ return "critical";
532
+ }
533
+ if (lowerLabels.some((l) => l.includes("high") || l.includes("p1") || l.includes("important"))) {
534
+ return "high";
535
+ }
536
+ if (lowerLabels.some((l) => l.includes("low") || l.includes("p3"))) {
537
+ return "low";
538
+ }
539
+ return "medium";
540
+ }
541
+ /**
542
+ * Build the query string for fetching issues
543
+ */
544
+ buildIssueQuery(options) {
545
+ const params = new URLSearchParams();
546
+ // Always filter by ready label
547
+ params.set("labels", this.labels.ready);
548
+ // Sort by creation date (oldest first for FIFO)
549
+ params.set("sort", "created");
550
+ params.set("direction", "asc");
551
+ // State filter - only open issues unless claimed are included
552
+ params.set("state", "open");
553
+ // Pagination
554
+ if (options?.limit) {
555
+ params.set("per_page", String(Math.min(options.limit, 100)));
556
+ }
557
+ else {
558
+ params.set("per_page", "30");
559
+ }
560
+ if (options?.cursor) {
561
+ params.set("page", options.cursor);
562
+ }
563
+ return params.toString();
564
+ }
565
+ /**
566
+ * Extract pagination cursor from Link header
567
+ */
568
+ extractNextPage(linkHeader) {
569
+ if (!linkHeader)
570
+ return undefined;
571
+ // Parse Link header format: <url>; rel="next", <url>; rel="last"
572
+ const links = linkHeader.split(",");
573
+ for (const link of links) {
574
+ const match = link.match(/<[^>]*[?&]page=(\d+)[^>]*>;\s*rel="next"/);
575
+ if (match) {
576
+ return match[1];
577
+ }
578
+ }
579
+ return undefined;
580
+ }
581
+ /**
582
+ * Fetch available work items from GitHub Issues
583
+ *
584
+ * Queries issues with the ready label, excludes issues with blocked/wip labels,
585
+ * and returns them sorted by creation date (oldest first).
586
+ *
587
+ * Includes retry logic for transient failures and rate limit handling.
588
+ */
589
+ async fetchAvailableWork(options) {
590
+ const { owner, repo } = this.getOwnerRepo();
591
+ const query = this.buildIssueQuery(options);
592
+ const endpoint = `/repos/${owner}/${repo}/issues?${query}`;
593
+ const { data: issues, headers } = await this.apiRequestWithHeaders("GET", endpoint);
594
+ // Filter out issues with excluded labels and issues that are already claimed
595
+ const filteredIssues = issues.filter((issue) => {
596
+ const labelNames = issue.labels.map((l) => l.name.toLowerCase());
597
+ // Exclude issues with any of the exclude labels
598
+ const hasExcludedLabel = this.excludeLabels.some((excluded) => labelNames.includes(excluded.toLowerCase()));
599
+ if (hasExcludedLabel)
600
+ return false;
601
+ // Unless includeClaimed is true, exclude issues with in_progress label
602
+ if (!options?.includeClaimed) {
603
+ const hasInProgressLabel = labelNames.includes(this.labels.in_progress.toLowerCase());
604
+ if (hasInProgressLabel)
605
+ return false;
606
+ }
607
+ // Apply additional label filters if specified
608
+ if (options?.labels && options.labels.length > 0) {
609
+ const hasAllLabels = options.labels.every((required) => labelNames.includes(required.toLowerCase()));
610
+ if (!hasAllLabels)
611
+ return false;
612
+ }
613
+ return true;
614
+ });
615
+ // Filter by priority if specified
616
+ let workItems = filteredIssues.map((issue) => this.issueToWorkItem(issue));
617
+ if (options?.priority && options.priority.length > 0) {
618
+ workItems = workItems.filter((item) => options.priority.includes(item.priority));
619
+ }
620
+ // Extract pagination info from Link header
621
+ const linkHeader = headers.get("Link");
622
+ const nextCursor = this.extractNextPage(linkHeader);
623
+ return {
624
+ items: workItems,
625
+ nextCursor,
626
+ };
627
+ }
628
+ /**
629
+ * Claim a work item by adding the in-progress label and removing the ready label
630
+ */
631
+ async claimWork(workItemId) {
632
+ const { owner, repo } = this.getOwnerRepo();
633
+ const issueNumber = this.parseWorkItemId(workItemId);
634
+ try {
635
+ // First, get the current issue to verify it exists and is claimable
636
+ const issue = await this.apiRequest("GET", `/repos/${owner}/${repo}/issues/${issueNumber}`);
637
+ if (!issue) {
638
+ return {
639
+ success: false,
640
+ reason: "not_found",
641
+ message: `Issue #${issueNumber} not found`,
642
+ };
643
+ }
644
+ if (issue.state === "closed") {
645
+ return {
646
+ success: false,
647
+ reason: "invalid_state",
648
+ message: `Issue #${issueNumber} is closed`,
649
+ };
650
+ }
651
+ const labelNames = issue.labels.map((l) => l.name.toLowerCase());
652
+ // Check if already claimed
653
+ if (labelNames.includes(this.labels.in_progress.toLowerCase())) {
654
+ return {
655
+ success: false,
656
+ reason: "already_claimed",
657
+ message: `Issue #${issueNumber} is already claimed`,
658
+ };
659
+ }
660
+ // Add the in-progress label
661
+ await this.apiRequest("POST", `/repos/${owner}/${repo}/issues/${issueNumber}/labels`, { labels: [this.labels.in_progress] });
662
+ // Remove the ready label (if present)
663
+ if (labelNames.includes(this.labels.ready.toLowerCase())) {
664
+ try {
665
+ await this.apiRequest("DELETE", `/repos/${owner}/${repo}/issues/${issueNumber}/labels/${encodeURIComponent(this.labels.ready)}`);
666
+ }
667
+ catch {
668
+ // Ignore errors removing the ready label - it might not exist
669
+ }
670
+ }
671
+ // Fetch the updated issue
672
+ const updatedIssue = await this.apiRequest("GET", `/repos/${owner}/${repo}/issues/${issueNumber}`);
673
+ return {
674
+ success: true,
675
+ workItem: this.issueToWorkItem(updatedIssue),
676
+ };
677
+ }
678
+ catch (error) {
679
+ if (error instanceof GitHubAPIError) {
680
+ if (error.statusCode === 404) {
681
+ return {
682
+ success: false,
683
+ reason: "not_found",
684
+ message: `Issue #${issueNumber} not found`,
685
+ };
686
+ }
687
+ if (error.statusCode === 403) {
688
+ return {
689
+ success: false,
690
+ reason: "permission_denied",
691
+ message: `Permission denied for issue #${issueNumber}`,
692
+ };
693
+ }
694
+ return {
695
+ success: false,
696
+ reason: "source_error",
697
+ message: error.message,
698
+ };
699
+ }
700
+ return {
701
+ success: false,
702
+ reason: "source_error",
703
+ message: `Failed to claim issue: ${error instanceof Error ? error.message : String(error)}`,
704
+ };
705
+ }
706
+ }
707
+ /**
708
+ * Complete a work item by posting a comment with the result and optionally closing the issue
709
+ */
710
+ async completeWork(workItemId, result) {
711
+ const { owner, repo } = this.getOwnerRepo();
712
+ const issueNumber = this.parseWorkItemId(workItemId);
713
+ // Build the completion comment
714
+ const outcomeEmoji = result.outcome === "success"
715
+ ? "✅"
716
+ : result.outcome === "partial"
717
+ ? "⚠️"
718
+ : "❌";
719
+ let commentBody = `## ${outcomeEmoji} Work Completed\n\n`;
720
+ commentBody += `**Outcome:** ${result.outcome}\n\n`;
721
+ commentBody += `**Summary:** ${result.summary}\n`;
722
+ if (result.details) {
723
+ commentBody += `\n### Details\n\n${result.details}\n`;
724
+ }
725
+ if (result.artifacts && result.artifacts.length > 0) {
726
+ commentBody += `\n### Artifacts\n\n`;
727
+ for (const artifact of result.artifacts) {
728
+ commentBody += `- ${artifact}\n`;
729
+ }
730
+ }
731
+ if (result.error) {
732
+ commentBody += `\n### Error\n\n\`\`\`\n${result.error}\n\`\`\`\n`;
733
+ }
734
+ // Post the comment
735
+ await this.apiRequest("POST", `/repos/${owner}/${repo}/issues/${issueNumber}/comments`, { body: commentBody });
736
+ // Remove the in-progress label
737
+ try {
738
+ await this.apiRequest("DELETE", `/repos/${owner}/${repo}/issues/${issueNumber}/labels/${encodeURIComponent(this.labels.in_progress)}`);
739
+ }
740
+ catch {
741
+ // Ignore errors removing the label
742
+ }
743
+ // Close the issue if the outcome was success
744
+ if (result.outcome === "success") {
745
+ await this.apiRequest("PATCH", `/repos/${owner}/${repo}/issues/${issueNumber}`, { state: "closed", state_reason: "completed" });
746
+ }
747
+ }
748
+ /**
749
+ * Release a claimed work item by removing in-progress label and re-adding ready label
750
+ */
751
+ async releaseWork(workItemId, options) {
752
+ const { owner, repo } = this.getOwnerRepo();
753
+ const issueNumber = this.parseWorkItemId(workItemId);
754
+ try {
755
+ // Add a comment if requested
756
+ if (options?.addComment && options?.reason) {
757
+ await this.apiRequest("POST", `/repos/${owner}/${repo}/issues/${issueNumber}/comments`, { body: `⏸️ **Work Released**\n\nReason: ${options.reason}` });
758
+ }
759
+ // Remove the in-progress label
760
+ try {
761
+ await this.apiRequest("DELETE", `/repos/${owner}/${repo}/issues/${issueNumber}/labels/${encodeURIComponent(this.labels.in_progress)}`);
762
+ }
763
+ catch {
764
+ // Ignore errors removing the label
765
+ }
766
+ // Re-add the ready label if cleanup_on_failure is true (default)
767
+ if (this.config.cleanup_on_failure !== false) {
768
+ await this.apiRequest("POST", `/repos/${owner}/${repo}/issues/${issueNumber}/labels`, { labels: [this.labels.ready] });
769
+ }
770
+ return { success: true };
771
+ }
772
+ catch (error) {
773
+ return {
774
+ success: false,
775
+ message: `Failed to release issue: ${error instanceof Error ? error.message : String(error)}`,
776
+ };
777
+ }
778
+ }
779
+ /**
780
+ * Get a specific work item by ID
781
+ */
782
+ async getWork(workItemId) {
783
+ const { owner, repo } = this.getOwnerRepo();
784
+ const issueNumber = this.parseWorkItemId(workItemId);
785
+ try {
786
+ const issue = await this.apiRequest("GET", `/repos/${owner}/${repo}/issues/${issueNumber}`);
787
+ return this.issueToWorkItem(issue);
788
+ }
789
+ catch (error) {
790
+ if (error instanceof GitHubAPIError && error.statusCode === 404) {
791
+ return undefined;
792
+ }
793
+ throw error;
794
+ }
795
+ }
796
+ }
797
+ /**
798
+ * Factory function for creating GitHub adapters
799
+ */
800
+ export function createGitHubAdapter(config) {
801
+ return new GitHubWorkSourceAdapter(config);
802
+ }
803
+ //# sourceMappingURL=github.js.map