@shipfox/api-runners 4.0.0 → 6.0.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 (414) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/CHANGELOG.md +83 -0
  3. package/dist/config.d.ts +5 -1
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +53 -6
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/capacity-assignments.d.ts +7 -0
  8. package/dist/core/capacity-assignments.d.ts.map +1 -0
  9. package/dist/core/capacity-assignments.js +6 -0
  10. package/dist/core/capacity-assignments.js.map +1 -0
  11. package/dist/core/demand.d.ts +6 -6
  12. package/dist/core/demand.d.ts.map +1 -1
  13. package/dist/core/demand.js +10 -10
  14. package/dist/core/demand.js.map +1 -1
  15. package/dist/core/entities/capacity-assignment.d.ts +9 -0
  16. package/dist/core/entities/capacity-assignment.d.ts.map +1 -0
  17. package/dist/core/entities/capacity-assignment.js +3 -0
  18. package/dist/core/entities/capacity-assignment.js.map +1 -0
  19. package/dist/core/entities/ephemeral-registration-token.d.ts +1 -1
  20. package/dist/core/entities/ephemeral-registration-token.d.ts.map +1 -1
  21. package/dist/core/entities/ephemeral-registration-token.js.map +1 -1
  22. package/dist/core/entities/provisioner-capability-snapshot.d.ts +13 -0
  23. package/dist/core/entities/provisioner-capability-snapshot.d.ts.map +1 -0
  24. package/dist/core/entities/provisioner-capability-snapshot.js +3 -0
  25. package/dist/core/entities/provisioner-capability-snapshot.js.map +1 -0
  26. package/dist/core/entities/provisioner-token.d.ts +10 -2
  27. package/dist/core/entities/provisioner-token.d.ts.map +1 -1
  28. package/dist/core/entities/provisioner-token.js.map +1 -1
  29. package/dist/core/entities/{provisioned-runner.d.ts → runner-instance.d.ts} +6 -6
  30. package/dist/core/entities/runner-instance.d.ts.map +1 -0
  31. package/dist/core/entities/runner-instance.js +3 -0
  32. package/dist/core/entities/runner-instance.js.map +1 -0
  33. package/dist/core/entities/runner-session.d.ts +4 -2
  34. package/dist/core/entities/runner-session.d.ts.map +1 -1
  35. package/dist/core/entities/runner-session.js.map +1 -1
  36. package/dist/core/ephemeral-registration-tokens.d.ts +5 -5
  37. package/dist/core/ephemeral-registration-tokens.d.ts.map +1 -1
  38. package/dist/core/ephemeral-registration-tokens.js +12 -12
  39. package/dist/core/ephemeral-registration-tokens.js.map +1 -1
  40. package/dist/core/errors.d.ts +16 -4
  41. package/dist/core/errors.d.ts.map +1 -1
  42. package/dist/core/errors.js +22 -4
  43. package/dist/core/errors.js.map +1 -1
  44. package/dist/core/index.d.ts +10 -4
  45. package/dist/core/index.d.ts.map +1 -1
  46. package/dist/core/index.js +6 -2
  47. package/dist/core/index.js.map +1 -1
  48. package/dist/core/job-executions.d.ts +2 -0
  49. package/dist/core/job-executions.d.ts.map +1 -1
  50. package/dist/core/job-executions.js +1 -2
  51. package/dist/core/job-executions.js.map +1 -1
  52. package/dist/core/maintenance.d.ts +1 -1
  53. package/dist/core/maintenance.d.ts.map +1 -1
  54. package/dist/core/maintenance.js +5 -5
  55. package/dist/core/maintenance.js.map +1 -1
  56. package/dist/core/provisioner-capability-snapshots.d.ts +5 -0
  57. package/dist/core/provisioner-capability-snapshots.d.ts.map +1 -0
  58. package/dist/core/provisioner-capability-snapshots.js +10 -0
  59. package/dist/core/provisioner-capability-snapshots.js.map +1 -0
  60. package/dist/core/provisioner-tokens.d.ts +10 -0
  61. package/dist/core/provisioner-tokens.d.ts.map +1 -1
  62. package/dist/core/provisioner-tokens.js +23 -1
  63. package/dist/core/provisioner-tokens.js.map +1 -1
  64. package/dist/core/rate-limit.d.ts.map +1 -1
  65. package/dist/core/rate-limit.js +2 -7
  66. package/dist/core/rate-limit.js.map +1 -1
  67. package/dist/core/runner-activation.d.ts +13 -0
  68. package/dist/core/runner-activation.d.ts.map +1 -0
  69. package/dist/core/runner-activation.js +36 -0
  70. package/dist/core/runner-activation.js.map +1 -0
  71. package/dist/core/runner-control-sessions.d.ts +46 -0
  72. package/dist/core/runner-control-sessions.d.ts.map +1 -0
  73. package/dist/core/runner-control-sessions.js +121 -0
  74. package/dist/core/runner-control-sessions.js.map +1 -0
  75. package/dist/core/runner-instances.d.ts +79 -0
  76. package/dist/core/runner-instances.d.ts.map +1 -0
  77. package/dist/core/runner-instances.js +168 -0
  78. package/dist/core/runner-instances.js.map +1 -0
  79. package/dist/core/runner-sessions.d.ts +8 -2
  80. package/dist/core/runner-sessions.d.ts.map +1 -1
  81. package/dist/core/runner-sessions.js +8 -5
  82. package/dist/core/runner-sessions.js.map +1 -1
  83. package/dist/db/capacity-assignments.d.ts +7 -0
  84. package/dist/db/capacity-assignments.d.ts.map +1 -0
  85. package/dist/db/capacity-assignments.js +72 -0
  86. package/dist/db/capacity-assignments.js.map +1 -0
  87. package/dist/db/db.d.ts +2837 -1009
  88. package/dist/db/db.d.ts.map +1 -1
  89. package/dist/db/db.js +11 -2
  90. package/dist/db/db.js.map +1 -1
  91. package/dist/db/ephemeral-registration-tokens.d.ts +2 -2
  92. package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -1
  93. package/dist/db/ephemeral-registration-tokens.js +21 -21
  94. package/dist/db/ephemeral-registration-tokens.js.map +1 -1
  95. package/dist/db/index.d.ts +7 -4
  96. package/dist/db/index.d.ts.map +1 -1
  97. package/dist/db/index.js +5 -2
  98. package/dist/db/index.js.map +1 -1
  99. package/dist/db/job-executions.d.ts +6 -6
  100. package/dist/db/job-executions.d.ts.map +1 -1
  101. package/dist/db/job-executions.js +20 -19
  102. package/dist/db/job-executions.js.map +1 -1
  103. package/dist/db/provisioner-capability-snapshots.d.ts +21 -0
  104. package/dist/db/provisioner-capability-snapshots.d.ts.map +1 -0
  105. package/dist/db/provisioner-capability-snapshots.js +57 -0
  106. package/dist/db/provisioner-capability-snapshots.js.map +1 -0
  107. package/dist/db/provisioner-tokens.d.ts +7 -2
  108. package/dist/db/provisioner-tokens.d.ts.map +1 -1
  109. package/dist/db/provisioner-tokens.js +59 -10
  110. package/dist/db/provisioner-tokens.js.map +1 -1
  111. package/dist/db/reservations.d.ts +18 -0
  112. package/dist/db/reservations.d.ts.map +1 -1
  113. package/dist/db/reservations.js +88 -1
  114. package/dist/db/reservations.js.map +1 -1
  115. package/dist/db/runner-instances.d.ts +91 -0
  116. package/dist/db/runner-instances.d.ts.map +1 -0
  117. package/dist/db/runner-instances.js +511 -0
  118. package/dist/db/runner-instances.js.map +1 -0
  119. package/dist/db/runner-sessions.d.ts +5 -0
  120. package/dist/db/runner-sessions.d.ts.map +1 -1
  121. package/dist/db/runner-sessions.js +48 -2
  122. package/dist/db/runner-sessions.js.map +1 -1
  123. package/dist/db/schema/capacity-assignments.d.ts +113 -0
  124. package/dist/db/schema/capacity-assignments.d.ts.map +1 -0
  125. package/dist/db/schema/capacity-assignments.js +29 -0
  126. package/dist/db/schema/capacity-assignments.js.map +1 -0
  127. package/dist/db/schema/ephemeral-registration-tokens.d.ts +2 -2
  128. package/dist/db/schema/ephemeral-registration-tokens.js +3 -3
  129. package/dist/db/schema/ephemeral-registration-tokens.js.map +1 -1
  130. package/dist/db/schema/provisioner-capability-snapshots.d.ts +207 -0
  131. package/dist/db/schema/provisioner-capability-snapshots.d.ts.map +1 -0
  132. package/dist/db/schema/provisioner-capability-snapshots.js +38 -0
  133. package/dist/db/schema/provisioner-capability-snapshots.js.map +1 -0
  134. package/dist/db/schema/provisioner-tokens.d.ts +19 -1
  135. package/dist/db/schema/provisioner-tokens.d.ts.map +1 -1
  136. package/dist/db/schema/provisioner-tokens.js +29 -3
  137. package/dist/db/schema/provisioner-tokens.js.map +1 -1
  138. package/dist/db/schema/runner-activation-tokens.d.ts +161 -0
  139. package/dist/db/schema/runner-activation-tokens.d.ts.map +1 -0
  140. package/dist/db/schema/runner-activation-tokens.js +27 -0
  141. package/dist/db/schema/runner-activation-tokens.js.map +1 -0
  142. package/dist/db/schema/runner-control-sessions.d.ts +338 -0
  143. package/dist/db/schema/runner-control-sessions.d.ts.map +1 -0
  144. package/dist/db/schema/runner-control-sessions.js +52 -0
  145. package/dist/db/schema/runner-control-sessions.js.map +1 -0
  146. package/dist/db/schema/{provisioned-runners.d.ts → runner-instances.d.ts} +88 -34
  147. package/dist/db/schema/runner-instances.d.ts.map +1 -0
  148. package/dist/db/schema/{provisioned-runners.js → runner-instances.js} +19 -14
  149. package/dist/db/schema/runner-instances.js.map +1 -0
  150. package/dist/db/schema/runner-sessions.d.ts +39 -5
  151. package/dist/db/schema/runner-sessions.d.ts.map +1 -1
  152. package/dist/db/schema/runner-sessions.js +13 -6
  153. package/dist/db/schema/runner-sessions.js.map +1 -1
  154. package/dist/db/schema/running-job-executions.d.ts +2 -2
  155. package/dist/db/schema/running-job-executions.js +4 -4
  156. package/dist/db/schema/running-job-executions.js.map +1 -1
  157. package/dist/index.d.ts +7 -2
  158. package/dist/index.d.ts.map +1 -1
  159. package/dist/index.js +45 -38
  160. package/dist/index.js.map +1 -1
  161. package/dist/installation-provisioning.d.ts +14 -0
  162. package/dist/installation-provisioning.d.ts.map +1 -0
  163. package/dist/installation-provisioning.js +7 -0
  164. package/dist/installation-provisioning.js.map +1 -0
  165. package/dist/metrics/index.d.ts +1 -1
  166. package/dist/metrics/index.d.ts.map +1 -1
  167. package/dist/metrics/index.js +1 -1
  168. package/dist/metrics/index.js.map +1 -1
  169. package/dist/metrics/instance.d.ts +11 -7
  170. package/dist/metrics/instance.d.ts.map +1 -1
  171. package/dist/metrics/instance.js +13 -7
  172. package/dist/metrics/instance.js.map +1 -1
  173. package/dist/presentation/auth/index.d.ts +1 -0
  174. package/dist/presentation/auth/index.d.ts.map +1 -1
  175. package/dist/presentation/auth/index.js +1 -0
  176. package/dist/presentation/auth/index.js.map +1 -1
  177. package/dist/presentation/auth/provisioner-token-auth.d.ts.map +1 -1
  178. package/dist/presentation/auth/provisioner-token-auth.js +5 -1
  179. package/dist/presentation/auth/provisioner-token-auth.js.map +1 -1
  180. package/dist/presentation/auth/runner-control-session-auth.d.ts +4 -0
  181. package/dist/presentation/auth/runner-control-session-auth.d.ts.map +1 -0
  182. package/dist/presentation/auth/runner-control-session-auth.js +29 -0
  183. package/dist/presentation/auth/runner-control-session-auth.js.map +1 -0
  184. package/dist/presentation/auth/runner-registration-token-auth.d.ts +5 -1
  185. package/dist/presentation/auth/runner-registration-token-auth.d.ts.map +1 -1
  186. package/dist/presentation/auth/runner-registration-token-auth.js +21 -1
  187. package/dist/presentation/auth/runner-registration-token-auth.js.map +1 -1
  188. package/dist/presentation/dto/index.d.ts +2 -2
  189. package/dist/presentation/dto/index.d.ts.map +1 -1
  190. package/dist/presentation/dto/index.js +2 -2
  191. package/dist/presentation/dto/index.js.map +1 -1
  192. package/dist/presentation/dto/mint-registration-tokens.d.ts +5 -3
  193. package/dist/presentation/dto/mint-registration-tokens.d.ts.map +1 -1
  194. package/dist/presentation/dto/mint-registration-tokens.js +4 -4
  195. package/dist/presentation/dto/mint-registration-tokens.js.map +1 -1
  196. package/dist/presentation/dto/poll-demand.d.ts.map +1 -1
  197. package/dist/presentation/dto/poll-demand.js +7 -1
  198. package/dist/presentation/dto/poll-demand.js.map +1 -1
  199. package/dist/presentation/dto/provisioner-token.d.ts +1 -0
  200. package/dist/presentation/dto/provisioner-token.d.ts.map +1 -1
  201. package/dist/presentation/dto/provisioner-token.js +5 -1
  202. package/dist/presentation/dto/provisioner-token.js.map +1 -1
  203. package/dist/presentation/dto/{provisioned-runners.d.ts → runner-instances.d.ts} +9 -9
  204. package/dist/presentation/dto/runner-instances.d.ts.map +1 -0
  205. package/dist/presentation/dto/{provisioned-runners.js → runner-instances.js} +6 -6
  206. package/dist/presentation/dto/runner-instances.js.map +1 -0
  207. package/dist/presentation/index.d.ts +2 -2
  208. package/dist/presentation/index.d.ts.map +1 -1
  209. package/dist/presentation/index.js +2 -2
  210. package/dist/presentation/index.js.map +1 -1
  211. package/dist/presentation/inter-module.d.ts +5 -0
  212. package/dist/presentation/inter-module.d.ts.map +1 -0
  213. package/dist/presentation/inter-module.js +43 -0
  214. package/dist/presentation/inter-module.js.map +1 -0
  215. package/dist/presentation/routes/assign-capacity.d.ts +2 -0
  216. package/dist/presentation/routes/assign-capacity.d.ts.map +1 -0
  217. package/dist/presentation/routes/assign-capacity.js +44 -0
  218. package/dist/presentation/routes/assign-capacity.js.map +1 -0
  219. package/dist/presentation/routes/get-provisioner-me.d.ts.map +1 -1
  220. package/dist/presentation/routes/get-provisioner-me.js +5 -1
  221. package/dist/presentation/routes/get-provisioner-me.js.map +1 -1
  222. package/dist/presentation/routes/heartbeat.d.ts +2 -1
  223. package/dist/presentation/routes/heartbeat.d.ts.map +1 -1
  224. package/dist/presentation/routes/heartbeat.js +60 -50
  225. package/dist/presentation/routes/heartbeat.js.map +1 -1
  226. package/dist/presentation/routes/index.d.ts +3 -1
  227. package/dist/presentation/routes/index.d.ts.map +1 -1
  228. package/dist/presentation/routes/index.js +83 -50
  229. package/dist/presentation/routes/index.js.map +1 -1
  230. package/dist/presentation/routes/mint-registration-tokens.d.ts.map +1 -1
  231. package/dist/presentation/routes/mint-registration-tokens.js +5 -5
  232. package/dist/presentation/routes/mint-registration-tokens.js.map +1 -1
  233. package/dist/presentation/routes/planned-capacity.d.ts +3 -0
  234. package/dist/presentation/routes/planned-capacity.d.ts.map +1 -0
  235. package/dist/presentation/routes/planned-capacity.js +54 -0
  236. package/dist/presentation/routes/planned-capacity.js.map +1 -0
  237. package/dist/presentation/routes/poll-demand.d.ts +2 -0
  238. package/dist/presentation/routes/poll-demand.d.ts.map +1 -1
  239. package/dist/presentation/routes/poll-demand.js +86 -45
  240. package/dist/presentation/routes/poll-demand.js.map +1 -1
  241. package/dist/presentation/routes/reconcile-runner-instances.d.ts +2 -0
  242. package/dist/presentation/routes/reconcile-runner-instances.d.ts.map +1 -0
  243. package/dist/presentation/routes/reconcile-runner-instances.js +27 -0
  244. package/dist/presentation/routes/reconcile-runner-instances.js.map +1 -0
  245. package/dist/presentation/routes/register.d.ts +2 -1
  246. package/dist/presentation/routes/register.d.ts.map +1 -1
  247. package/dist/presentation/routes/register.js +55 -52
  248. package/dist/presentation/routes/register.js.map +1 -1
  249. package/dist/presentation/routes/report-runner-instances.d.ts +2 -0
  250. package/dist/presentation/routes/report-runner-instances.d.ts.map +1 -0
  251. package/dist/presentation/routes/report-runner-instances.js +37 -0
  252. package/dist/presentation/routes/report-runner-instances.js.map +1 -0
  253. package/dist/presentation/routes/request-job.d.ts +2 -1
  254. package/dist/presentation/routes/request-job.d.ts.map +1 -1
  255. package/dist/presentation/routes/request-job.js +39 -36
  256. package/dist/presentation/routes/request-job.js.map +1 -1
  257. package/dist/presentation/routes/runner-enrollment.d.ts +7 -0
  258. package/dist/presentation/routes/runner-enrollment.d.ts.map +1 -0
  259. package/dist/presentation/routes/runner-enrollment.js +185 -0
  260. package/dist/presentation/routes/runner-enrollment.js.map +1 -0
  261. package/dist/temporal/activities/index.d.ts +2 -2
  262. package/dist/temporal/activities/index.d.ts.map +1 -1
  263. package/dist/temporal/activities/index.js +2 -2
  264. package/dist/temporal/activities/index.js.map +1 -1
  265. package/dist/temporal/activities/maintenance-activities.d.ts +1 -1
  266. package/dist/temporal/activities/maintenance-activities.d.ts.map +1 -1
  267. package/dist/temporal/activities/maintenance-activities.js +3 -3
  268. package/dist/temporal/activities/maintenance-activities.js.map +1 -1
  269. package/dist/temporal/workflows/index.bundle.js +3 -3
  270. package/dist/temporal/workflows/stuck-job-detector.js +2 -2
  271. package/dist/temporal/workflows/stuck-job-detector.js.map +1 -1
  272. package/dist/tsconfig.test.tsbuildinfo +1 -1
  273. package/drizzle/0000_initial.sql +102 -24
  274. package/drizzle/meta/0000_snapshot.json +483 -63
  275. package/package.json +31 -36
  276. package/src/config.test.ts +0 -26
  277. package/src/config.ts +47 -6
  278. package/src/core/capacity-assignments.ts +10 -0
  279. package/src/core/demand.test.ts +29 -29
  280. package/src/core/demand.ts +22 -22
  281. package/src/core/entities/capacity-assignment.ts +8 -0
  282. package/src/core/entities/ephemeral-registration-token.ts +1 -1
  283. package/src/core/entities/provisioner-capability-snapshot.ts +12 -0
  284. package/src/core/entities/provisioner-token.ts +8 -2
  285. package/src/core/entities/{provisioned-runner.ts → runner-instance.ts} +5 -5
  286. package/src/core/entities/runner-session.ts +4 -2
  287. package/src/core/ephemeral-registration-tokens.test.ts +11 -11
  288. package/src/core/ephemeral-registration-tokens.ts +18 -20
  289. package/src/core/errors.ts +23 -4
  290. package/src/core/index.ts +39 -17
  291. package/src/core/job-executions.ts +3 -2
  292. package/src/core/maintenance.test.ts +13 -17
  293. package/src/core/maintenance.ts +5 -5
  294. package/src/core/provisioner-capability-snapshots.test.ts +35 -0
  295. package/src/core/provisioner-capability-snapshots.ts +12 -0
  296. package/src/core/provisioner-tokens.test.ts +20 -0
  297. package/src/core/provisioner-tokens.ts +33 -0
  298. package/src/core/rate-limit.test.ts +11 -27
  299. package/src/core/rate-limit.ts +3 -11
  300. package/src/core/runner-activation.ts +72 -0
  301. package/src/core/runner-control-sessions.ts +190 -0
  302. package/src/core/{provisioned-runners.test.ts → runner-instances.test.ts} +24 -24
  303. package/src/core/runner-instances.ts +310 -0
  304. package/src/core/runner-sessions.test.ts +112 -22
  305. package/src/core/runner-sessions.ts +25 -14
  306. package/src/db/capacity-assignments.test.ts +159 -0
  307. package/src/db/capacity-assignments.ts +116 -0
  308. package/src/db/db.ts +11 -2
  309. package/src/db/ephemeral-registration-tokens.test.ts +9 -9
  310. package/src/db/ephemeral-registration-tokens.ts +26 -26
  311. package/src/db/index.ts +31 -21
  312. package/src/db/job-executions.test.ts +13 -11
  313. package/src/db/job-executions.ts +33 -27
  314. package/src/db/provisioner-capability-snapshots.test.ts +175 -0
  315. package/src/db/provisioner-capability-snapshots.ts +130 -0
  316. package/src/db/provisioner-tokens.test.ts +139 -2
  317. package/src/db/provisioner-tokens.ts +123 -15
  318. package/src/db/reservations.test.ts +49 -0
  319. package/src/db/reservations.ts +146 -1
  320. package/src/db/{provisioned-runners.test.ts → runner-instances.test.ts} +429 -353
  321. package/src/db/runner-instances.ts +942 -0
  322. package/src/db/runner-sessions.test.ts +19 -6
  323. package/src/db/runner-sessions.ts +78 -2
  324. package/src/db/schema/capacity-assignments.ts +34 -0
  325. package/src/db/schema/ephemeral-registration-tokens.ts +4 -4
  326. package/src/db/schema/provisioner-capability-snapshots.ts +46 -0
  327. package/src/db/schema/provisioner-tokens.ts +33 -2
  328. package/src/db/schema/runner-activation-tokens.ts +22 -0
  329. package/src/db/schema/runner-control-sessions.ts +46 -0
  330. package/src/db/schema/{provisioned-runners.ts → runner-instances.ts} +26 -27
  331. package/src/db/schema/runner-sessions.ts +11 -7
  332. package/src/db/schema/running-job-executions.ts +5 -5
  333. package/src/index.test.ts +26 -0
  334. package/src/index.ts +39 -19
  335. package/src/installation-provisioning.ts +14 -0
  336. package/src/metrics/index.ts +5 -5
  337. package/src/metrics/instance.ts +25 -14
  338. package/src/presentation/auth/index.ts +4 -0
  339. package/src/presentation/auth/provisioner-token-auth.ts +8 -4
  340. package/src/presentation/auth/runner-control-session-auth.ts +31 -0
  341. package/src/presentation/auth/runner-registration-token-auth.ts +39 -3
  342. package/src/presentation/dto/index.ts +5 -5
  343. package/src/presentation/dto/mint-registration-tokens.ts +8 -15
  344. package/src/presentation/dto/poll-demand.ts +3 -1
  345. package/src/presentation/dto/provisioner-token.ts +5 -0
  346. package/src/presentation/dto/{provisioned-runners.ts → runner-instances.ts} +13 -15
  347. package/src/presentation/index.ts +2 -1
  348. package/src/presentation/inter-module.test.ts +15 -0
  349. package/src/presentation/inter-module.ts +53 -0
  350. package/src/presentation/routes/assign-capacity.ts +48 -0
  351. package/src/presentation/routes/get-provisioner-me.ts +3 -4
  352. package/src/presentation/routes/heartbeat.test.ts +4 -3
  353. package/src/presentation/routes/heartbeat.ts +67 -64
  354. package/src/presentation/routes/index.ts +93 -43
  355. package/src/presentation/routes/list-active-runners.test.ts +23 -23
  356. package/src/presentation/routes/manual-registration-tokens.test.ts +4 -3
  357. package/src/presentation/routes/mint-registration-tokens.test.ts +12 -12
  358. package/src/presentation/routes/mint-registration-tokens.ts +5 -7
  359. package/src/presentation/routes/planned-capacity.ts +49 -0
  360. package/src/presentation/routes/poll-demand-release.test.ts +4 -4
  361. package/src/presentation/routes/poll-demand.test.ts +161 -22
  362. package/src/presentation/routes/poll-demand.ts +105 -45
  363. package/src/presentation/routes/provisioner-me.test.ts +37 -3
  364. package/src/presentation/routes/{reconcile-provisioned-runners.test.ts → reconcile-runner-instances.test.ts} +66 -61
  365. package/src/presentation/routes/reconcile-runner-instances.ts +30 -0
  366. package/src/presentation/routes/register.test.ts +9 -5
  367. package/src/presentation/routes/register.ts +61 -57
  368. package/src/presentation/routes/{report-provisioned-runners.test.ts → report-runner-instances.test.ts} +28 -28
  369. package/src/presentation/routes/{report-provisioned-runners.ts → report-runner-instances.ts} +14 -14
  370. package/src/presentation/routes/request-job.test.ts +5 -3
  371. package/src/presentation/routes/request-job.ts +41 -37
  372. package/src/presentation/routes/runner-enrollment.test.ts +259 -0
  373. package/src/presentation/routes/runner-enrollment.ts +174 -0
  374. package/src/temporal/activities/index.ts +2 -2
  375. package/src/temporal/activities/maintenance-activities.test.ts +7 -7
  376. package/src/temporal/activities/maintenance-activities.ts +3 -3
  377. package/src/temporal/workflows/stuck-job-detector.test.ts +3 -3
  378. package/src/temporal/workflows/stuck-job-detector.ts +2 -2
  379. package/test/env.ts +1 -3
  380. package/test/factories/ephemeral-registration-token.ts +2 -2
  381. package/test/factories/provisioner-token.ts +3 -1
  382. package/test/factories/runner-instance.ts +57 -0
  383. package/test/factories/runner-session.ts +3 -1
  384. package/test/fixtures/auth-inter-module.ts +30 -0
  385. package/test/globalSetup.ts +5 -1
  386. package/test/index.ts +2 -1
  387. package/tsconfig.build.tsbuildinfo +1 -1
  388. package/dist/core/entities/provisioned-runner.d.ts.map +0 -1
  389. package/dist/core/entities/provisioned-runner.js +0 -3
  390. package/dist/core/entities/provisioned-runner.js.map +0 -1
  391. package/dist/core/provisioned-runners.d.ts +0 -67
  392. package/dist/core/provisioned-runners.d.ts.map +0 -1
  393. package/dist/core/provisioned-runners.js +0 -162
  394. package/dist/core/provisioned-runners.js.map +0 -1
  395. package/dist/db/provisioned-runners.d.ts +0 -79
  396. package/dist/db/provisioned-runners.d.ts.map +0 -1
  397. package/dist/db/provisioned-runners.js +0 -466
  398. package/dist/db/provisioned-runners.js.map +0 -1
  399. package/dist/db/schema/provisioned-runners.d.ts.map +0 -1
  400. package/dist/db/schema/provisioned-runners.js.map +0 -1
  401. package/dist/presentation/dto/provisioned-runners.d.ts.map +0 -1
  402. package/dist/presentation/dto/provisioned-runners.js.map +0 -1
  403. package/dist/presentation/routes/reconcile-provisioned-runners.d.ts +0 -2
  404. package/dist/presentation/routes/reconcile-provisioned-runners.d.ts.map +0 -1
  405. package/dist/presentation/routes/reconcile-provisioned-runners.js +0 -27
  406. package/dist/presentation/routes/reconcile-provisioned-runners.js.map +0 -1
  407. package/dist/presentation/routes/report-provisioned-runners.d.ts +0 -2
  408. package/dist/presentation/routes/report-provisioned-runners.d.ts.map +0 -1
  409. package/dist/presentation/routes/report-provisioned-runners.js +0 -37
  410. package/dist/presentation/routes/report-provisioned-runners.js.map +0 -1
  411. package/src/core/provisioned-runners.ts +0 -301
  412. package/src/db/provisioned-runners.ts +0 -886
  413. package/src/presentation/routes/reconcile-provisioned-runners.ts +0 -30
  414. package/test/factories/provisioned-runner.ts +0 -78
