@shivaedev/work-fleet 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (502) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +60 -18
  3. package/dist/DispatchRequest.d.ts +13 -0
  4. package/dist/DispatchRequest.d.ts.map +1 -0
  5. package/dist/DispatchRequest.js +12 -0
  6. package/dist/DispatchRequest.js.map +1 -0
  7. package/dist/admit.d.ts +11 -0
  8. package/dist/admit.d.ts.map +1 -1
  9. package/dist/attention.d.ts +1 -0
  10. package/dist/attention.d.ts.map +1 -1
  11. package/dist/attention.js +4 -0
  12. package/dist/attention.js.map +1 -1
  13. package/dist/board/fleetField.d.ts +1 -0
  14. package/dist/board/fleetField.d.ts.map +1 -1
  15. package/dist/board/fleetField.js +4 -0
  16. package/dist/board/fleetField.js.map +1 -1
  17. package/dist/board/markdown.d.ts.map +1 -1
  18. package/dist/board/markdown.js +7 -2
  19. package/dist/board/markdown.js.map +1 -1
  20. package/dist/board/publish.d.ts +2 -0
  21. package/dist/board/publish.d.ts.map +1 -1
  22. package/dist/board/publishReport.d.ts +1 -0
  23. package/dist/board/publishReport.d.ts.map +1 -1
  24. package/dist/board/readPass.d.ts +1 -0
  25. package/dist/board/readPass.d.ts.map +1 -1
  26. package/dist/cli.js +13 -6
  27. package/dist/cli.js.map +1 -1
  28. package/dist/engine/abandonStalled.d.ts +1171 -1004
  29. package/dist/engine/abandonStalled.d.ts.map +1 -1
  30. package/dist/engine/admissionOrder.d.ts +1091 -0
  31. package/dist/engine/admissionOrder.d.ts.map +1 -0
  32. package/dist/engine/admissionOrder.js +15 -0
  33. package/dist/engine/admissionOrder.js.map +1 -0
  34. package/dist/engine/applyResolution.d.ts +20 -0
  35. package/dist/engine/applyResolution.d.ts.map +1 -1
  36. package/dist/engine/approveBrief.d.ts +31 -0
  37. package/dist/engine/approveBrief.d.ts.map +1 -1
  38. package/dist/engine/authorizeResolution.d.ts +1160 -1003
  39. package/dist/engine/authorizeResolution.d.ts.map +1 -1
  40. package/dist/engine/boardBrief.d.ts +32 -0
  41. package/dist/engine/boardBrief.d.ts.map +1 -1
  42. package/dist/engine/capacityRetry.d.ts +3488 -3007
  43. package/dist/engine/capacityRetry.d.ts.map +1 -1
  44. package/dist/engine/closeDecisions.d.ts +20 -0
  45. package/dist/engine/closeDecisions.d.ts.map +1 -1
  46. package/dist/engine/completeBoardWork.d.ts +20 -0
  47. package/dist/engine/completeBoardWork.d.ts.map +1 -1
  48. package/dist/engine/completeReport.d.ts +21 -0
  49. package/dist/engine/completeReport.d.ts.map +1 -1
  50. package/dist/engine/continueCommand.d.ts +10 -0
  51. package/dist/engine/continueCommand.d.ts.map +1 -1
  52. package/dist/engine/continueWorker.d.ts +20 -0
  53. package/dist/engine/continueWorker.d.ts.map +1 -1
  54. package/dist/engine/deliverPendingAnswers.d.ts +1085 -898
  55. package/dist/engine/deliverPendingAnswers.d.ts.map +1 -1
  56. package/dist/engine/dispatchCommand.d.ts +14 -1
  57. package/dist/engine/dispatchCommand.d.ts.map +1 -1
  58. package/dist/engine/dispatchCommand.js +25 -5
  59. package/dist/engine/dispatchCommand.js.map +1 -1
  60. package/dist/engine/ensureDecisionOwner.d.ts +20 -0
  61. package/dist/engine/ensureDecisionOwner.d.ts.map +1 -1
  62. package/dist/engine/ensurePublication.d.ts +20 -0
  63. package/dist/engine/ensurePublication.d.ts.map +1 -1
  64. package/dist/engine/failureContext.d.ts +1 -0
  65. package/dist/engine/failureContext.d.ts.map +1 -1
  66. package/dist/engine/failureContext.js.map +1 -1
  67. package/dist/engine/failureMessage.js +11 -1
  68. package/dist/engine/failureMessage.js.map +1 -1
  69. package/dist/engine/health.d.ts +10 -0
  70. package/dist/engine/health.d.ts.map +1 -1
  71. package/dist/engine/inspectRenewal.d.ts +21 -0
  72. package/dist/engine/inspectRenewal.d.ts.map +1 -1
  73. package/dist/engine/launchInput.d.ts +10 -0
  74. package/dist/engine/launchInput.d.ts.map +1 -1
  75. package/dist/engine/launchReceipt.d.ts +10 -0
  76. package/dist/engine/launchReceipt.d.ts.map +1 -1
  77. package/dist/engine/localOnlyCriteria.d.ts +1 -0
  78. package/dist/engine/localOnlyCriteria.d.ts.map +1 -1
  79. package/dist/engine/localOnlyCriteria.js +5 -1
  80. package/dist/engine/localOnlyCriteria.js.map +1 -1
  81. package/dist/engine/makeAdopt.d.ts +21 -0
  82. package/dist/engine/makeAdopt.d.ts.map +1 -1
  83. package/dist/engine/makeAdvance.d.ts +10 -0
  84. package/dist/engine/makeAdvance.d.ts.map +1 -1
  85. package/dist/engine/makeDeliver.d.ts +20 -0
  86. package/dist/engine/makeDeliver.d.ts.map +1 -1
  87. package/dist/engine/makeHuman.d.ts +20 -0
  88. package/dist/engine/makeHuman.d.ts.map +1 -1
  89. package/dist/engine/makeLaunch.d.ts +20 -0
  90. package/dist/engine/makeLaunch.d.ts.map +1 -1
  91. package/dist/engine/makeLaunch.js +4 -13
  92. package/dist/engine/makeLaunch.js.map +1 -1
  93. package/dist/engine/makeObserve.d.ts +20 -0
  94. package/dist/engine/makeObserve.d.ts.map +1 -1
  95. package/dist/engine/makeReview.d.ts +21 -0
  96. package/dist/engine/makeReview.d.ts.map +1 -1
  97. package/dist/engine/makeState.d.ts +1164 -1010
  98. package/dist/engine/makeState.d.ts.map +1 -1
  99. package/dist/engine/makeState.js +19 -11
  100. package/dist/engine/makeState.js.map +1 -1
  101. package/dist/engine/makeValidate.d.ts +21 -0
  102. package/dist/engine/makeValidate.d.ts.map +1 -1
  103. package/dist/engine/mergeReadiness.d.ts +1425 -1764
  104. package/dist/engine/mergeReadiness.d.ts.map +1 -1
  105. package/dist/engine/mergeReadiness.js +3 -4
  106. package/dist/engine/mergeReadiness.js.map +1 -1
  107. package/dist/engine/observeDeliveryFailure.d.ts +1170 -1003
  108. package/dist/engine/observeDeliveryFailure.d.ts.map +1 -1
  109. package/dist/engine/observeRenewal.d.ts +33 -0
  110. package/dist/engine/observeRenewal.d.ts.map +1 -1
  111. package/dist/engine/obtainReview.d.ts +11 -0
  112. package/dist/engine/obtainReview.d.ts.map +1 -1
  113. package/dist/engine/obtainReview.js +2 -1
  114. package/dist/engine/obtainReview.js.map +1 -1
  115. package/dist/engine/operationRecord.d.ts +1898 -1779
  116. package/dist/engine/operationRecord.d.ts.map +1 -1
  117. package/dist/engine/persistTerminalReceipts.d.ts +20 -0
  118. package/dist/engine/persistTerminalReceipts.d.ts.map +1 -1
  119. package/dist/engine/prepareCommand.d.ts +10 -0
  120. package/dist/engine/prepareCommand.d.ts.map +1 -1
  121. package/dist/engine/prepareCommand.js +1 -1
  122. package/dist/engine/prepareCommand.js.map +1 -1
  123. package/dist/engine/queueAnswerCommand.d.ts +10 -0
  124. package/dist/engine/queueAnswerCommand.d.ts.map +1 -1
  125. package/dist/engine/readOrContinueReview.d.ts +11 -0
  126. package/dist/engine/readOrContinueReview.d.ts.map +1 -1
  127. package/dist/engine/readOrContinueReview.js +2 -1
  128. package/dist/engine/readOrContinueReview.js.map +1 -1
  129. package/dist/engine/readResolution.d.ts +1 -0
  130. package/dist/engine/readResolution.d.ts.map +1 -1
  131. package/dist/engine/readRetry.d.ts +2344 -2010
  132. package/dist/engine/readRetry.d.ts.map +1 -1
  133. package/dist/engine/readRetry.js +20 -1
  134. package/dist/engine/readRetry.js.map +1 -1
  135. package/dist/engine/reassignCommand.d.ts +10 -0
  136. package/dist/engine/reassignCommand.d.ts.map +1 -1
  137. package/dist/engine/reconcileAttempt.d.ts +12 -1
  138. package/dist/engine/reconcileAttempt.d.ts.map +1 -1
  139. package/dist/engine/reconcileAttempt.js +2 -1
  140. package/dist/engine/reconcileAttempt.js.map +1 -1
  141. package/dist/engine/reconcileCommand.d.ts +10 -0
  142. package/dist/engine/reconcileCommand.d.ts.map +1 -1
  143. package/dist/engine/recordResponseCommand.d.ts +551 -0
  144. package/dist/engine/recordResponseCommand.d.ts.map +1 -0
  145. package/dist/engine/recordResponseCommand.js +30 -0
  146. package/dist/engine/recordResponseCommand.js.map +1 -0
  147. package/dist/engine/recoverStall.d.ts +1172 -1004
  148. package/dist/engine/recoverStall.d.ts.map +1 -1
  149. package/dist/engine/recoverStall.js +1 -1
  150. package/dist/engine/recoverStall.js.map +1 -1
  151. package/dist/engine/recoverUnobservable.d.ts +1172 -1004
  152. package/dist/engine/recoverUnobservable.d.ts.map +1 -1
  153. package/dist/engine/refreshBrief.d.ts +2344 -2009
  154. package/dist/engine/refreshBrief.d.ts.map +1 -1
  155. package/dist/engine/refreshReviewResult.d.ts +21 -0
  156. package/dist/engine/refreshReviewResult.d.ts.map +1 -1
  157. package/dist/engine/releaseClosedPreparation.d.ts +1171 -1003
  158. package/dist/engine/releaseClosedPreparation.d.ts.map +1 -1
  159. package/dist/engine/releaseExecution.d.ts +1170 -1003
  160. package/dist/engine/releaseExecution.d.ts.map +1 -1
  161. package/dist/engine/releaseFinishedWork.d.ts +10 -0
  162. package/dist/engine/releaseFinishedWork.d.ts.map +1 -1
  163. package/dist/engine/repository.d.ts +1115 -1003
  164. package/dist/engine/repository.d.ts.map +1 -1
  165. package/dist/engine/repository.js +12 -12
  166. package/dist/engine/repository.js.map +1 -1
  167. package/dist/engine/requestConflictRepair.d.ts +1170 -1003
  168. package/dist/engine/requestConflictRepair.d.ts.map +1 -1
  169. package/dist/engine/requestRework.d.ts +20 -0
  170. package/dist/engine/requestRework.d.ts.map +1 -1
  171. package/dist/engine/resolveCommand.d.ts +10 -0
  172. package/dist/engine/resolveCommand.d.ts.map +1 -1
  173. package/dist/engine/resolveExecution.d.ts +1170 -1003
  174. package/dist/engine/resolveExecution.d.ts.map +1 -1
  175. package/dist/engine/resumeAttempt.d.ts +1171 -1003
  176. package/dist/engine/resumeAttempt.d.ts.map +1 -1
  177. package/dist/engine/retireUnacknowledged.d.ts +20 -0
  178. package/dist/engine/retireUnacknowledged.d.ts.map +1 -1
  179. package/dist/engine/retryTerminal.d.ts +1160 -1003
  180. package/dist/engine/retryTerminal.d.ts.map +1 -1
  181. package/dist/engine/saveLaunch.d.ts +10598 -0
  182. package/dist/engine/saveLaunch.d.ts.map +1 -0
  183. package/dist/engine/saveLaunch.js +30 -0
  184. package/dist/engine/saveLaunch.js.map +1 -0
  185. package/dist/engine/scheduler.d.ts +21 -1
  186. package/dist/engine/scheduler.d.ts.map +1 -1
  187. package/dist/engine/scheduler.js +3 -5
  188. package/dist/engine/scheduler.js.map +1 -1
  189. package/dist/engine/sessionCleanup.d.ts +20 -0
  190. package/dist/engine/sessionCleanup.d.ts.map +1 -1
  191. package/dist/engine/settleMergedDelivery.d.ts +1170 -1003
  192. package/dist/engine/settleMergedDelivery.d.ts.map +1 -1
  193. package/dist/engine/steerBrief.d.ts +1075 -898
  194. package/dist/engine/steerBrief.d.ts.map +1 -1
  195. package/dist/engine/submissionFailure.d.ts +1150 -1003
  196. package/dist/engine/submissionFailure.d.ts.map +1 -1
  197. package/dist/engine/submitLaunch.d.ts +1160 -1003
  198. package/dist/engine/submitLaunch.d.ts.map +1 -1
  199. package/dist/engine/views.d.ts.map +1 -1
  200. package/dist/engine/views.js +4 -0
  201. package/dist/engine/views.js.map +1 -1
  202. package/dist/engine/workerReport.d.ts +30 -0
  203. package/dist/engine/workerReport.d.ts.map +1 -1
  204. package/dist/fleet.d.ts +2716 -983
  205. package/dist/fleet.d.ts.map +1 -1
  206. package/dist/fleet.js +4 -0
  207. package/dist/fleet.js.map +1 -1
  208. package/dist/host/actionCommands.d.ts.map +1 -1
  209. package/dist/host/actionCommands.js +7 -6
  210. package/dist/host/actionCommands.js.map +1 -1
  211. package/dist/host/admission.d.ts +24 -1
  212. package/dist/host/admission.d.ts.map +1 -1
  213. package/dist/host/admission.js +2 -2
  214. package/dist/host/admission.js.map +1 -1
  215. package/dist/host/answerWork.d.ts +12 -2
  216. package/dist/host/answerWork.d.ts.map +1 -1
  217. package/dist/host/answerWork.js +19 -23
  218. package/dist/host/answerWork.js.map +1 -1
  219. package/dist/host/commands.d.ts +28 -4
  220. package/dist/host/commands.d.ts.map +1 -1
  221. package/dist/host/commands.js +19 -5
  222. package/dist/host/commands.js.map +1 -1
  223. package/dist/host/config.d.ts +77 -1
  224. package/dist/host/config.d.ts.map +1 -1
  225. package/dist/host/config.js +37 -9
  226. package/dist/host/config.js.map +1 -1
  227. package/dist/host/configSettings.d.ts +486 -0
  228. package/dist/host/configSettings.d.ts.map +1 -0
  229. package/dist/host/configSettings.js +113 -0
  230. package/dist/host/configSettings.js.map +1 -0
  231. package/dist/host/daemon.d.ts +302 -607
  232. package/dist/host/daemon.d.ts.map +1 -1
  233. package/dist/host/daemon.js +2 -1
  234. package/dist/host/daemon.js.map +1 -1
  235. package/dist/host/daemonClient.d.ts +300 -605
  236. package/dist/host/daemonClient.d.ts.map +1 -1
  237. package/dist/host/daemonClient.js +28 -7
  238. package/dist/host/daemonClient.js.map +1 -1
  239. package/dist/host/daemonContract.d.ts +882 -1987
  240. package/dist/host/daemonContract.d.ts.map +1 -1
  241. package/dist/host/daemonContract.js +12 -4
  242. package/dist/host/daemonContract.js.map +1 -1
  243. package/dist/host/daemonDiagnostics.d.ts.map +1 -1
  244. package/dist/host/daemonDiagnostics.js +9 -3
  245. package/dist/host/daemonDiagnostics.js.map +1 -1
  246. package/dist/host/daemonHandlers.d.ts +2 -1
  247. package/dist/host/daemonHandlers.d.ts.map +1 -1
  248. package/dist/host/daemonHandlers.js +20 -7
  249. package/dist/host/daemonHandlers.js.map +1 -1
  250. package/dist/host/daemonSnapshots.d.ts.map +1 -1
  251. package/dist/host/daemonSnapshots.js +21 -1
  252. package/dist/host/daemonSnapshots.js.map +1 -1
  253. package/dist/host/dispatchRequests.d.ts +30 -0
  254. package/dist/host/dispatchRequests.d.ts.map +1 -0
  255. package/dist/host/dispatchRequests.js +40 -0
  256. package/dist/host/dispatchRequests.js.map +1 -0
  257. package/dist/host/follow.js +1 -1
  258. package/dist/host/follow.js.map +1 -1
  259. package/dist/host/followState.d.ts.map +1 -1
  260. package/dist/host/followState.js +5 -4
  261. package/dist/host/followState.js.map +1 -1
  262. package/dist/host/github.d.ts +5 -6
  263. package/dist/host/github.d.ts.map +1 -1
  264. package/dist/host/github.js +3 -4
  265. package/dist/host/github.js.map +1 -1
  266. package/dist/host/githubApi.d.ts +15 -0
  267. package/dist/host/githubApi.d.ts.map +1 -0
  268. package/dist/host/githubApi.js +117 -0
  269. package/dist/host/githubApi.js.map +1 -0
  270. package/dist/host/githubChecks.d.ts +13 -3
  271. package/dist/host/githubChecks.d.ts.map +1 -1
  272. package/dist/host/githubChecks.js +5 -5
  273. package/dist/host/githubChecks.js.map +1 -1
  274. package/dist/host/githubDelivery.d.ts +13 -1
  275. package/dist/host/githubDelivery.d.ts.map +1 -1
  276. package/dist/host/githubDelivery.js +39 -6
  277. package/dist/host/githubDelivery.js.map +1 -1
  278. package/dist/host/githubRateLimit.d.ts +23 -0
  279. package/dist/host/githubRateLimit.d.ts.map +1 -0
  280. package/dist/host/githubRateLimit.js +70 -0
  281. package/dist/host/githubRateLimit.js.map +1 -0
  282. package/dist/host/githubRequiredChecks.d.ts +1 -2
  283. package/dist/host/githubRequiredChecks.d.ts.map +1 -1
  284. package/dist/host/githubRequiredChecks.js +2 -2
  285. package/dist/host/githubRequiredChecks.js.map +1 -1
  286. package/dist/host/githubResponse.d.ts +10 -0
  287. package/dist/host/githubResponse.d.ts.map +1 -0
  288. package/dist/host/githubResponse.js +29 -0
  289. package/dist/host/githubResponse.js.map +1 -0
  290. package/dist/host/githubResult.d.ts +3 -2
  291. package/dist/host/githubResult.d.ts.map +1 -1
  292. package/dist/host/githubResult.js +9 -12
  293. package/dist/host/githubResult.js.map +1 -1
  294. package/dist/host/githubTransport.d.ts +13 -0
  295. package/dist/host/githubTransport.d.ts.map +1 -0
  296. package/dist/host/{githubRead.js → githubTransport.js} +9 -10
  297. package/dist/host/githubTransport.js.map +1 -0
  298. package/dist/host/integrations.d.ts +6 -0
  299. package/dist/host/integrations.d.ts.map +1 -0
  300. package/dist/host/integrations.js +34 -0
  301. package/dist/host/integrations.js.map +1 -0
  302. package/dist/host/liveConfig.d.ts +21 -0
  303. package/dist/host/liveConfig.d.ts.map +1 -0
  304. package/dist/host/liveConfig.js +74 -0
  305. package/dist/host/liveConfig.js.map +1 -0
  306. package/dist/host/providers.d.ts.map +1 -1
  307. package/dist/host/providers.js +2 -2
  308. package/dist/host/providers.js.map +1 -1
  309. package/dist/host/readWaitStates.d.ts.map +1 -1
  310. package/dist/host/readWaitStates.js +8 -18
  311. package/dist/host/readWaitStates.js.map +1 -1
  312. package/dist/host/reassignWork.d.ts +10 -0
  313. package/dist/host/reassignWork.d.ts.map +1 -1
  314. package/dist/host/recordDecisionResponse.d.ts +1 -0
  315. package/dist/host/recordDecisionResponse.d.ts.map +1 -1
  316. package/dist/host/releaseWork.d.ts +24 -3
  317. package/dist/host/releaseWork.d.ts.map +1 -1
  318. package/dist/host/releaseWork.js +11 -7
  319. package/dist/host/releaseWork.js.map +1 -1
  320. package/dist/host/renderStatus.d.ts +2 -2
  321. package/dist/host/renderStatus.d.ts.map +1 -1
  322. package/dist/host/renderStatus.js +8 -2
  323. package/dist/host/renderStatus.js.map +1 -1
  324. package/dist/host/reportBoard.d.ts +1 -1
  325. package/dist/host/reportBoard.d.ts.map +1 -1
  326. package/dist/host/reportBoard.js +6 -3
  327. package/dist/host/reportBoard.js.map +1 -1
  328. package/dist/host/reportPublication.d.ts +2 -1
  329. package/dist/host/reportPublication.d.ts.map +1 -1
  330. package/dist/host/reportPublication.js +5 -2
  331. package/dist/host/reportPublication.js.map +1 -1
  332. package/dist/host/runtime.d.ts +8 -6
  333. package/dist/host/runtime.d.ts.map +1 -1
  334. package/dist/host/runtime.js +37 -46
  335. package/dist/host/runtime.js.map +1 -1
  336. package/dist/host/snapshotStream.d.ts.map +1 -1
  337. package/dist/host/snapshotStream.js +4 -1
  338. package/dist/host/snapshotStream.js.map +1 -1
  339. package/dist/host/status.d.ts +22 -2
  340. package/dist/host/status.d.ts.map +1 -1
  341. package/dist/host/status.js +19 -5
  342. package/dist/host/status.js.map +1 -1
  343. package/dist/host/statusLayer.d.ts +3 -3
  344. package/dist/host/statusLayer.d.ts.map +1 -1
  345. package/dist/host/statusLayer.js +13 -3
  346. package/dist/host/statusLayer.js.map +1 -1
  347. package/dist/host/waitOutcome.d.ts.map +1 -1
  348. package/dist/host/waitOutcome.js +5 -2
  349. package/dist/host/waitOutcome.js.map +1 -1
  350. package/dist/model.d.ts +39 -0
  351. package/dist/model.d.ts.map +1 -1
  352. package/dist/model.js +5 -0
  353. package/dist/model.js.map +1 -1
  354. package/dist/operations.d.ts +270 -0
  355. package/dist/operations.d.ts.map +1 -1
  356. package/dist/packageInfo.d.ts +1 -1
  357. package/dist/packageInfo.js +1 -1
  358. package/dist/policy.d.ts +7 -0
  359. package/dist/policy.d.ts.map +1 -1
  360. package/dist/policy.js +2 -0
  361. package/dist/policy.js.map +1 -1
  362. package/dist/providers/codexCloud.d.ts +2 -0
  363. package/dist/providers/codexCloud.d.ts.map +1 -1
  364. package/dist/providers/codexCloud.js +2 -1
  365. package/dist/providers/codexCloud.js.map +1 -1
  366. package/dist/qualifyDependencies.d.ts +10 -0
  367. package/dist/qualifyDependencies.d.ts.map +1 -1
  368. package/dist/scheduling/admission.d.ts +1635 -0
  369. package/dist/scheduling/admission.d.ts.map +1 -0
  370. package/dist/scheduling/admission.js +11 -0
  371. package/dist/scheduling/admission.js.map +1 -0
  372. package/dist/scheduling/areas.d.ts +30 -0
  373. package/dist/scheduling/areas.d.ts.map +1 -1
  374. package/dist/scheduling/backPressure.d.ts +1637 -0
  375. package/dist/scheduling/backPressure.d.ts.map +1 -0
  376. package/dist/scheduling/backPressure.js +44 -0
  377. package/dist/scheduling/backPressure.js.map +1 -0
  378. package/dist/storage/FleetStoreOptions.d.ts +2 -2
  379. package/dist/storage/FleetStoreOptions.d.ts.map +1 -1
  380. package/dist/storage/addShapeColumns.d.ts +5 -0
  381. package/dist/storage/addShapeColumns.d.ts.map +1 -0
  382. package/dist/storage/addShapeColumns.js +24 -0
  383. package/dist/storage/addShapeColumns.js.map +1 -0
  384. package/dist/storage/dispatchStore.d.ts +47 -0
  385. package/dist/storage/dispatchStore.d.ts.map +1 -0
  386. package/dist/storage/dispatchStore.js +89 -0
  387. package/dist/storage/dispatchStore.js.map +1 -0
  388. package/dist/storage/errors.d.ts +2 -1
  389. package/dist/storage/errors.d.ts.map +1 -1
  390. package/dist/storage/errors.js +2 -1
  391. package/dist/storage/errors.js.map +1 -1
  392. package/dist/storage/fleetMigrations.d.ts +1 -1
  393. package/dist/storage/fleetMigrations.d.ts.map +1 -1
  394. package/dist/storage/fleetMigrations.js +24 -17
  395. package/dist/storage/fleetMigrations.js.map +1 -1
  396. package/dist/storage/listedRecords.d.ts +9 -0
  397. package/dist/storage/listedRecords.d.ts.map +1 -0
  398. package/dist/storage/listedRecords.js +40 -0
  399. package/dist/storage/listedRecords.js.map +1 -0
  400. package/dist/storage/makePostgresFleetStore.d.ts +2 -6
  401. package/dist/storage/makePostgresFleetStore.d.ts.map +1 -1
  402. package/dist/storage/makePostgresFleetStore.js +4 -10
  403. package/dist/storage/makePostgresFleetStore.js.map +1 -1
  404. package/dist/storage/model.d.ts +9 -0
  405. package/dist/storage/model.d.ts.map +1 -1
  406. package/dist/storage/model.js.map +1 -1
  407. package/dist/storage/shapeV14.d.ts +808 -0
  408. package/dist/storage/shapeV14.d.ts.map +1 -0
  409. package/dist/storage/shapeV14.js +18 -0
  410. package/dist/storage/shapeV14.js.map +1 -0
  411. package/package.json +6 -6
  412. package/src/DispatchRequest.ts +13 -0
  413. package/src/attention.ts +5 -0
  414. package/src/board/fleetField.ts +5 -0
  415. package/src/board/markdown.ts +7 -2
  416. package/src/cli.ts +24 -10
  417. package/src/engine/admissionOrder.ts +25 -0
  418. package/src/engine/dispatchCommand.ts +29 -5
  419. package/src/engine/failureContext.ts +1 -0
  420. package/src/engine/failureMessage.ts +12 -1
  421. package/src/engine/localOnlyCriteria.ts +6 -1
  422. package/src/engine/makeLaunch.ts +4 -17
  423. package/src/engine/makeState.ts +30 -26
  424. package/src/engine/mergeReadiness.ts +6 -5
  425. package/src/engine/obtainReview.ts +2 -1
  426. package/src/engine/prepareCommand.ts +1 -1
  427. package/src/engine/readOrContinueReview.ts +2 -1
  428. package/src/engine/readRetry.ts +23 -1
  429. package/src/engine/reconcileAttempt.ts +2 -1
  430. package/src/engine/recordResponseCommand.ts +38 -0
  431. package/src/engine/recoverStall.ts +1 -1
  432. package/src/engine/repository.ts +17 -16
  433. package/src/engine/saveLaunch.ts +45 -0
  434. package/src/engine/scheduler.ts +4 -6
  435. package/src/engine/views.ts +5 -0
  436. package/src/fleet.ts +4 -0
  437. package/src/host/actionCommands.ts +22 -7
  438. package/src/host/admission.ts +3 -3
  439. package/src/host/answerWork.ts +28 -27
  440. package/src/host/commands.ts +29 -8
  441. package/src/host/config.ts +46 -9
  442. package/src/host/configSettings.ts +124 -0
  443. package/src/host/daemon.ts +2 -1
  444. package/src/host/daemonClient.ts +42 -7
  445. package/src/host/daemonContract.ts +12 -4
  446. package/src/host/daemonDiagnostics.ts +11 -3
  447. package/src/host/daemonHandlers.ts +24 -7
  448. package/src/host/daemonSnapshots.ts +24 -1
  449. package/src/host/dispatchRequests.ts +53 -0
  450. package/src/host/follow.ts +1 -1
  451. package/src/host/followState.ts +6 -6
  452. package/src/host/github.ts +8 -6
  453. package/src/host/githubApi.ts +142 -0
  454. package/src/host/githubChecks.ts +6 -5
  455. package/src/host/githubDelivery.ts +47 -10
  456. package/src/host/githubRateLimit.ts +91 -0
  457. package/src/host/githubRequiredChecks.ts +2 -2
  458. package/src/host/githubResponse.ts +37 -0
  459. package/src/host/githubResult.ts +15 -17
  460. package/src/host/githubTransport.ts +31 -0
  461. package/src/host/integrations.ts +40 -0
  462. package/src/host/liveConfig.ts +134 -0
  463. package/src/host/providers.ts +4 -2
  464. package/src/host/readWaitStates.ts +8 -20
  465. package/src/host/releaseWork.ts +12 -9
  466. package/src/host/renderStatus.ts +10 -3
  467. package/src/host/reportBoard.ts +6 -3
  468. package/src/host/reportPublication.ts +6 -4
  469. package/src/host/runtime.ts +48 -56
  470. package/src/host/snapshotStream.ts +6 -1
  471. package/src/host/status.ts +24 -7
  472. package/src/host/statusLayer.ts +21 -5
  473. package/src/host/waitOutcome.ts +5 -2
  474. package/src/model.ts +6 -0
  475. package/src/packageInfo.ts +1 -1
  476. package/src/policy.ts +7 -0
  477. package/src/providers/codexCloud.ts +2 -1
  478. package/src/scheduling/admission.ts +19 -0
  479. package/src/scheduling/backPressure.ts +55 -0
  480. package/src/storage/FleetStoreOptions.ts +7 -2
  481. package/src/storage/addShapeColumns.ts +24 -0
  482. package/src/storage/dispatchStore.ts +104 -0
  483. package/src/storage/errors.ts +2 -1
  484. package/src/storage/fleetMigrations.ts +24 -17
  485. package/src/storage/listedRecords.ts +53 -0
  486. package/src/storage/makePostgresFleetStore.ts +4 -10
  487. package/src/storage/model.ts +7 -0
  488. package/src/storage/shapeV14.ts +19 -0
  489. package/dist/host/githubRead.d.ts +0 -5
  490. package/dist/host/githubRead.d.ts.map +0 -1
  491. package/dist/host/githubRead.js.map +0 -1
  492. package/dist/host/recordPayloads.d.ts +0 -4
  493. package/dist/host/recordPayloads.d.ts.map +0 -1
  494. package/dist/host/recordPayloads.js +0 -21
  495. package/dist/host/recordPayloads.js.map +0 -1
  496. package/dist/storage/migrateFleet.d.ts +0 -3
  497. package/dist/storage/migrateFleet.d.ts.map +0 -1
  498. package/dist/storage/migrateFleet.js +0 -11
  499. package/dist/storage/migrateFleet.js.map +0 -1
  500. package/src/host/githubRead.ts +0 -20
  501. package/src/host/recordPayloads.ts +0 -24
  502. package/src/storage/migrateFleet.ts +0 -11
