@shivaedev/work-fleet 0.2.1 → 0.4.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 (515) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +61 -19
  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 +2345 -2010
  132. package/dist/engine/readRetry.d.ts.map +1 -1
  133. package/dist/engine/readRetry.js +22 -2
  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/cliOptions.d.ts +1 -1
  220. package/dist/host/cliOptions.d.ts.map +1 -1
  221. package/dist/host/cliOptions.js +1 -1
  222. package/dist/host/cliOptions.js.map +1 -1
  223. package/dist/host/commands.d.ts +28 -4
  224. package/dist/host/commands.d.ts.map +1 -1
  225. package/dist/host/commands.js +19 -5
  226. package/dist/host/commands.js.map +1 -1
  227. package/dist/host/config.d.ts +20 -5
  228. package/dist/host/config.d.ts.map +1 -1
  229. package/dist/host/config.js +17 -19
  230. package/dist/host/config.js.map +1 -1
  231. package/dist/host/configFile.d.ts +66 -0
  232. package/dist/host/configFile.d.ts.map +1 -0
  233. package/dist/host/configFile.js +37 -0
  234. package/dist/host/configFile.js.map +1 -0
  235. package/dist/host/configSettings.d.ts +488 -0
  236. package/dist/host/configSettings.d.ts.map +1 -0
  237. package/dist/host/configSettings.js +115 -0
  238. package/dist/host/configSettings.js.map +1 -0
  239. package/dist/host/daemon.d.ts +881 -1185
  240. package/dist/host/daemon.d.ts.map +1 -1
  241. package/dist/host/daemon.js +2 -1
  242. package/dist/host/daemon.js.map +1 -1
  243. package/dist/host/daemonClient.d.ts +301 -605
  244. package/dist/host/daemonClient.d.ts.map +1 -1
  245. package/dist/host/daemonClient.js +28 -7
  246. package/dist/host/daemonClient.js.map +1 -1
  247. package/dist/host/daemonContract.d.ts +1973 -3075
  248. package/dist/host/daemonContract.d.ts.map +1 -1
  249. package/dist/host/daemonContract.js +12 -4
  250. package/dist/host/daemonContract.js.map +1 -1
  251. package/dist/host/daemonDiagnostics.d.ts.map +1 -1
  252. package/dist/host/daemonDiagnostics.js +9 -3
  253. package/dist/host/daemonDiagnostics.js.map +1 -1
  254. package/dist/host/daemonHandlers.d.ts +2 -1
  255. package/dist/host/daemonHandlers.d.ts.map +1 -1
  256. package/dist/host/daemonHandlers.js +20 -7
  257. package/dist/host/daemonHandlers.js.map +1 -1
  258. package/dist/host/daemonSnapshots.d.ts.map +1 -1
  259. package/dist/host/daemonSnapshots.js +21 -1
  260. package/dist/host/daemonSnapshots.js.map +1 -1
  261. package/dist/host/dispatchRequests.d.ts +30 -0
  262. package/dist/host/dispatchRequests.d.ts.map +1 -0
  263. package/dist/host/dispatchRequests.js +40 -0
  264. package/dist/host/dispatchRequests.js.map +1 -0
  265. package/dist/host/follow.js +1 -1
  266. package/dist/host/follow.js.map +1 -1
  267. package/dist/host/followState.d.ts.map +1 -1
  268. package/dist/host/followState.js +5 -4
  269. package/dist/host/followState.js.map +1 -1
  270. package/dist/host/github.d.ts +5 -6
  271. package/dist/host/github.d.ts.map +1 -1
  272. package/dist/host/github.js +3 -4
  273. package/dist/host/github.js.map +1 -1
  274. package/dist/host/githubApi.d.ts +15 -0
  275. package/dist/host/githubApi.d.ts.map +1 -0
  276. package/dist/host/githubApi.js +117 -0
  277. package/dist/host/githubApi.js.map +1 -0
  278. package/dist/host/githubChecks.d.ts +13 -3
  279. package/dist/host/githubChecks.d.ts.map +1 -1
  280. package/dist/host/githubChecks.js +5 -5
  281. package/dist/host/githubChecks.js.map +1 -1
  282. package/dist/host/githubDelivery.d.ts +13 -1
  283. package/dist/host/githubDelivery.d.ts.map +1 -1
  284. package/dist/host/githubDelivery.js +39 -6
  285. package/dist/host/githubDelivery.js.map +1 -1
  286. package/dist/host/githubRateLimit.d.ts +23 -0
  287. package/dist/host/githubRateLimit.d.ts.map +1 -0
  288. package/dist/host/githubRateLimit.js +70 -0
  289. package/dist/host/githubRateLimit.js.map +1 -0
  290. package/dist/host/githubRequiredChecks.d.ts +1 -2
  291. package/dist/host/githubRequiredChecks.d.ts.map +1 -1
  292. package/dist/host/githubRequiredChecks.js +2 -2
  293. package/dist/host/githubRequiredChecks.js.map +1 -1
  294. package/dist/host/githubResponse.d.ts +10 -0
  295. package/dist/host/githubResponse.d.ts.map +1 -0
  296. package/dist/host/githubResponse.js +29 -0
  297. package/dist/host/githubResponse.js.map +1 -0
  298. package/dist/host/githubResult.d.ts +3 -2
  299. package/dist/host/githubResult.d.ts.map +1 -1
  300. package/dist/host/githubResult.js +9 -12
  301. package/dist/host/githubResult.js.map +1 -1
  302. package/dist/host/githubTransport.d.ts +13 -0
  303. package/dist/host/githubTransport.d.ts.map +1 -0
  304. package/dist/host/{githubRead.js → githubTransport.js} +9 -10
  305. package/dist/host/githubTransport.js.map +1 -0
  306. package/dist/host/integrations.d.ts +6 -0
  307. package/dist/host/integrations.d.ts.map +1 -0
  308. package/dist/host/integrations.js +34 -0
  309. package/dist/host/integrations.js.map +1 -0
  310. package/dist/host/liveConfig.d.ts +21 -0
  311. package/dist/host/liveConfig.d.ts.map +1 -0
  312. package/dist/host/liveConfig.js +75 -0
  313. package/dist/host/liveConfig.js.map +1 -0
  314. package/dist/host/providers.d.ts.map +1 -1
  315. package/dist/host/providers.js +2 -2
  316. package/dist/host/providers.js.map +1 -1
  317. package/dist/host/readWaitStates.d.ts.map +1 -1
  318. package/dist/host/readWaitStates.js +8 -18
  319. package/dist/host/readWaitStates.js.map +1 -1
  320. package/dist/host/reassignWork.d.ts +10 -0
  321. package/dist/host/reassignWork.d.ts.map +1 -1
  322. package/dist/host/recordDecisionResponse.d.ts +1 -0
  323. package/dist/host/recordDecisionResponse.d.ts.map +1 -1
  324. package/dist/host/releaseWork.d.ts +24 -3
  325. package/dist/host/releaseWork.d.ts.map +1 -1
  326. package/dist/host/releaseWork.js +11 -7
  327. package/dist/host/releaseWork.js.map +1 -1
  328. package/dist/host/renderStatus.d.ts +2 -2
  329. package/dist/host/renderStatus.d.ts.map +1 -1
  330. package/dist/host/renderStatus.js +8 -2
  331. package/dist/host/renderStatus.js.map +1 -1
  332. package/dist/host/reportBoard.d.ts +1 -1
  333. package/dist/host/reportBoard.d.ts.map +1 -1
  334. package/dist/host/reportBoard.js +6 -3
  335. package/dist/host/reportBoard.js.map +1 -1
  336. package/dist/host/reportPublication.d.ts +2 -1
  337. package/dist/host/reportPublication.d.ts.map +1 -1
  338. package/dist/host/reportPublication.js +5 -2
  339. package/dist/host/reportPublication.js.map +1 -1
  340. package/dist/host/runtime.d.ts +8 -6
  341. package/dist/host/runtime.d.ts.map +1 -1
  342. package/dist/host/runtime.js +38 -46
  343. package/dist/host/runtime.js.map +1 -1
  344. package/dist/host/snapshotStream.d.ts.map +1 -1
  345. package/dist/host/snapshotStream.js +4 -1
  346. package/dist/host/snapshotStream.js.map +1 -1
  347. package/dist/host/status.d.ts +22 -2
  348. package/dist/host/status.d.ts.map +1 -1
  349. package/dist/host/status.js +19 -5
  350. package/dist/host/status.js.map +1 -1
  351. package/dist/host/statusLayer.d.ts +3 -3
  352. package/dist/host/statusLayer.d.ts.map +1 -1
  353. package/dist/host/statusLayer.js +13 -3
  354. package/dist/host/statusLayer.js.map +1 -1
  355. package/dist/host/waitOutcome.d.ts.map +1 -1
  356. package/dist/host/waitOutcome.js +5 -2
  357. package/dist/host/waitOutcome.js.map +1 -1
  358. package/dist/model.d.ts +39 -0
  359. package/dist/model.d.ts.map +1 -1
  360. package/dist/model.js +5 -0
  361. package/dist/model.js.map +1 -1
  362. package/dist/operations.d.ts +270 -0
  363. package/dist/operations.d.ts.map +1 -1
  364. package/dist/packageInfo.d.ts +1 -1
  365. package/dist/packageInfo.js +1 -1
  366. package/dist/policy.d.ts +8 -0
  367. package/dist/policy.d.ts.map +1 -1
  368. package/dist/policy.js +2 -0
  369. package/dist/policy.js.map +1 -1
  370. package/dist/providers/codexCloud.d.ts +2 -0
  371. package/dist/providers/codexCloud.d.ts.map +1 -1
  372. package/dist/providers/codexCloud.js +2 -1
  373. package/dist/providers/codexCloud.js.map +1 -1
  374. package/dist/qualifyDependencies.d.ts +10 -0
  375. package/dist/qualifyDependencies.d.ts.map +1 -1
  376. package/dist/scheduling/admission.d.ts +1635 -0
  377. package/dist/scheduling/admission.d.ts.map +1 -0
  378. package/dist/scheduling/admission.js +11 -0
  379. package/dist/scheduling/admission.js.map +1 -0
  380. package/dist/scheduling/areas.d.ts +30 -0
  381. package/dist/scheduling/areas.d.ts.map +1 -1
  382. package/dist/scheduling/areas.js +1 -0
  383. package/dist/scheduling/areas.js.map +1 -1
  384. package/dist/scheduling/backPressure.d.ts +1637 -0
  385. package/dist/scheduling/backPressure.d.ts.map +1 -0
  386. package/dist/scheduling/backPressure.js +44 -0
  387. package/dist/scheduling/backPressure.js.map +1 -0
  388. package/dist/storage/FleetStoreOptions.d.ts +2 -2
  389. package/dist/storage/FleetStoreOptions.d.ts.map +1 -1
  390. package/dist/storage/addShapeColumns.d.ts +5 -0
  391. package/dist/storage/addShapeColumns.d.ts.map +1 -0
  392. package/dist/storage/addShapeColumns.js +24 -0
  393. package/dist/storage/addShapeColumns.js.map +1 -0
  394. package/dist/storage/dispatchStore.d.ts +47 -0
  395. package/dist/storage/dispatchStore.d.ts.map +1 -0
  396. package/dist/storage/dispatchStore.js +89 -0
  397. package/dist/storage/dispatchStore.js.map +1 -0
  398. package/dist/storage/errors.d.ts +2 -1
  399. package/dist/storage/errors.d.ts.map +1 -1
  400. package/dist/storage/errors.js +2 -1
  401. package/dist/storage/errors.js.map +1 -1
  402. package/dist/storage/fleetMigrations.d.ts +1 -1
  403. package/dist/storage/fleetMigrations.d.ts.map +1 -1
  404. package/dist/storage/fleetMigrations.js +24 -17
  405. package/dist/storage/fleetMigrations.js.map +1 -1
  406. package/dist/storage/listedRecords.d.ts +9 -0
  407. package/dist/storage/listedRecords.d.ts.map +1 -0
  408. package/dist/storage/listedRecords.js +40 -0
  409. package/dist/storage/listedRecords.js.map +1 -0
  410. package/dist/storage/makePostgresFleetStore.d.ts +2 -6
  411. package/dist/storage/makePostgresFleetStore.d.ts.map +1 -1
  412. package/dist/storage/makePostgresFleetStore.js +4 -10
  413. package/dist/storage/makePostgresFleetStore.js.map +1 -1
  414. package/dist/storage/model.d.ts +9 -0
  415. package/dist/storage/model.d.ts.map +1 -1
  416. package/dist/storage/model.js.map +1 -1
  417. package/dist/storage/shapeV14.d.ts +808 -0
  418. package/dist/storage/shapeV14.d.ts.map +1 -0
  419. package/dist/storage/shapeV14.js +18 -0
  420. package/dist/storage/shapeV14.js.map +1 -0
  421. package/package.json +6 -6
  422. package/src/DispatchRequest.ts +13 -0
  423. package/src/attention.ts +5 -0
  424. package/src/board/fleetField.ts +5 -0
  425. package/src/board/markdown.ts +7 -2
  426. package/src/cli.ts +24 -10
  427. package/src/engine/admissionOrder.ts +25 -0
  428. package/src/engine/dispatchCommand.ts +29 -5
  429. package/src/engine/failureContext.ts +1 -0
  430. package/src/engine/failureMessage.ts +12 -1
  431. package/src/engine/localOnlyCriteria.ts +6 -1
  432. package/src/engine/makeLaunch.ts +4 -17
  433. package/src/engine/makeState.ts +30 -26
  434. package/src/engine/mergeReadiness.ts +6 -5
  435. package/src/engine/obtainReview.ts +2 -1
  436. package/src/engine/prepareCommand.ts +1 -1
  437. package/src/engine/readOrContinueReview.ts +2 -1
  438. package/src/engine/readRetry.ts +26 -2
  439. package/src/engine/reconcileAttempt.ts +2 -1
  440. package/src/engine/recordResponseCommand.ts +38 -0
  441. package/src/engine/recoverStall.ts +1 -1
  442. package/src/engine/repository.ts +17 -16
  443. package/src/engine/saveLaunch.ts +45 -0
  444. package/src/engine/scheduler.ts +4 -6
  445. package/src/engine/views.ts +5 -0
  446. package/src/fleet.ts +4 -0
  447. package/src/host/actionCommands.ts +22 -7
  448. package/src/host/admission.ts +3 -3
  449. package/src/host/answerWork.ts +28 -27
  450. package/src/host/cliOptions.ts +1 -1
  451. package/src/host/commands.ts +29 -8
  452. package/src/host/config.ts +26 -22
  453. package/src/host/configFile.ts +40 -0
  454. package/src/host/configSettings.ts +126 -0
  455. package/src/host/daemon.ts +2 -1
  456. package/src/host/daemonClient.ts +42 -7
  457. package/src/host/daemonContract.ts +12 -4
  458. package/src/host/daemonDiagnostics.ts +11 -3
  459. package/src/host/daemonHandlers.ts +24 -7
  460. package/src/host/daemonSnapshots.ts +24 -1
  461. package/src/host/dispatchRequests.ts +53 -0
  462. package/src/host/follow.ts +1 -1
  463. package/src/host/followState.ts +6 -6
  464. package/src/host/github.ts +8 -6
  465. package/src/host/githubApi.ts +142 -0
  466. package/src/host/githubChecks.ts +6 -5
  467. package/src/host/githubDelivery.ts +47 -10
  468. package/src/host/githubRateLimit.ts +91 -0
  469. package/src/host/githubRequiredChecks.ts +2 -2
  470. package/src/host/githubResponse.ts +37 -0
  471. package/src/host/githubResult.ts +15 -17
  472. package/src/host/githubTransport.ts +31 -0
  473. package/src/host/integrations.ts +40 -0
  474. package/src/host/liveConfig.ts +135 -0
  475. package/src/host/providers.ts +4 -2
  476. package/src/host/readWaitStates.ts +8 -20
  477. package/src/host/releaseWork.ts +12 -9
  478. package/src/host/renderStatus.ts +10 -3
  479. package/src/host/reportBoard.ts +6 -3
  480. package/src/host/reportPublication.ts +6 -4
  481. package/src/host/runtime.ts +49 -56
  482. package/src/host/snapshotStream.ts +6 -1
  483. package/src/host/status.ts +24 -7
  484. package/src/host/statusLayer.ts +21 -5
  485. package/src/host/waitOutcome.ts +5 -2
  486. package/src/model.ts +6 -0
  487. package/src/packageInfo.ts +1 -1
  488. package/src/policy.ts +8 -0
  489. package/src/providers/codexCloud.ts +2 -1
  490. package/src/scheduling/admission.ts +19 -0
  491. package/src/scheduling/areas.ts +1 -0
  492. package/src/scheduling/backPressure.ts +55 -0
  493. package/src/storage/FleetStoreOptions.ts +7 -2
  494. package/src/storage/addShapeColumns.ts +24 -0
  495. package/src/storage/dispatchStore.ts +104 -0
  496. package/src/storage/errors.ts +2 -1
  497. package/src/storage/fleetMigrations.ts +24 -17
  498. package/src/storage/listedRecords.ts +53 -0
  499. package/src/storage/makePostgresFleetStore.ts +4 -10
  500. package/src/storage/model.ts +7 -0
  501. package/src/storage/shapeV14.ts +19 -0
  502. package/dist/host/githubRead.d.ts +0 -5
  503. package/dist/host/githubRead.d.ts.map +0 -1
  504. package/dist/host/githubRead.js.map +0 -1
  505. package/dist/host/recordPayloads.d.ts +0 -4
  506. package/dist/host/recordPayloads.d.ts.map +0 -1
  507. package/dist/host/recordPayloads.js +0 -21
  508. package/dist/host/recordPayloads.js.map +0 -1
  509. package/dist/storage/migrateFleet.d.ts +0 -3
  510. package/dist/storage/migrateFleet.d.ts.map +0 -1
  511. package/dist/storage/migrateFleet.js +0 -11
  512. package/dist/storage/migrateFleet.js.map +0 -1
  513. package/src/host/githubRead.ts +0 -20
  514. package/src/host/recordPayloads.ts +0 -24
  515. package/src/storage/migrateFleet.ts +0 -11