@@ -2,6 +2,8 @@ import { canonicalizeLabels } from '@shipfox/runner-labels';
2
2
  import { and, asc, eq, gt, inArray, lt, sql } from 'drizzle-orm';
3
3
  import { db } from './db.js';
4
4
  import { pendingJobExecutions } from './schema/pending-job-executions.js';
5
+ import { provisionerCapabilitySnapshots } from './schema/provisioner-capability-snapshots.js';
6
+ import { provisionerTokens } from './schema/provisioner-tokens.js';
5
7
  import { reservations } from './schema/reservations.js';
6
8
  export async function pollDemandAndReserve(params) {
7
9
  return await db().transaction(async (tx)=>{
@@ -10,7 +12,58 @@ export async function pollDemandAndReserve(params) {
10
12
  }
11
13
  export async function pollDemandAndReserveTx(tx, params) {
12
14
  await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${params.workspaceId}))`);
13
- const demandRows = (await tx.select({
15
+ return await pollDemandAndReserveLockedTx(tx, params);
16
+ }
17
+ export async function pollInstallationDemandAndReserve(params) {
18
+ const candidateWorkspaceIds = await listInstallationDemandWorkspaceIds(params.eligibleWorkspaceIds);
19
+ const results = [];
20
+ let remainingMaxReservations = params.maxReservations;
21
+ const remainingTemplates = params.templates.map((template)=>({
22
+ ...template,
23
+ labels: [
24
+ ...canonicalizeLabels(template.labels)
25
+ ],
26
+ remainingSlots: template.availableSlots
27
+ }));
28
+ for (const workspaceId of candidateWorkspaceIds){
29
+ if (params.signal?.aborted || remainingMaxReservations === 0) break;
30
+ const result = await db().transaction(async (tx)=>{
31
+ const lockResult = await tx.execute(sql`select pg_try_advisory_xact_lock(hashtext(${workspaceId})) as locked`);
32
+ const locked = lockResult.rows[0];
33
+ if (!locked?.locked) return {
34
+ stats: [],
35
+ reservations: []
36
+ };
37
+ return await pollDemandAndReserveLockedTx(tx, {
38
+ workspaceId,
39
+ provisionerId: params.provisionerId,
40
+ maxReservations: remainingMaxReservations,
41
+ ttlSeconds: params.ttlSeconds,
42
+ templates: remainingTemplates.map((template)=>({
43
+ ...template,
44
+ availableSlots: template.remainingSlots
45
+ })),
46
+ capabilityWindowSeconds: params.capabilityWindowSeconds
47
+ });
48
+ });
49
+ results.push(result);
50
+ consumeInstallationTemplateSlots(remainingTemplates, result.reservations);
51
+ params.onReservations?.(result.reservations);
52
+ remainingMaxReservations -= result.reservations.reduce((total, reservation)=>total + reservation.count, 0);
53
+ }
54
+ return {
55
+ stats: results.flatMap((result)=>result.stats),
56
+ reservations: results.flatMap((result)=>result.reservations)
57
+ };
58
+ }
59
+ function consumeInstallationTemplateSlots(templates, reservations) {
60
+ for (const reservation of reservations){
61
+ const satisfyingTemplates = templates.filter((template)=>isSubset(reservation.labels, template.labels)).sort((a, b)=>a.labels.length - b.labels.length || a.templateKey.localeCompare(b.templateKey));
62
+ drawSlots(satisfyingTemplates, reservation.count);
63
+ }
64
+ }
65
+ async function pollDemandAndReserveLockedTx(tx, params) {
66
+ let demandRows = (await tx.select({
14
67
  requiredLabels: pendingJobExecutions.requiredLabels,
15
68
  queued: sql`count(*)::int`,
16
69
  oldestQueuedAt: sql`min(${pendingJobExecutions.createdAt})`
@@ -18,6 +71,13 @@ export async function pollDemandAndReserveTx(tx, params) {
18
71
  ...row,
19
72
  oldestQueuedAt: new Date(row.oldestQueuedAt)
20
73
  }));
74
+ if (params.capabilityWindowSeconds !== undefined) {
75
+ const capabilityLabels = await listActiveWorkspaceCapabilityLabelsTx(tx, {
76
+ workspaceId: params.workspaceId,
77
+ windowSeconds: params.capabilityWindowSeconds
78
+ });
79
+ demandRows = demandRows.filter((demand)=>!capabilityLabels.some((labels)=>isSubset(demand.requiredLabels, labels)));
80
+ }
21
81
  const activeReservationRows = await tx.select({
22
82
  requiredLabels: reservations.requiredLabels,
23
83
  reserved: sql`coalesce(sum(${reservations.count}), 0)::int`
@@ -41,6 +101,9 @@ export async function pollDemandAndReserveTx(tx, params) {
41
101
  const stats = [];
42
102
  const grants = [];
43
103
  let remainingMaxReservations = params.maxReservations;
104
+ const workspaceIdField = params.capabilityWindowSeconds === undefined ? {} : {
105
+ workspaceId: params.workspaceId
106
+ };
44
107
  for (const demand of sortDemandRows(demandRows)){
45
108
  const satisfyingTemplates = templates.filter((template)=>isSubset(demand.requiredLabels, template.labels)).sort((a, b)=>a.labels.length - b.labels.length || a.templateKey.localeCompare(b.templateKey));
46
109
  if (satisfyingTemplates.length === 0) continue;
@@ -66,12 +129,14 @@ export async function pollDemandAndReserveTx(tx, params) {
66
129
  reservedAfterGrant += grant;
67
130
  grants.push({
68
131
  reservationId: inserted.id,
132
+ ...workspaceIdField,
69
133
  labels: demand.requiredLabels,
70
134
  count: grant,
71
135
  expiresAt: inserted.expiresAt
72
136
  });
73
137
  }
74
138
  stats.push({
139
+ ...workspaceIdField,
75
140
  labels: demand.requiredLabels,
76
141
  queued: demand.queued,
77
142
  reserved: reservedAfterGrant,
@@ -83,6 +148,28 @@ export async function pollDemandAndReserveTx(tx, params) {
83
148
  reservations: grants
84
149
  };
85
150
  }
151
+ async function listInstallationDemandWorkspaceIds(eligibleWorkspaceIds) {
152
+ if (eligibleWorkspaceIds.size === 0) return [];
153
+ const rows = await db().select({
154
+ workspaceId: pendingJobExecutions.workspaceId,
155
+ oldestQueuedAt: sql`min(${pendingJobExecutions.createdAt})`
156
+ }).from(pendingJobExecutions).where(inArray(pendingJobExecutions.workspaceId, [
157
+ ...eligibleWorkspaceIds
158
+ ])).groupBy(pendingJobExecutions.workspaceId).orderBy(asc(sql`min(${pendingJobExecutions.createdAt})`), asc(pendingJobExecutions.workspaceId));
159
+ return rows.map((row)=>row.workspaceId);
160
+ }
161
+ export async function listQueuedDemandWorkspaceIds() {
162
+ const rows = await db().select({
163
+ workspaceId: pendingJobExecutions.workspaceId
164
+ }).from(pendingJobExecutions).groupBy(pendingJobExecutions.workspaceId);
165
+ return rows.map((row)=>row.workspaceId);
166
+ }
167
+ async function listActiveWorkspaceCapabilityLabelsTx(tx, params) {
168
+ const rows = await tx.select({
169
+ labels: provisionerCapabilitySnapshots.labels
170
+ }).from(provisionerCapabilitySnapshots).innerJoin(provisionerTokens, eq(provisionerTokens.id, provisionerCapabilitySnapshots.provisionerId)).where(and(eq(provisionerCapabilitySnapshots.workspaceId, params.workspaceId), eq(provisionerTokens.workspaceId, params.workspaceId), eq(provisionerTokens.scope, 'workspace'), sql`${provisionerCapabilitySnapshots.advertisedAt} > now() - (${params.windowSeconds} || ' seconds')::interval`, sql`${provisionerTokens.revokedAt} is null`, sql`(${provisionerTokens.expiresAt} is null or ${provisionerTokens.expiresAt} > now())`));
171
+ return rows.map((row)=>row.labels);
172
+ }
86
173
  export async function deleteExpiredReservations(params) {
87
174
  const expiredIds = db().select({
88
175
  id: reservations.id
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/db/reservations.ts"],"sourcesContent":["import {canonicalizeLabels} from '@shipfox/runner-labels';\nimport {and, asc, eq, gt, inArray, lt, sql} from 'drizzle-orm';\nimport type {Tx} from './db.js';\nimport {db} from './db.js';\nimport {pendingJobExecutions} from './schema/pending-job-executions.js';\nimport {reservations} from './schema/reservations.js';\n\nexport interface ReservationTemplate {\n templateKey: string;\n labels: string[];\n availableSlots: number;\n starting: number;\n running: number;\n}\n\nexport interface DemandStat {\n labels: string[];\n queued: number;\n reserved: number;\n oldestQueuedAt: Date;\n}\n\nexport interface ReservationGrant {\n reservationId: string;\n labels: string[];\n count: number;\n expiresAt: Date;\n}\n\nexport interface PollDemandAndReserveParams {\n workspaceId: string;\n provisionerId: string;\n maxReservations: number;\n ttlSeconds: number;\n templates: ReservationTemplate[];\n}\n\ninterface NormalizedTemplate {\n templateKey: string;\n labels: string[];\n remainingSlots: number;\n}\n\ninterface DemandRow {\n requiredLabels: string[];\n queued: number;\n oldestQueuedAt: Date;\n}\n\nexport async function pollDemandAndReserve(\n params: PollDemandAndReserveParams,\n): Promise<{stats: DemandStat[]; reservations: ReservationGrant[]}> {\n return await db().transaction(async (tx) => {\n return await pollDemandAndReserveTx(tx, params);\n });\n}\n\nexport async function pollDemandAndReserveTx(\n tx: Tx,\n params: PollDemandAndReserveParams,\n): Promise<{stats: DemandStat[]; reservations: ReservationGrant[]}> {\n await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${params.workspaceId}))`);\n\n const demandRows = (\n await tx\n .select({\n requiredLabels: pendingJobExecutions.requiredLabels,\n queued: sql<number>`count(*)::int`,\n oldestQueuedAt: sql<Date | string>`min(${pendingJobExecutions.createdAt})`,\n })\n .from(pendingJobExecutions)\n .where(eq(pendingJobExecutions.workspaceId, params.workspaceId))\n .groupBy(pendingJobExecutions.requiredLabels)\n ).map((row) => ({\n ...row,\n oldestQueuedAt: new Date(row.oldestQueuedAt),\n }));\n\n const activeReservationRows = await tx\n .select({\n requiredLabels: reservations.requiredLabels,\n reserved: sql<number>`coalesce(sum(${reservations.count}), 0)::int`,\n })\n .from(reservations)\n .where(\n and(eq(reservations.workspaceId, params.workspaceId), gt(reservations.expiresAt, sql`now()`)),\n )\n .groupBy(reservations.requiredLabels);\n\n const reservedByLabels = new Map(\n activeReservationRows.map((row) => [labelKey(row.requiredLabels), row.reserved]),\n );\n const activeProvisionerReservationRows = await tx\n .select({\n requiredLabels: reservations.requiredLabels,\n reserved: sql<number>`coalesce(sum(${reservations.count}), 0)::int`,\n })\n .from(reservations)\n .where(\n and(\n eq(reservations.workspaceId, params.workspaceId),\n eq(reservations.provisionerId, params.provisionerId),\n gt(reservations.expiresAt, sql`now()`),\n ),\n )\n .groupBy(reservations.requiredLabels);\n\n const templates = params.templates.map((template) => ({\n templateKey: template.templateKey,\n labels: [...canonicalizeLabels(template.labels)],\n remainingSlots: template.availableSlots,\n }));\n deductProvisionerReservations(templates, activeProvisionerReservationRows);\n const stats: DemandStat[] = [];\n const grants: ReservationGrant[] = [];\n let remainingMaxReservations = params.maxReservations;\n\n for (const demand of sortDemandRows(demandRows)) {\n const satisfyingTemplates = templates\n .filter((template) => isSubset(demand.requiredLabels, template.labels))\n .sort(\n (a, b) => a.labels.length - b.labels.length || a.templateKey.localeCompare(b.templateKey),\n );\n\n if (satisfyingTemplates.length === 0) continue;\n\n const reserved = reservedByLabels.get(labelKey(demand.requiredLabels)) ?? 0;\n const unreserved = Math.max(0, demand.queued - reserved);\n const capacity = satisfyingTemplates.reduce(\n (total, template) => total + template.remainingSlots,\n 0,\n );\n const grant = Math.min(unreserved, capacity, remainingMaxReservations);\n let reservedAfterGrant = reserved;\n\n if (grant > 0 && params.maxReservations > 0) {\n const [inserted] = await tx\n .insert(reservations)\n .values({\n workspaceId: params.workspaceId,\n provisionerId: params.provisionerId,\n requiredLabels: demand.requiredLabels,\n count: grant,\n expiresAt: sql`now() + (${params.ttlSeconds} || ' seconds')::interval`,\n })\n .returning({id: reservations.id, expiresAt: reservations.expiresAt});\n\n if (!inserted) throw new Error('Insert returned no rows');\n\n remainingMaxReservations -= grant;\n drawSlots(satisfyingTemplates, grant);\n reservedAfterGrant += grant;\n grants.push({\n reservationId: inserted.id,\n labels: demand.requiredLabels,\n count: grant,\n expiresAt: inserted.expiresAt,\n });\n }\n\n stats.push({\n labels: demand.requiredLabels,\n queued: demand.queued,\n reserved: reservedAfterGrant,\n oldestQueuedAt: demand.oldestQueuedAt,\n });\n }\n\n return {stats, reservations: grants};\n}\n\nexport async function deleteExpiredReservations(params?: {limit?: number}): Promise<number> {\n const expiredIds = db()\n .select({id: reservations.id})\n .from(reservations)\n .where(lt(reservations.expiresAt, sql`now()`))\n .orderBy(asc(reservations.expiresAt))\n .limit(params?.limit ?? 1000);\n\n const deleted = await db()\n .delete(reservations)\n .where(inArray(reservations.id, expiredIds))\n .returning({id: reservations.id});\n\n return deleted.length;\n}\n\nexport async function deleteReservationsByIds(ids: string[]): Promise<number> {\n if (ids.length === 0) return 0;\n\n const deleted = await db()\n .delete(reservations)\n .where(inArray(reservations.id, ids))\n .returning({id: reservations.id});\n\n return deleted.length;\n}\n\nexport async function releaseReservationUnits(\n tx: Tx,\n params: {\n workspaceId: string;\n provisionerId: string;\n releases: Array<{reservationId: string; count: number}>;\n },\n): Promise<number> {\n let released = 0;\n\n for (const release of params.releases) {\n if (release.count <= 0) continue;\n\n const decremented = await tx\n .update(reservations)\n .set({count: sql`${reservations.count} - ${release.count}`})\n .where(\n and(\n eq(reservations.id, release.reservationId),\n eq(reservations.workspaceId, params.workspaceId),\n eq(reservations.provisionerId, params.provisionerId),\n gt(reservations.count, release.count),\n gt(reservations.expiresAt, sql`now()`),\n ),\n )\n .returning({id: reservations.id});\n\n if (decremented.length > 0) {\n released += release.count;\n continue;\n }\n\n const deleted = await tx\n .delete(reservations)\n .where(\n and(\n eq(reservations.id, release.reservationId),\n eq(reservations.workspaceId, params.workspaceId),\n eq(reservations.provisionerId, params.provisionerId),\n sql`${reservations.count} <= ${release.count}`,\n gt(reservations.expiresAt, sql`now()`),\n ),\n )\n .returning({count: reservations.count});\n\n released += deleted.reduce((total, row) => total + row.count, 0);\n }\n\n return released;\n}\n\nfunction deductProvisionerReservations(\n templates: NormalizedTemplate[],\n activeReservations: {requiredLabels: string[]; reserved: number}[],\n): void {\n for (const reservation of sortReservationRows(activeReservations)) {\n const satisfyingTemplates = templates\n .filter((template) => isSubset(reservation.requiredLabels, template.labels))\n .sort(\n (a, b) => a.labels.length - b.labels.length || a.templateKey.localeCompare(b.templateKey),\n );\n drawSlots(satisfyingTemplates, reservation.reserved);\n }\n}\n\nfunction sortDemandRows(rows: DemandRow[]): DemandRow[] {\n return [...rows].sort((a, b) => {\n const specificity = b.requiredLabels.length - a.requiredLabels.length;\n if (specificity !== 0) return specificity;\n return a.oldestQueuedAt.getTime() - b.oldestQueuedAt.getTime();\n });\n}\n\nfunction sortReservationRows<T extends {requiredLabels: string[]}>(rows: T[]): T[] {\n return [...rows].sort((a, b) => {\n const specificity = b.requiredLabels.length - a.requiredLabels.length;\n if (specificity !== 0) return specificity;\n return labelKey(a.requiredLabels).localeCompare(labelKey(b.requiredLabels));\n });\n}\n\nfunction isSubset(requiredLabels: string[], availableLabels: string[]): boolean {\n return requiredLabels.every((label) => availableLabels.includes(label));\n}\n\nfunction drawSlots(templates: NormalizedTemplate[], count: number): void {\n let remaining = count;\n for (const template of templates) {\n if (remaining === 0) return;\n const used = Math.min(template.remainingSlots, remaining);\n template.remainingSlots -= used;\n remaining -= used;\n }\n}\n\nfunction labelKey(labels: string[]): string {\n return JSON.stringify(labels);\n}\n"],"names":["canonicalizeLabels","and","asc","eq","gt","inArray","lt","sql","db","pendingJobExecutions","reservations","pollDemandAndReserve","params","transaction","tx","pollDemandAndReserveTx","execute","workspaceId","demandRows","select","requiredLabels","queued","oldestQueuedAt","createdAt","from","where","groupBy","map","row","Date","activeReservationRows","reserved","count","expiresAt","reservedByLabels","Map","labelKey","activeProvisionerReservationRows","provisionerId","templates","template","templateKey","labels","remainingSlots","availableSlots","deductProvisionerReservations","stats","grants","remainingMaxReservations","maxReservations","demand","sortDemandRows","satisfyingTemplates","filter","isSubset","sort","a","b","length","localeCompare","get","unreserved","Math","max","capacity","reduce","total","grant","min","reservedAfterGrant","inserted","insert","values","ttlSeconds","returning","id","Error","drawSlots","push","reservationId","deleteExpiredReservations","expiredIds","orderBy","limit","deleted","delete","deleteReservationsByIds","ids","releaseReservationUnits","released","release","releases","decremented","update","set","activeReservations","reservation","sortReservationRows","rows","specificity","getTime","availableLabels","every","label","includes","remaining","used","JSON","stringify"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/db/reservations.ts"],"sourcesContent":["import {canonicalizeLabels} from '@shipfox/runner-labels';\nimport {and, asc, eq, gt, inArray, lt, sql} from 'drizzle-orm';\nimport type {Tx} from './db.js';\nimport {db} from './db.js';\nimport {pendingJobExecutions} from './schema/pending-job-executions.js';\nimport {provisionerCapabilitySnapshots} from './schema/provisioner-capability-snapshots.js';\nimport {provisionerTokens} from './schema/provisioner-tokens.js';\nimport {reservations} from './schema/reservations.js';\n\nexport interface ReservationTemplate {\n templateKey: string;\n labels: string[];\n availableSlots: number;\n starting: number;\n running: number;\n}\n\nexport interface DemandStat {\n workspaceId?: string;\n labels: string[];\n queued: number;\n reserved: number;\n oldestQueuedAt: Date;\n}\n\nexport interface ReservationGrant {\n reservationId: string;\n workspaceId?: string;\n labels: string[];\n count: number;\n expiresAt: Date;\n}\n\nexport interface PollDemandAndReserveParams {\n workspaceId: string;\n provisionerId: string;\n maxReservations: number;\n ttlSeconds: number;\n templates: ReservationTemplate[];\n capabilityWindowSeconds?: number;\n}\n\nexport interface InstallationPollDemandAndReserveParams {\n provisionerId: string;\n maxReservations: number;\n ttlSeconds: number;\n templates: ReservationTemplate[];\n capabilityWindowSeconds: number;\n eligibleWorkspaceIds: ReadonlySet<string>;\n signal?: AbortSignal;\n onReservations?: (reservations: ReservationGrant[]) => void;\n}\n\ninterface NormalizedTemplate {\n templateKey: string;\n labels: string[];\n remainingSlots: number;\n}\n\ninterface DemandRow {\n requiredLabels: string[];\n queued: number;\n oldestQueuedAt: Date;\n}\n\nexport async function pollDemandAndReserve(\n params: PollDemandAndReserveParams,\n): Promise<{stats: DemandStat[]; reservations: ReservationGrant[]}> {\n return await db().transaction(async (tx) => {\n return await pollDemandAndReserveTx(tx, params);\n });\n}\n\nexport async function pollDemandAndReserveTx(\n tx: Tx,\n params: PollDemandAndReserveParams,\n): Promise<{stats: DemandStat[]; reservations: ReservationGrant[]}> {\n await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${params.workspaceId}))`);\n return await pollDemandAndReserveLockedTx(tx, params);\n}\n\nexport async function pollInstallationDemandAndReserve(\n params: InstallationPollDemandAndReserveParams,\n): Promise<{stats: DemandStat[]; reservations: ReservationGrant[]}> {\n const candidateWorkspaceIds = await listInstallationDemandWorkspaceIds(\n params.eligibleWorkspaceIds,\n );\n const results: Array<{stats: DemandStat[]; reservations: ReservationGrant[]}> = [];\n let remainingMaxReservations = params.maxReservations;\n const remainingTemplates = params.templates.map((template) => ({\n ...template,\n labels: [...canonicalizeLabels(template.labels)],\n remainingSlots: template.availableSlots,\n }));\n for (const workspaceId of candidateWorkspaceIds) {\n if (params.signal?.aborted || remainingMaxReservations === 0) break;\n const result = await db().transaction(async (tx) => {\n const lockResult = await tx.execute<{locked: boolean}>(\n sql`select pg_try_advisory_xact_lock(hashtext(${workspaceId})) as locked`,\n );\n const locked = lockResult.rows[0];\n if (!locked?.locked) return {stats: [], reservations: []};\n return await pollDemandAndReserveLockedTx(tx, {\n workspaceId,\n provisionerId: params.provisionerId,\n maxReservations: remainingMaxReservations,\n ttlSeconds: params.ttlSeconds,\n templates: remainingTemplates.map((template) => ({\n ...template,\n availableSlots: template.remainingSlots,\n })),\n capabilityWindowSeconds: params.capabilityWindowSeconds,\n });\n });\n results.push(result);\n consumeInstallationTemplateSlots(remainingTemplates, result.reservations);\n params.onReservations?.(result.reservations);\n remainingMaxReservations -= result.reservations.reduce(\n (total, reservation) => total + reservation.count,\n 0,\n );\n }\n return {\n stats: results.flatMap((result) => result.stats),\n reservations: results.flatMap((result) => result.reservations),\n };\n}\n\nfunction consumeInstallationTemplateSlots(\n templates: NormalizedTemplate[],\n reservations: ReservationGrant[],\n): void {\n for (const reservation of reservations) {\n const satisfyingTemplates = templates\n .filter((template) => isSubset(reservation.labels, template.labels))\n .sort(\n (a, b) => a.labels.length - b.labels.length || a.templateKey.localeCompare(b.templateKey),\n );\n drawSlots(satisfyingTemplates, reservation.count);\n }\n}\n\nasync function pollDemandAndReserveLockedTx(\n tx: Tx,\n params: PollDemandAndReserveParams,\n): Promise<{stats: DemandStat[]; reservations: ReservationGrant[]}> {\n let demandRows = (\n await tx\n .select({\n requiredLabels: pendingJobExecutions.requiredLabels,\n queued: sql<number>`count(*)::int`,\n oldestQueuedAt: sql<Date | string>`min(${pendingJobExecutions.createdAt})`,\n })\n .from(pendingJobExecutions)\n .where(eq(pendingJobExecutions.workspaceId, params.workspaceId))\n .groupBy(pendingJobExecutions.requiredLabels)\n ).map((row) => ({\n ...row,\n oldestQueuedAt: new Date(row.oldestQueuedAt),\n }));\n if (params.capabilityWindowSeconds !== undefined) {\n const capabilityLabels = await listActiveWorkspaceCapabilityLabelsTx(tx, {\n workspaceId: params.workspaceId,\n windowSeconds: params.capabilityWindowSeconds,\n });\n demandRows = demandRows.filter(\n (demand) => !capabilityLabels.some((labels) => isSubset(demand.requiredLabels, labels)),\n );\n }\n\n const activeReservationRows = await tx\n .select({\n requiredLabels: reservations.requiredLabels,\n reserved: sql<number>`coalesce(sum(${reservations.count}), 0)::int`,\n })\n .from(reservations)\n .where(\n and(eq(reservations.workspaceId, params.workspaceId), gt(reservations.expiresAt, sql`now()`)),\n )\n .groupBy(reservations.requiredLabels);\n\n const reservedByLabels = new Map(\n activeReservationRows.map((row) => [labelKey(row.requiredLabels), row.reserved]),\n );\n const activeProvisionerReservationRows = await tx\n .select({\n requiredLabels: reservations.requiredLabels,\n reserved: sql<number>`coalesce(sum(${reservations.count}), 0)::int`,\n })\n .from(reservations)\n .where(\n and(\n eq(reservations.workspaceId, params.workspaceId),\n eq(reservations.provisionerId, params.provisionerId),\n gt(reservations.expiresAt, sql`now()`),\n ),\n )\n .groupBy(reservations.requiredLabels);\n\n const templates = params.templates.map((template) => ({\n templateKey: template.templateKey,\n labels: [...canonicalizeLabels(template.labels)],\n remainingSlots: template.availableSlots,\n }));\n deductProvisionerReservations(templates, activeProvisionerReservationRows);\n const stats: DemandStat[] = [];\n const grants: ReservationGrant[] = [];\n let remainingMaxReservations = params.maxReservations;\n const workspaceIdField =\n params.capabilityWindowSeconds === undefined ? {} : {workspaceId: params.workspaceId};\n\n for (const demand of sortDemandRows(demandRows)) {\n const satisfyingTemplates = templates\n .filter((template) => isSubset(demand.requiredLabels, template.labels))\n .sort(\n (a, b) => a.labels.length - b.labels.length || a.templateKey.localeCompare(b.templateKey),\n );\n\n if (satisfyingTemplates.length === 0) continue;\n\n const reserved = reservedByLabels.get(labelKey(demand.requiredLabels)) ?? 0;\n const unreserved = Math.max(0, demand.queued - reserved);\n const capacity = satisfyingTemplates.reduce(\n (total, template) => total + template.remainingSlots,\n 0,\n );\n const grant = Math.min(unreserved, capacity, remainingMaxReservations);\n let reservedAfterGrant = reserved;\n\n if (grant > 0 && params.maxReservations > 0) {\n const [inserted] = await tx\n .insert(reservations)\n .values({\n workspaceId: params.workspaceId,\n provisionerId: params.provisionerId,\n requiredLabels: demand.requiredLabels,\n count: grant,\n expiresAt: sql`now() + (${params.ttlSeconds} || ' seconds')::interval`,\n })\n .returning({id: reservations.id, expiresAt: reservations.expiresAt});\n\n if (!inserted) throw new Error('Insert returned no rows');\n\n remainingMaxReservations -= grant;\n drawSlots(satisfyingTemplates, grant);\n reservedAfterGrant += grant;\n grants.push({\n reservationId: inserted.id,\n ...workspaceIdField,\n labels: demand.requiredLabels,\n count: grant,\n expiresAt: inserted.expiresAt,\n });\n }\n\n stats.push({\n ...workspaceIdField,\n labels: demand.requiredLabels,\n queued: demand.queued,\n reserved: reservedAfterGrant,\n oldestQueuedAt: demand.oldestQueuedAt,\n });\n }\n\n return {stats, reservations: grants};\n}\n\nasync function listInstallationDemandWorkspaceIds(eligibleWorkspaceIds: ReadonlySet<string>) {\n if (eligibleWorkspaceIds.size === 0) return [];\n const rows = await db()\n .select({\n workspaceId: pendingJobExecutions.workspaceId,\n oldestQueuedAt: sql<Date>`min(${pendingJobExecutions.createdAt})`,\n })\n .from(pendingJobExecutions)\n .where(inArray(pendingJobExecutions.workspaceId, [...eligibleWorkspaceIds]))\n .groupBy(pendingJobExecutions.workspaceId)\n .orderBy(\n asc(sql`min(${pendingJobExecutions.createdAt})`),\n asc(pendingJobExecutions.workspaceId),\n );\n return rows.map((row) => row.workspaceId);\n}\n\nexport async function listQueuedDemandWorkspaceIds(): Promise<string[]> {\n const rows = await db()\n .select({workspaceId: pendingJobExecutions.workspaceId})\n .from(pendingJobExecutions)\n .groupBy(pendingJobExecutions.workspaceId);\n return rows.map((row) => row.workspaceId);\n}\n\nasync function listActiveWorkspaceCapabilityLabelsTx(\n tx: Tx,\n params: {workspaceId: string; windowSeconds: number},\n): Promise<string[][]> {\n const rows = await tx\n .select({labels: provisionerCapabilitySnapshots.labels})\n .from(provisionerCapabilitySnapshots)\n .innerJoin(\n provisionerTokens,\n eq(provisionerTokens.id, provisionerCapabilitySnapshots.provisionerId),\n )\n .where(\n and(\n eq(provisionerCapabilitySnapshots.workspaceId, params.workspaceId),\n eq(provisionerTokens.workspaceId, params.workspaceId),\n eq(provisionerTokens.scope, 'workspace'),\n sql`${provisionerCapabilitySnapshots.advertisedAt} > now() - (${params.windowSeconds} || ' seconds')::interval`,\n sql`${provisionerTokens.revokedAt} is null`,\n sql`(${provisionerTokens.expiresAt} is null or ${provisionerTokens.expiresAt} > now())`,\n ),\n );\n return rows.map((row) => row.labels);\n}\n\nexport async function deleteExpiredReservations(params?: {limit?: number}): Promise<number> {\n const expiredIds = db()\n .select({id: reservations.id})\n .from(reservations)\n .where(lt(reservations.expiresAt, sql`now()`))\n .orderBy(asc(reservations.expiresAt))\n .limit(params?.limit ?? 1000);\n\n const deleted = await db()\n .delete(reservations)\n .where(inArray(reservations.id, expiredIds))\n .returning({id: reservations.id});\n\n return deleted.length;\n}\n\nexport async function deleteReservationsByIds(ids: string[]): Promise<number> {\n if (ids.length === 0) return 0;\n\n const deleted = await db()\n .delete(reservations)\n .where(inArray(reservations.id, ids))\n .returning({id: reservations.id});\n\n return deleted.length;\n}\n\nexport async function releaseReservationUnits(\n tx: Tx,\n params: {\n workspaceId: string;\n provisionerId: string;\n releases: Array<{reservationId: string; count: number}>;\n },\n): Promise<number> {\n let released = 0;\n\n for (const release of params.releases) {\n if (release.count <= 0) continue;\n\n const decremented = await tx\n .update(reservations)\n .set({count: sql`${reservations.count} - ${release.count}`})\n .where(\n and(\n eq(reservations.id, release.reservationId),\n eq(reservations.workspaceId, params.workspaceId),\n eq(reservations.provisionerId, params.provisionerId),\n gt(reservations.count, release.count),\n gt(reservations.expiresAt, sql`now()`),\n ),\n )\n .returning({id: reservations.id});\n\n if (decremented.length > 0) {\n released += release.count;\n continue;\n }\n\n const deleted = await tx\n .delete(reservations)\n .where(\n and(\n eq(reservations.id, release.reservationId),\n eq(reservations.workspaceId, params.workspaceId),\n eq(reservations.provisionerId, params.provisionerId),\n sql`${reservations.count} <= ${release.count}`,\n gt(reservations.expiresAt, sql`now()`),\n ),\n )\n .returning({count: reservations.count});\n\n released += deleted.reduce((total, row) => total + row.count, 0);\n }\n\n return released;\n}\n\nfunction deductProvisionerReservations(\n templates: NormalizedTemplate[],\n activeReservations: {requiredLabels: string[]; reserved: number}[],\n): void {\n for (const reservation of sortReservationRows(activeReservations)) {\n const satisfyingTemplates = templates\n .filter((template) => isSubset(reservation.requiredLabels, template.labels))\n .sort(\n (a, b) => a.labels.length - b.labels.length || a.templateKey.localeCompare(b.templateKey),\n );\n drawSlots(satisfyingTemplates, reservation.reserved);\n }\n}\n\nfunction sortDemandRows(rows: DemandRow[]): DemandRow[] {\n return [...rows].sort((a, b) => {\n const specificity = b.requiredLabels.length - a.requiredLabels.length;\n if (specificity !== 0) return specificity;\n return a.oldestQueuedAt.getTime() - b.oldestQueuedAt.getTime();\n });\n}\n\nfunction sortReservationRows<T extends {requiredLabels: string[]}>(rows: T[]): T[] {\n return [...rows].sort((a, b) => {\n const specificity = b.requiredLabels.length - a.requiredLabels.length;\n if (specificity !== 0) return specificity;\n return labelKey(a.requiredLabels).localeCompare(labelKey(b.requiredLabels));\n });\n}\n\nfunction isSubset(requiredLabels: string[], availableLabels: string[]): boolean {\n return requiredLabels.every((label) => availableLabels.includes(label));\n}\n\nfunction drawSlots(templates: NormalizedTemplate[], count: number): void {\n let remaining = count;\n for (const template of templates) {\n if (remaining === 0) return;\n const used = Math.min(template.remainingSlots, remaining);\n template.remainingSlots -= used;\n remaining -= used;\n }\n}\n\nfunction labelKey(labels: string[]): string {\n return JSON.stringify(labels);\n}\n"],"names":["canonicalizeLabels","and","asc","eq","gt","inArray","lt","sql","db","pendingJobExecutions","provisionerCapabilitySnapshots","provisionerTokens","reservations","pollDemandAndReserve","params","transaction","tx","pollDemandAndReserveTx","execute","workspaceId","pollDemandAndReserveLockedTx","pollInstallationDemandAndReserve","candidateWorkspaceIds","listInstallationDemandWorkspaceIds","eligibleWorkspaceIds","results","remainingMaxReservations","maxReservations","remainingTemplates","templates","map","template","labels","remainingSlots","availableSlots","signal","aborted","result","lockResult","locked","rows","stats","provisionerId","ttlSeconds","capabilityWindowSeconds","push","consumeInstallationTemplateSlots","onReservations","reduce","total","reservation","count","flatMap","satisfyingTemplates","filter","isSubset","sort","a","b","length","templateKey","localeCompare","drawSlots","demandRows","select","requiredLabels","queued","oldestQueuedAt","createdAt","from","where","groupBy","row","Date","undefined","capabilityLabels","listActiveWorkspaceCapabilityLabelsTx","windowSeconds","demand","some","activeReservationRows","reserved","expiresAt","reservedByLabels","Map","labelKey","activeProvisionerReservationRows","deductProvisionerReservations","grants","workspaceIdField","sortDemandRows","get","unreserved","Math","max","capacity","grant","min","reservedAfterGrant","inserted","insert","values","returning","id","Error","reservationId","size","orderBy","listQueuedDemandWorkspaceIds","innerJoin","scope","advertisedAt","revokedAt","deleteExpiredReservations","expiredIds","limit","deleted","delete","deleteReservationsByIds","ids","releaseReservationUnits","released","release","releases","decremented","update","set","activeReservations","sortReservationRows","specificity","getTime","availableLabels","every","label","includes","remaining","used","JSON","stringify"],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ import type { RunnerInstance, RunnerInstanceState } from '#core/entities/runner-instance.js';
2
+ import type { Tx } from './db.js';
3
+ import { type RunnerInstanceBoundJobExecution } from './job-executions.js';
4
+ export declare const terminalStates: readonly ["stopped", "failed", "terminated"];
5
+ export declare const activeStates: readonly ["starting", "running", "stopping"];
6
+ export declare const divergenceCountStates: readonly ["starting", "running"];
7
+ export type RunnerInstanceTerminateIntentReason = 'job-cancelled';
8
+ export interface RunnerInstanceTerminateIntent {
9
+ providerRunnerId: string;
10
+ reason: RunnerInstanceTerminateIntentReason;
11
+ }
12
+ export interface ActiveRunnerInstanceTemplateCount {
13
+ templateKey: string;
14
+ state: (typeof divergenceCountStates)[number];
15
+ count: number;
16
+ }
17
+ export interface RunnerInstanceReportEvent {
18
+ providerRunnerId: string;
19
+ reservationId: string | null;
20
+ templateKey: string | null;
21
+ labels: string[];
22
+ state: RunnerInstanceState;
23
+ reason: string | null;
24
+ runnerSessionId: string | null;
25
+ providerKind: string | null;
26
+ reportedAt: Date;
27
+ }
28
+ export interface ReportRunnerInstancesParams {
29
+ workspaceId: string | null;
30
+ provisionerId: string;
31
+ events: RunnerInstanceReportEvent[];
32
+ }
33
+ export interface ReconcileRunnerInstancesParams {
34
+ workspaceId: string | null;
35
+ provisionerId: string;
36
+ observedRunnerInstanceIds: string[];
37
+ terminateGraceSeconds: number;
38
+ }
39
+ export interface ReconcileRunnerInstancesDbResult {
40
+ observedRows: RunnerInstance[];
41
+ boundJobExecutionsByRunnerInstanceId: Map<string, RunnerInstanceBoundJobExecution>;
42
+ absentIds: string[];
43
+ reservationsReleased: number;
44
+ }
45
+ export interface ReapStaleRunnerInstancesResult {
46
+ reaped: number;
47
+ reservationsReleased: number;
48
+ }
49
+ export declare function createPlannedProvisionedCapacity(params: {
50
+ provisionerId: string;
51
+ providerKind: string | null;
52
+ templateKey: string | null;
53
+ }): Promise<{
54
+ capacityId: string;
55
+ }>;
56
+ export declare function attachProviderRunnerId(params: {
57
+ capacityId: string;
58
+ provisionerId: string;
59
+ providerRunnerId: string;
60
+ }): Promise<boolean>;
61
+ export declare function reportRunnerInstances(params: ReportRunnerInstancesParams): Promise<{
62
+ accepted: number;
63
+ reservationsReleased: number;
64
+ terminateIntentsHonored: RunnerInstanceTerminateIntent[];
65
+ }>;
66
+ export declare function listActiveRunnerInstanceCountsByTemplateTx(tx: Tx, params: {
67
+ workspaceId: string;
68
+ provisionerId: string;
69
+ }): Promise<ActiveRunnerInstanceTemplateCount[]>;
70
+ export declare function listActiveRunnerInstances(params: {
71
+ workspaceId: string;
72
+ windowSeconds: number;
73
+ limit?: number;
74
+ }): Promise<RunnerInstance[]>;
75
+ export declare function listProvisionerTerminateIntents(params: {
76
+ workspaceId: string;
77
+ provisionerId: string;
78
+ limit: number;
79
+ }): Promise<string[]>;
80
+ export declare function listProvisionerTerminateIntentRowsTx(tx: Tx, params: {
81
+ workspaceId: string;
82
+ provisionerId: string;
83
+ limit: number;
84
+ }): Promise<RunnerInstanceTerminateIntent[]>;
85
+ export declare function reconcileRunnerInstances(params: ReconcileRunnerInstancesParams): Promise<ReconcileRunnerInstancesDbResult>;
86
+ export declare function reapStaleRunnerInstances(params: {
87
+ thresholdSeconds: number;
88
+ limit: number;
89
+ }): Promise<ReapStaleRunnerInstancesResult>;
90
+ export declare function isTerminalState(state: RunnerInstanceState): boolean;
91
+ //# sourceMappingURL=runner-instances.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-instances.d.ts","sourceRoot":"","sources":["../../src/db/runner-instances.ts"],"names":[],"mappings":"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"}