@@ -0,0 +1,124 @@
1
+ import { Equal, Schema } from "effect";
2
+ import { type FleetHost, HostConfig } from "#host/config.ts";
3
+ import type { AppliedConfig } from "#host/liveConfig.ts";
4
+ import { FleetViews } from "#model.ts";
5
+
6
+ // A running daemon applies these settings from an edited file; every other setting waits for a restart.
7
+ export const RELOADABLE_SETTINGS = [
8
+ "areaMergeWaitMs",
9
+ "backlog",
10
+ "concurrency",
11
+ "intervalSeconds",
12
+ "launch",
13
+ "openPullRequests",
14
+ "quota",
15
+ "stallMinutes",
16
+ "worker.reviewerGuidance",
17
+ "worker.workerGuidance",
18
+ ] as const;
19
+
20
+ export const ConfigStatus = Schema.Struct({
21
+ file: Schema.optional(Schema.String),
22
+ loadedAt: Schema.Number,
23
+ rejected: Schema.optional(Schema.Struct({ field: Schema.optional(Schema.String), message: Schema.String })),
24
+ restartRequired: Schema.Array(Schema.String),
25
+ settings: HostConfig,
26
+ });
27
+ export type ConfigStatus = typeof ConfigStatus.Type;
28
+
29
+ export const DaemonStatus = Schema.Struct({ ...FleetViews.fields, config: Schema.optional(ConfigStatus) });
30
+
31
+ function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
32
+ return typeof value === "object" && value !== null && !Array.isArray(value);
33
+ }
34
+
35
+ export function setting(config: HostConfig, name: string): unknown {
36
+ return name.split(".").reduce<unknown>((value, key) => (isRecord(value) ? value[key] : undefined), config);
37
+ }
38
+
39
+ function appliedSettings(host: FleetHost): HostConfig {
40
+ return {
41
+ areaMergeWaitMs: host.areaMergeWaitMs,
42
+ backlog: host.backlog,
43
+ ...(host.cloud === undefined ? {} : { cloud: host.cloud }),
44
+ concurrency: host.concurrency,
45
+ database: { maxConnections: host.maxConnections },
46
+ intervalSeconds: host.interval,
47
+ launch: host.launch,
48
+ ...(host.local === undefined ? {} : { local: host.local }),
49
+ namespace: host.namespace,
50
+ ...(host.openPullRequests === undefined ? {} : { openPullRequests: host.openPullRequests }),
51
+ provider: host.provider,
52
+ quota: host.quota,
53
+ repository: host.repository,
54
+ ...(host.result === undefined ? {} : { result: host.result }),
55
+ stallMinutes: host.stallMinutes,
56
+ ...(host.worker === undefined ? {} : { worker: host.worker }),
57
+ };
58
+ }
59
+
60
+ function write(target: Record<string, unknown>, [key, ...rest]: readonly string[], value: unknown) {
61
+ if (key === undefined) {
62
+ return;
63
+ }
64
+ if (rest.length > 0) {
65
+ const child = target[key];
66
+ if (isRecord(child)) {
67
+ const copy = { ...child };
68
+ write(copy, rest, value);
69
+ target[key] = copy;
70
+ }
71
+ return;
72
+ }
73
+ if (value === undefined) {
74
+ delete target[key];
75
+ } else {
76
+ target[key] = value;
77
+ }
78
+ }
79
+
80
+ // A reloadable setting nested under a section that the running config lacks stays unapplied until a restart adds the section.
81
+ export function withReloadable(applied: HostConfig, next: HostConfig): Readonly<Record<string, unknown>> {
82
+ const merged: Record<string, unknown> = { ...applied };
83
+ for (const name of RELOADABLE_SETTINGS) {
84
+ write(merged, name.split("."), setting(next, name));
85
+ }
86
+ return merged;
87
+ }
88
+
89
+ export function changedSettings(left: unknown, right: unknown, prefix = ""): readonly string[] {
90
+ if (isRecord(left) && isRecord(right)) {
91
+ return [...new Set([...Object.keys(left), ...Object.keys(right)])]
92
+ .sort((first, second) => first.localeCompare(second))
93
+ .flatMap((key) => changedSettings(left[key], right[key], prefix === "" ? key : `${prefix}.${key}`));
94
+ }
95
+ return Equal.equals(left, right) ? [] : [prefix];
96
+ }
97
+
98
+ export function configStatus({ host, loadedAt, rejected, restartRequired }: AppliedConfig): ConfigStatus {
99
+ return {
100
+ ...(host.configFile === undefined ? {} : { file: host.configFile }),
101
+ loadedAt,
102
+ ...(rejected === undefined
103
+ ? {}
104
+ : { rejected: { ...(rejected.field === undefined ? {} : { field: rejected.field }), message: rejected.message } }),
105
+ restartRequired,
106
+ settings: appliedSettings(host),
107
+ };
108
+ }
109
+
110
+ export function renderConfig(config: ConfigStatus): string {
111
+ const applying = RELOADABLE_SETTINGS.flatMap((name) => {
112
+ const value = setting(config.settings, name);
113
+ return value === undefined ? [] : [`${name} ${typeof value === "object" ? JSON.stringify(value) : String(value)}`];
114
+ });
115
+ const rejected = config.rejected;
116
+ return [
117
+ `Config loaded ${new Date(config.loadedAt).toISOString()}${config.file === undefined ? "" : ` from ${config.file}`}`,
118
+ ` provider ${config.settings.provider}, ${applying.join(", ")}`,
119
+ ...(config.restartRequired.length === 0 ? [] : [` Restart to apply: ${config.restartRequired.join(", ")}`]),
120
+ ...(rejected === undefined
121
+ ? []
122
+ : [` Rejected edit${rejected.field === undefined ? "" : ` of ${rejected.field}`}, still applying the config above: ${rejected.message}`]),
123
+ ].join("\n");
124
+ }
@@ -5,6 +5,7 @@ import { type HostParts, hostLayer, hostScheduler, productionParts } from "#host
5
5
  import type { FleetHost } from "#host/config.ts";