@@ -0,0 +1,126 @@
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
+ "readRetryMs",
16
+ "stallMinutes",
17
+ "worker.reviewerGuidance",
18
+ "worker.workerGuidance",
19
+ ] as const;
20
+
21
+ export const ConfigStatus = Schema.Struct({
22
+ file: Schema.optional(Schema.String),
23
+ loadedAt: Schema.Number,
24
+ rejected: Schema.optional(Schema.Struct({ field: Schema.optional(Schema.String), message: Schema.String })),
25
+ restartRequired: Schema.Array(Schema.String),
26
+ settings: HostConfig,
27
+ });
28
+ export type ConfigStatus = typeof ConfigStatus.Type;
29
+
30
+ export const DaemonStatus = Schema.Struct({ ...FleetViews.fields, config: Schema.optional(ConfigStatus) });
31
+
32
+ function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
33
+ return typeof value === "object" && value !== null && !Array.isArray(value);
34
+ }
35
+
36
+ export function setting(config: HostConfig, name: string): unknown {
37
+ return name.split(".").reduce<unknown>((value, key) => (isRecord(value) ? value[key] : undefined), config);
38
+ }
39
+
40
+ function appliedSettings(host: FleetHost): HostConfig {
41
+ return {
42
+ areaMergeWaitMs: host.areaMergeWaitMs,
43
+ backlog: host.backlog,
44
+ ...(host.cloud === undefined ? {} : { cloud: host.cloud }),
45
+ concurrency: host.concurrency,
46
+ database: { maxConnections: host.maxConnections },
47
+ intervalSeconds: host.interval,
48
+ launch: host.launch,
49
+ ...(host.local === undefined ? {} : { local: host.local }),
50
+ namespace: host.namespace,
51
+ ...(host.openPullRequests === undefined ? {} : { openPullRequests: host.openPullRequests }),
52
+ provider: host.provider,
53
+ quota: host.quota,
54
+ readRetryMs: host.readRetryMs,
55
+ repository: host.repository,
56
+ ...(host.result === undefined ? {} : { result: host.result }),
57
+ stallMinutes: host.stallMinutes,
58
+ ...(host.worker === undefined ? {} : { worker: host.worker }),
59
+ };
60
+ }
61
+
62
+ function write(target: Record<string, unknown>, [key, ...rest]: readonly string[], value: unknown) {
63
+ if (key === undefined) {
64
+ return;
65
+ }
66
+ if (rest.length > 0) {
67
+ const child = target[key];
68
+ if (isRecord(child)) {
69
+ const copy = { ...child };
70
+ write(copy, rest, value);
71
+ target[key] = copy;
72
+ }
73
+ return;
74
+ }
75
+ if (value === undefined) {
76
+ delete target[key];
77
+ } else {
78
+ target[key] = value;
79
+ }
80
+ }
81
+
82
+ // A reloadable setting nested under a section that the running config lacks stays unapplied until a restart adds the section.
83
+ export function withReloadable(applied: HostConfig, next: HostConfig): Readonly<Record<string, unknown>> {
84
+ const merged: Record<string, unknown> = { ...applied };
85
+ for (const name of RELOADABLE_SETTINGS) {
86
+ write(merged, name.split("."), setting(next, name));
87
+ }
88
+ return merged;
89
+ }
90
+
91
+ export function changedSettings(left: unknown, right: unknown, prefix = ""): readonly string[] {
92
+ if (isRecord(left) && isRecord(right)) {
93
+ return [...new Set([...Object.keys(left), ...Object.keys(right)])]
94
+ .sort((first, second) => first.localeCompare(second))
95
+ .flatMap((key) => changedSettings(left[key], right[key], prefix === "" ? key : `${prefix}.${key}`));
96
+ }
97
+ return Equal.equals(left, right) ? [] : [prefix];
98
+ }
99
+
100
+ export function configStatus({ host, loadedAt, rejected, restartRequired }: AppliedConfig): ConfigStatus {
101
+ return {
102
+ ...(host.configFile === undefined ? {} : { file: host.configFile }),
103
+ loadedAt,
104
+ ...(rejected === undefined
105
+ ? {}
106
+ : { rejected: { ...(rejected.field === undefined ? {} : { field: rejected.field }), message: rejected.message } }),
107
+ restartRequired,
108
+ settings: appliedSettings(host),
109
+ };
110
+ }
111
+
112
+ export function renderConfig(config: ConfigStatus): string {
113
+ const applying = RELOADABLE_SETTINGS.flatMap((name) => {
114
+ const value = setting(config.settings, name);
115
+ return value === undefined ? [] : [`${name} ${typeof value === "object" ? JSON.stringify(value) : String(value)}`];
116
+ });
117
+ const rejected = config.rejected;
118
+ return [
119
+ `Config loaded ${new Date(config.loadedAt).toISOString()}${config.file === undefined ? "" : ` from ${config.file}`}`,
120
+ ` provider ${config.settings.provider}, ${applying.join(", ")}`,
121
+ ...(config.restartRequired.length === 0 ? [] : [` Restart to apply: ${config.restartRequired.join(", ")}`]),
122
+ ...(rejected === undefined
123
+ ? []
124
+ : [` Rejected edit${rejected.field === undefined ? "" : ` of ${rejected.field}`}, still applying the config above: ${rejected.message}`]),
125
+ ].join("\n");
126
+ }
@@ -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));