6
6
  import { DaemonContract, RPC_PATH } from "#host/daemonContract.ts";
7
7
  import { daemonHandlers } from "#host/daemonHandlers.ts";
8
+ import { watchedConfig } from "#host/liveConfig.ts";
8
9
  import { fleetHandlers } from "#operations.ts";
9
10
 
10
11
  const { DAEMON_PORT: DEFAULT_DAEMON_PORT } = diagnosticsApi;
@@ -21,7 +22,7 @@ export function daemonHostOptions(host: FleetHost, parts: HostParts = production
21
22
  handlers: Layer.merge(fleetHandlers, Layer.unwrap(daemonHandlers(host))),
22
23
  policy: "loopback" as const,
23
24
  rpcPath: RPC_PATH,
24
- services: hostLayer(host, parts),
25
+ services: hostLayer(host, parts, watchedConfig(host)),
25
26
  };
26
27
  }
27
28
 
@@ -19,18 +19,53 @@ export function daemonClient(port: number = DAEMON_PORT) {
19
19
  );
20
20
  }
21
21
 
22
- export function daemonRequest<A, E, R>(port: number, use: (client: Effect.Success<ReturnType<typeof daemonClient>>) => Effect.Effect<A, E, R>) {
23
- return Effect.scoped(daemonClient(port).pipe(Effect.flatMap(use))).pipe(Effect.mapError((error) => daemonError(error, port)));
22
+ export function daemonRequest<A, E, R>(
23
+ port: number,
24
+ use: (client: Effect.Success<ReturnType<typeof daemonClient>>) => Effect.Effect<A, E, R>,
25
+ request = "request",
26
+ timeoutMs = 5000,
27
+ ) {
28
+ return Effect.scoped(daemonClient(port).pipe(Effect.flatMap(use))).pipe(
29
+ Effect.timeout(timeoutMs),
30
+ Effect.mapError((error) => daemonError(error, port, request)),
31
+ );
24
32
  }
25
33
 
26
- export function daemonError(error: unknown, port: number) {
27
- if (isFleetError(error)) {
34
+ export function daemonError(error: unknown, port: number, request = "request") {
35
+ if (isFleetError(error) && (error._tag !== "ServiceUnavailable" || request === "request")) {
28
36
  return error;
29
37
  }
30
- const transport = error instanceof RpcClientError && error.reason._tag === "HttpError" && error.reason.kind === "TransportError";
38
+ const refused = error instanceof RpcClientError && connectionRefused(error.reason);
39
+ const timeout = requestTimedOut(error);
40
+ const detail = error instanceof Error ? error.message : String(error);
41
+ const recording = request.startsWith("answer ") ? "; answer recording is unconfirmed; check status or watch before retrying" : "";
31
42
  return new ServiceUnavailable({
32
- message: transport
43
+ message: refused
33
44
  ? `No Fleet daemon is running at 127.0.0.1:${port}; start work-fleet serve <board-dir> --port ${port}`
34
- : `Fleet daemon communication failed at 127.0.0.1:${port}: ${error instanceof Error ? error.message : String(error)}`,
45
+ : `Fleet daemon ${request} ${timeout ? "timed out" : "failed"} at 127.0.0.1:${port}: ${detail}${recording}`,
35
46
  });
36
47
  }
48
+
49
+ function requestTimedOut(value: unknown, depth = 0): boolean {
50
+ if (typeof value !== "object" || value === null || depth > 8) {
51
+ return false;
52
+ }
53
+ return (
54
+ ("_tag" in value && value._tag === "TimeoutError")
55
+ || ("name" in value && value.name === "TimeoutError")
56
+ || ("code" in value && ["ETIMEDOUT", "UND_ERR_HEADERS_TIMEOUT", "UND_ERR_BODY_TIMEOUT", "UND_ERR_CONNECT_TIMEOUT"].includes(String(value.code)))
57
+ || ("cause" in value && requestTimedOut(value.cause, depth + 1))
58
+ || ("reason" in value && requestTimedOut(value.reason, depth + 1))
59
+ );
60
+ }
61
+
62
+ function connectionRefused(value: unknown, depth = 0): boolean {
63
+ if (typeof value !== "object" || value === null || depth > 8) {
64
+ return false;
65
+ }
66
+ return (
67
+ ("code" in value && value.code === "ECONNREFUSED")
68
+ || ("cause" in value && connectionRefused(value.cause, depth + 1))
69
+ || ("reason" in value && connectionRefused(value.reason, depth + 1))
70
+ );
71
+ }
@@ -3,8 +3,10 @@ import { contract } from "@shivaedev/effect-contract/contract.ts";
3
3
  import { command, query, stream } from "@shivaedev/effect-contract/operation.ts";
4
4
  import { RequestTracing } from "@shivaedev/platform/rpc/middleware.ts";
5
5
  import { diagnostics as diagnosticsApi } from "@shivaedev/work-board/integration/diagnostics.ts";
6
+ import { DispatchRequest } from "#DispatchRequest.ts";
6
7
  import { fleetRejections } from "#errors.ts";
7
- import { FleetRecord, FleetViews } from "#model.ts";
8
+ import { DaemonStatus } from "#host/configSettings.ts";
9
+ import { FleetRecord } from "#model.ts";
8
10
  import { FleetContract, fleetRecords } from "#operations.ts";
9
11
 
10
12
  const { DAEMON_RPC_PATH, DiagnosticsContract } = diagnosticsApi;
@@ -31,17 +33,23 @@ export const ClientContract = contract("daemon", {
31
33
  }),
32
34
  command("dispatch", {
33
35
  invalidates: ({ workId }) => [fleetRecords.list, fleetRecords.item(workId)],
34
- payload: { workId: Schema.String },
36
+ payload: { ignoreAreas: Schema.optional(Schema.Boolean), now: Schema.optional(Schema.Boolean), workId: Schema.String },
35
37
  rejections: fleetRejections,
36
- success: FleetRecord,
38
+ success: DispatchRequest,
37
39
  }),
38
40
  ],
39
41
  queries: [
42
+ query("dispatch-status", {
43
+ payload: { workId: Schema.String },
44
+ reads: ({ workId }) => [fleetRecords.item(workId)],
45
+ rejections: fleetRejections,
46
+ success: Schema.NullOr(DispatchRequest),
47
+ }),
40
48
  query("status", {
41
49
  payload: { workId: Schema.optional(Schema.String) },
42
50
  reads: ({ workId }) => [workId === undefined ? fleetRecords.list : fleetRecords.item(workId)],
43
51
  rejections: fleetRejections,
44
- success: FleetViews,
52
+ success: DaemonStatus,
45
53
  }),
46
54
  ],
47
55
  streams: [
@@ -1,5 +1,7 @@
1
1
  import { Effect } from "effect";
2
+ import { ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
2
3
  import { diagnostics as diagnosticsApi } from "@shivaedev/work-board/integration/diagnostics.ts";
4
+ import { failureMessage } from "#engine/failureMessage.ts";
3
5
  import { HealthSnapshot } from "#engine/health.ts";
4
6
  import { fleetItems } from "#host/fleetItems.ts";
5
7
 
@@ -7,14 +9,20 @@ const { codexLimits, DiagnosticsContract, DiagnosticsSources, diagnostics, githu
7
9
 
8
10
  export const daemonDiagnostics = Effect.gen(function* () {
9
11
  const provided = yield* Effect.serviceOption(DiagnosticsSources);
12
+ const fleet = yield* HealthSnapshot;
10
13
  const sources = {
11
14
  codex: provided._tag === "Some" ? provided.value.codex : codexLimits(),
12
- fleet: yield* HealthSnapshot,
13
- github: provided._tag === "Some" ? provided.value.github : githubLimits(),
15
+ fleet,
16
+ github: githubLimits(fleet),
14
17
  };
15
18
  const read = yield* diagnostics(sources);
16
19
  const items = yield* fleetItems;
17
20
  return DiagnosticsContract.toLayer(
18
- Effect.succeed(DiagnosticsContract.of({ "daemon.diagnostics": () => read, "daemon.fleetItems": () => items.pipe(Effect.orDie) })),
21
+ Effect.succeed(
22
+ DiagnosticsContract.of({
23
+ "daemon.diagnostics": () => read,
24
+ "daemon.fleetItems": () => items.pipe(Effect.mapError((error) => new ServiceUnavailable({ message: failureMessage(error) }))),
25
+ }),
26
+ ),
19
27
  );
20
28
  });
@@ -1,31 +1,48 @@
1
- import { Effect, Layer } from "effect";
1
+ import { Effect, Fiber, Layer } from "effect";
2
2
  import { exposeFailure } from "#exposeFailure.ts";
3
- import { dispatchApproved } from "#host/admission.ts";
4
3
  import { answerWork } from "#host/answerWork.ts";
5
4
  import type { FleetHost } from "#host/config.ts";
5
+ import { configStatus } from "#host/configSettings.ts";
6
6
  import { ClientContract } from "#host/daemonContract.ts";
7
7
  import { daemonDiagnostics } from "#host/daemonDiagnostics.ts";
8
8
  import { daemonSnapshots } from "#host/daemonSnapshots.ts";
9
+ import { dispatchRequests } from "#host/dispatchRequests.ts";
10
+ import { LiveConfig } from "#host/liveConfig.ts";
9
11
  import { nameStoredThreads } from "#host/nameThreads.ts";
10
- import { releaseItems } from "#host/releaseWork.ts";
12
+ import { decisionResponses, releaseItems } from "#host/releaseWork.ts";
11
13
  import { fleetStatus } from "#host/status.ts";
12
14
 
13
15
  export const daemonHandlers = Effect.fn("FleetDaemon.daemonHandlers")(function* (host: FleetHost) {
16
+ const scope = yield* Effect.scope;
14
17
  const follow = yield* daemonSnapshots(host);
15
18
  const diagnostics = yield* daemonDiagnostics;
19
+ const dispatch = yield* dispatchRequests(host);
20
+ const live = yield* LiveConfig;
21
+ const status = Effect.zipWith(fleetStatus(host), live.current, (views, applied) => ({ ...views, config: configStatus(applied) }));
22
+ // The server already accepts connections while handlers build, so the Board watcher starts beside them instead of delaying them.
23
+ const responses = yield* Layer.buildWithScope(decisionResponses(host), scope).pipe(Effect.forkIn(scope));
24
+ // A client that stops waiting closes its request; the Board response it asked for must still be recorded and applied.
25
+ function settle<A, E, R>(effect: Effect.Effect<A, E, R>) {
26
+ return Fiber.join(responses).pipe(
27
+ Effect.flatMap((context) => effect.pipe(Effect.provide(context))),
28
+ Effect.forkIn(scope),
29
+ Effect.flatMap(Fiber.join),
30
+ );
31
+ }
16
32
  return Layer.merge(
17
33
  diagnostics,
18
34
  ClientContract.toLayer(
19
35
  Effect.succeed(
20
36
  ClientContract.of({
21
37
  "daemon.answer": ({ workId, text, release }) =>
22
- exposeFailure(answerWork(host, workId, text, release).pipe(Effect.map(({ value }) => value))),
23
- "daemon.dispatch": ({ workId }) => exposeFailure(dispatchApproved(host, workId).pipe(Effect.map(({ value }) => value))),
38
+ exposeFailure(settle(answerWork(host, workId, text, release, true)).pipe(Effect.map(({ value }) => value))),
39
+ "daemon.dispatch": ({ workId, now, ignoreAreas }) => exposeFailure(dispatch.accept(workId, { ignoreAreas, now })),
40
+ "daemon.dispatch-status": ({ workId }) => exposeFailure(dispatch.get(workId)),
24
41
  "daemon.follow": ({ workIds, follow: continuous }) => follow(workIds, continuous),
25
42
  "daemon.name-threads": () => exposeFailure(nameStoredThreads(host)),
26
43
  "daemon.release": ({ workIds }) =>
27
- exposeFailure(releaseItems(host, workIds).pipe(Effect.map((records) => records.map(({ value }) => value)))),
28
- "daemon.status": ({ workId }) => exposeFailure(fleetStatus(host, workId)),
44
+ exposeFailure(settle(releaseItems(host, workIds)).pipe(Effect.map((records) => records.map(({ value }) => value)))),
45
+ "daemon.status": ({ workId }) => exposeFailure(workId === undefined ? status : fleetStatus(host, workId)),
29
46
  }),
30
47
  ),
31
48
  ),
@@ -2,15 +2,21 @@ import { Effect, Schedule, Stream } from "effect";
2
2
  import { SqlClient } from "effect/sql";
3
3
  import { AppChanges } from "@shivaedev/platform/appHost.ts";
4
4
  import { ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
5
+ import type { DispatchRequest } from "#DispatchRequest.ts";
5
6
  import { fleetRecords } from "#fleetRecords.ts";
6
7
  import type { FleetHost } from "#host/config.ts";
7
8
  import { readWaitStates } from "#host/readWaitStates.ts";
8
9
  import { snapshotStream } from "#host/snapshotStream.ts";
10
+ import type { State } from "#host/waitOutcome.ts";
11
+ import { dispatchStore } from "#storage/dispatchStore.ts";
9
12
 
10
13
  export const daemonSnapshots = Effect.fn("FleetDaemon.daemonSnapshots")(function* (host: FleetHost) {
11
14
  const sql = yield* SqlClient.SqlClient;
12
15
  const changes = yield* AppChanges;
13
- const read = readWaitStates(host).pipe(
16
+ const dispatches = yield* dispatchStore(host.namespace);
17
+ const read = Effect.gen(function* () {
18
+ return dispatchStates(yield* readWaitStates(host), yield* dispatches.list());
19
+ }).pipe(
14
20
  Effect.provideService(SqlClient.SqlClient, sql),
15
21
  Effect.retry({ schedule: Schedule.spaced("500 millis"), times: 19 }),
16
22
  Effect.mapError(() => new ServiceUnavailable({ message: "Fleet daemon could not read work states after 20 consecutive attempts" })),
@@ -22,3 +28,20 @@ export const daemonSnapshots = Effect.fn("FleetDaemon.daemonSnapshots")(function
22
28
  );
23
29
  return (ids: readonly string[], continuous = false) => snapshotStream(snapshots, ids, continuous);
24
30
  });
31
+
32
+ function dispatchStates(states: readonly (typeof State.Type)[], requests: readonly DispatchRequest[]) {
33
+ const result = new Map<string, typeof State.Type>(states.map((state) => [state.workId, state]));
34
+ for (const request of requests) {
35
+ const state = result.get(request.workId);
36
+ if (request.status === "completed" || (request.status === "failed" && state !== undefined && state.stage !== "prepared")) {
37
+ continue;
38
+ }
39
+ result.set(request.workId, {
40
+ attentionOwner: request.status === "pending" ? "automation" : "orchestrator",
41
+ blocker: request.status === "pending" ? "Dispatch recorded; preparing in the background" : `Dispatch failed: ${request.error}`,
42
+ stage: request.status === "pending" ? "dispatching" : "needs-human",
43
+ workId: request.workId,
44
+ });
45
+ }
46
+ return [...result.values()].sort((left, right) => left.workId.localeCompare(right.workId));
47
+ }
@@ -0,0 +1,53 @@
1
+ import { Cause, Effect } from "effect";
2
+ import { BadRequest, Forbidden } from "@shivaedev/platform/errors/taxonomy.ts";
3
+ import type { DispatchRequest } from "#DispatchRequest.ts";
4
+ import { failureMessage } from "#engine/failureMessage.ts";
5
+ import { dispatchApproved } from "#host/admission.ts";
6
+ import type { FleetHost } from "#host/config.ts";
7
+ import { BoardGateway } from "#policy.ts";
8
+ import { dispatchStore } from "#storage/dispatchStore.ts";
9
+
10
+ export const dispatchRequests = Effect.fn("FleetDaemon.dispatchRequests")(function* (host: FleetHost) {
11
+ const scope = yield* Effect.scope;
12
+ const board = yield* BoardGateway;
13
+ const store = yield* dispatchStore(host.namespace);
14
+ const running = new Set<string>();
15
+ const start = Effect.fn("FleetDaemon.start")(function* (request: DispatchRequest) {
16
+ if (running.has(request.id)) {
17
+ return;
18
+ }
19
+ running.add(request.id);
20
+ yield* dispatchApproved(host, request.workId, request.now ? { ignoreAreas: request.ignoreAreas === true } : undefined).pipe(
21
+ Effect.andThen(store.finish(request)),
22
+ Effect.catchCause((cause) =>
23
+ Cause.hasInterrupts(cause) ? Effect.failCause(cause) : store.finish(request, failureMessage(Cause.squash(cause))),
24
+ ),
25
+ Effect.catchCause((cause) => (Cause.hasInterrupts(cause) ? Effect.void : Effect.logError(cause))),
26
+ Effect.ensuring(Effect.sync(() => running.delete(request.id))),
27
+ Effect.forkIn(scope),
28
+ );
29
+ });
30
+ for (const request of yield* store.list()) {
31
+ if (request.status === "pending") {
32
+ yield* start(request);
33
+ }
34
+ }
35
+ return {
36
+ accept: Effect.fn("FleetDaemon.accept")(function* (
37
+ workId: string,
38
+ options: { readonly ignoreAreas?: boolean | undefined; readonly now?: boolean | undefined } = {},
39
+ ) {
40
+ if (options.ignoreAreas === true && options.now !== true) {
41
+ return yield* new BadRequest({ message: "Ignoring area claims needs a dispatch now request" });
42
+ }
43
+ const work = yield* board.get(workId);
44
+ if (!work.approved) {
45
+ return yield* new Forbidden({ message: `${workId} needs "fleet: approved" in its Board frontmatter` });
46
+ }
47
+ const request = yield* store.accept(workId, options.now === true ? { ignoreAreas: options.ignoreAreas === true } : undefined);
48
+ yield* start(request);
49
+ return request;
50
+ }),
51
+ get: store.get,
52
+ };
53
+ });
@@ -34,7 +34,7 @@ export function follow(options: FollowOptions, workIds: readonly string[]) {
34
34
  Stream.catch((error) => {
35
35
  failures += 1;
36
36
  if (!connected || isFleetError(error) || failures >= 20) {
37
- return Stream.fail(daemonError(error, port));
37
+ return Stream.fail(daemonError(error, port, "follow"));
38
38
  }
39
39
  return Stream.unwrap(Effect.sleep(options.intervalMs).pipe(Effect.as(connect())));
40
40
  }),
@@ -20,9 +20,7 @@ export function decodeFollowState(value: unknown): FollowState {
20
20
 
21
21
  export function unrecognizedState(state: FollowState): boolean {
22
22
  return (
23
- state.undecoded !== undefined
24
- || !Schema.is(FleetRecord.fields.stage)(state.stage)
25
- || (state.decision?.code !== undefined && !Schema.is(DecisionCode)(state.decision.code))
23
+ state.undecoded !== undefined || !knownStage(state.stage) || (state.decision?.code !== undefined && !Schema.is(DecisionCode)(state.decision.code))
26
24
  );
27
25
  }
28
26
 
@@ -35,7 +33,9 @@ export function followOutcome(state: FollowState): WaitOutcome | undefined {
35
33
  if (code !== undefined && !Schema.is(DecisionCode)(code)) {
36
34
  return { code: outcome?.code ?? WAIT_EXIT.error, summary: `${outcome?.summary ?? `${state.workId} ${state.stage}`}: ${code}` };
37
35
  }
38
- return (
39
- outcome ?? (Schema.is(FleetRecord.fields.stage)(state.stage) ? undefined : { code: WAIT_EXIT.error, summary: `${state.workId} ${state.stage}` })
40
- );
36
+ return outcome ?? (knownStage(state.stage) ? undefined : { code: WAIT_EXIT.error, summary: `${state.workId} ${state.stage}` });
37
+ }
38
+
39
+ function knownStage(stage: string) {
40
+ return stage === "dispatching" || Schema.is(FleetRecord.fields.stage)(stage);
41
41
  }
@@ -1,6 +1,6 @@
1
1
  import { Effect, Schema } from "effect";
2
- import { ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
3
2
  import type { FleetError } from "#errors.ts";
3
+ import { githubFailure } from "#host/githubRateLimit.ts";
4
4
 
5
5
  const Sha = Schema.String.check(Schema.isPattern(/^[a-f\d]{40}$/u));
6
6
  const Positive = Schema.Int.check(Schema.isGreaterThan(0));
@@ -20,13 +20,15 @@ export const FilePages = Schema.Array(
20
20
  ),
21
21
  ),
22
22
  );
23
- export const Repository = Schema.Struct({ nameWithOwner: Schema.String });
24
23
 
25
- export type GithubRead = (args: readonly string[]) => Effect.Effect<string, FleetError>;
24
+ export interface GithubRead {
25
+ readonly get: (endpoint: string) => Effect.Effect<string, FleetError>;
26
+ readonly pages: (endpoint: string) => Effect.Effect<string, FleetError>;
27
+ }
26
28
 
27
- export function githubJson<TSchema extends Schema.Top>(schema: TSchema, read: GithubRead, args: readonly string[]) {
28
- return read(args).pipe(
29
+ export function githubJson<TSchema extends Schema.Top>(schema: TSchema, read: Effect.Effect<string, FleetError>) {
30
+ return read.pipe(
29
31
  Effect.flatMap(Schema.decodeUnknownEffect(Schema.fromJsonString(schema))),
30
- Effect.mapError(() => new ServiceUnavailable({ message: "GitHub observation failed or returned invalid data" })),
32
+ Effect.mapError(githubFailure("GitHub observation failed or returned invalid data")),
31
33
  );
32
34
  }
@@ -0,0 +1,142 @@
1
+ import { Clock, Effect, Layer } from "effect";
2
+ import { defineService } from "@shivaedev/effect-service/define-service.ts";
3
+ import { ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
4
+ import {
5
+ type GithubObservation,
6
+ type GithubPause,
7
+ githubPaused,
8
+ pace,
9
+ type RecordedObservation,
10
+ rateLimitWait,
11
+ readObservation,
12
+ reservedUntil,
13
+ } from "#host/githubRateLimit.ts";
14
+ import { nextLink, type ParsedResponse, parseResponse, requestResource } from "#host/githubResponse.ts";
15
+ import { GithubTransport, ghTransport } from "#host/githubTransport.ts";
16
+
17
+ interface Page {
18
+ readonly body: string;
19
+ readonly etag: string | undefined;
20
+ readonly next: string | undefined;
21
+ }
22
+ interface Block {
23
+ readonly failures: number;
24
+ readonly until: number;
25
+ }
26
+
27
+ const CACHE_LIMIT = 1000;
28
+ const PAGE_LIMIT = 100;
29
+
30
+ export const GithubApi = defineService({
31
+ id: "Fleet/GithubApi",
32
+ initialize: Effect.gen(function* () {
33
+ return {
34
+ blocks: new Map<string, Block>(),
35
+ cache: new Map<string, Page>(),
36
+ observations: new Map<string, RecordedObservation>(),
37
+ transport: yield* GithubTransport,
38
+ };
39
+ }),
40
+ methods: (state) => {
41
+ function remember(url: string, page: Page) {
42
+ state.cache.delete(url);
43
+ state.cache.set(url, page);
44
+ for (const key of state.cache.keys()) {
45
+ if (state.cache.size <= CACHE_LIMIT) {
46
+ break;
47
+ }
48
+ state.cache.delete(key);
49
+ }
50
+ return page;
51
+ }
52
+ function paused(resource: string, now: number): { readonly by: GithubPause; readonly until: number } | undefined {
53
+ const blocked = state.blocks.get(resource)?.until;
54
+ if (blocked !== undefined && blocked > now) {
55
+ return { by: "rate-limit", until: blocked };
56
+ }
57
+ const reserved = reservedUntil(state.observations.get(resource), now);
58
+ return reserved === undefined ? undefined : { by: "reserve", until: reserved };
59
+ }
60
+ function limited(observation: RecordedObservation, now: number): GithubObservation {
61
+ const pause = paused(observation.resource, now);
62
+ return { ...observation, limitedBy: pause?.by ?? null, limitedUntil: pause?.until ?? null };
63
+ }
64
+ const settle = Effect.fn("Fleet.settle")(function* (url: string, exitCode: number, response: ParsedResponse, now: number) {
65
+ const observation = readObservation(response.headers, now);
66
+ const resource = observation?.resource ?? requestResource(url);
67
+ if (observation !== undefined) {
68
+ state.observations.set(resource, observation);
69
+ }
70
+ const previous = state.blocks.get(resource);
71
+ const wait = rateLimitWait(response, observation, now, previous?.failures ?? 0);
72
+ if (wait !== undefined) {
73
+ const until = Math.max(wait, previous?.until ?? 0);
74
+ state.blocks.set(resource, { failures: (previous?.failures ?? 0) + 1, until });
75
+ return yield* githubPaused("rate-limit", until);
76
+ }
77
+ state.blocks.delete(resource);
78
+ const cached = state.cache.get(url);
79
+ if (response.status === 304 && cached !== undefined) {
80
+ return remember(url, cached);
81
+ }
82
+ if (exitCode !== 0 || response.status < 200 || response.status > 299) {
83
+ return yield* new ServiceUnavailable({ message: `GitHub request failed with HTTP ${response.status}` });
84
+ }
85
+ return remember(url, { body: response.body, etag: response.headers.get("etag"), next: nextLink(response.headers) });
86
+ });
87
+ const request = Effect.fn("Fleet.request")(function* (url: string) {
88
+ const pause = paused(requestResource(url), yield* Clock.currentTimeMillis);
89
+ if (pause !== undefined) {
90
+ return yield* githubPaused(pause.by, pause.until);
91
+ }
92
+ const etag = state.cache.get(url)?.etag;
93
+ const { exitCode, output } = yield* state.transport([
94
+ "api",
95
+ "--include",
96
+ "--method",
97
+ "GET",
98
+ ...(etag === undefined ? [] : ["--header", `If-None-Match: ${etag}`]),
99
+ url,
100
+ ]);
101
+ const response = parseResponse(output);
102
+ if (response === undefined) {
103
+ return yield* new ServiceUnavailable({ message: "GitHub returned a response without an HTTP status" });
104
+ }
105
+ return yield* settle(url, exitCode, response, yield* Clock.currentTimeMillis);
106
+ });
107
+ return {
108
+ get: (endpoint: string) => request(endpoint).pipe(Effect.map(({ body }) => body)),
109
+ observations: () =>
110
+ Effect.gen(function* () {
111
+ const now = yield* Clock.currentTimeMillis;
112
+ return [...state.observations.values()]
113
+ .map((observation) => limited(observation, now))
114
+ .sort((left, right) => left.resource.localeCompare(right.resource));
115
+ }),
116
+ pace: (intervalMs: number) =>
117
+ Effect.gen(function* () {
118
+ const now = yield* Clock.currentTimeMillis;
119
+ const core = state.observations.get("core");
120
+ return pace(core === undefined ? undefined : limited(core, now), intervalMs, now);
121
+ }),
122
+ pages: (endpoint: string) =>
123
+ Effect.gen(function* () {
124
+ const bodies: string[] = [];
125
+ let next: string | undefined = endpoint;
126
+ while (next !== undefined) {
127
+ if (bodies.length >= PAGE_LIMIT) {
128
+ return yield* new ServiceUnavailable({ message: `GitHub returned more than ${PAGE_LIMIT} pages` });
129
+ }
130
+ const page: Page = yield* request(next);
131
+ bodies.push(page.body);
132
+ next = page.next;
133
+ }
134
+ return `[${bodies.join(",")}]`;
135
+ }),
136
+ };
137
+ },
138
+ requires: [GithubTransport],
139
+ });
140
+ export type GithubApi = typeof GithubApi.Identifier;
141
+
142
+ export const githubApiLayer = GithubApi.layer.pipe(Layer.provide(ghTransport));