@shipfox/api-runners 5.0.0 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (400) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/CHANGELOG.md +80 -0
  3. package/dist/config.d.ts +5 -1
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +54 -7
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/demand.d.ts +6 -6
  8. package/dist/core/demand.d.ts.map +1 -1
  9. package/dist/core/demand.js +10 -10
  10. package/dist/core/demand.js.map +1 -1
  11. package/dist/core/entities/ephemeral-registration-token.d.ts +1 -1
  12. package/dist/core/entities/ephemeral-registration-token.d.ts.map +1 -1
  13. package/dist/core/entities/ephemeral-registration-token.js.map +1 -1
  14. package/dist/core/entities/provisioner-capability-snapshot.d.ts +13 -0
  15. package/dist/core/entities/provisioner-capability-snapshot.d.ts.map +1 -0
  16. package/dist/core/entities/provisioner-capability-snapshot.js +3 -0
  17. package/dist/core/entities/provisioner-capability-snapshot.js.map +1 -0
  18. package/dist/core/entities/provisioner-token.d.ts +10 -2
  19. package/dist/core/entities/provisioner-token.d.ts.map +1 -1
  20. package/dist/core/entities/provisioner-token.js.map +1 -1
  21. package/dist/core/entities/{provisioned-runner.d.ts → runner-instance.d.ts} +7 -6
  22. package/dist/core/entities/runner-instance.d.ts.map +1 -0
  23. package/dist/core/entities/runner-instance.js +3 -0
  24. package/dist/core/entities/runner-instance.js.map +1 -0
  25. package/dist/core/entities/runner-session.d.ts +4 -2
  26. package/dist/core/entities/runner-session.d.ts.map +1 -1
  27. package/dist/core/entities/runner-session.js.map +1 -1
  28. package/dist/core/ephemeral-registration-tokens.d.ts +5 -5
  29. package/dist/core/ephemeral-registration-tokens.d.ts.map +1 -1
  30. package/dist/core/ephemeral-registration-tokens.js +12 -12
  31. package/dist/core/ephemeral-registration-tokens.js.map +1 -1
  32. package/dist/core/errors.d.ts +16 -4
  33. package/dist/core/errors.d.ts.map +1 -1
  34. package/dist/core/errors.js +22 -4
  35. package/dist/core/errors.js.map +1 -1
  36. package/dist/core/index.d.ts +9 -5
  37. package/dist/core/index.d.ts.map +1 -1
  38. package/dist/core/index.js +6 -3
  39. package/dist/core/index.js.map +1 -1
  40. package/dist/core/job-executions.d.ts +2 -0
  41. package/dist/core/job-executions.d.ts.map +1 -1
  42. package/dist/core/job-executions.js +1 -2
  43. package/dist/core/job-executions.js.map +1 -1
  44. package/dist/core/maintenance.d.ts +1 -1
  45. package/dist/core/maintenance.d.ts.map +1 -1
  46. package/dist/core/maintenance.js +5 -5
  47. package/dist/core/maintenance.js.map +1 -1
  48. package/dist/core/provisioner-capability-snapshots.d.ts +5 -0
  49. package/dist/core/provisioner-capability-snapshots.d.ts.map +1 -0
  50. package/dist/core/provisioner-capability-snapshots.js +10 -0
  51. package/dist/core/provisioner-capability-snapshots.js.map +1 -0
  52. package/dist/core/provisioner-tokens.d.ts +10 -0
  53. package/dist/core/provisioner-tokens.d.ts.map +1 -1
  54. package/dist/core/provisioner-tokens.js +23 -1
  55. package/dist/core/provisioner-tokens.js.map +1 -1
  56. package/dist/core/rate-limit.d.ts.map +1 -1
  57. package/dist/core/rate-limit.js +2 -7
  58. package/dist/core/rate-limit.js.map +1 -1
  59. package/dist/core/runner-activation.d.ts +13 -0
  60. package/dist/core/runner-activation.d.ts.map +1 -0
  61. package/dist/core/runner-activation.js +36 -0
  62. package/dist/core/runner-activation.js.map +1 -0
  63. package/dist/core/runner-assignments.d.ts +6 -0
  64. package/dist/core/runner-assignments.d.ts.map +1 -0
  65. package/dist/core/runner-assignments.js +6 -0
  66. package/dist/core/runner-assignments.js.map +1 -0
  67. package/dist/core/runner-control-sessions.d.ts +46 -0
  68. package/dist/core/runner-control-sessions.d.ts.map +1 -0
  69. package/dist/core/runner-control-sessions.js +121 -0
  70. package/dist/core/runner-control-sessions.js.map +1 -0
  71. package/dist/core/runner-instances.d.ts +72 -0
  72. package/dist/core/runner-instances.d.ts.map +1 -0
  73. package/dist/core/runner-instances.js +165 -0
  74. package/dist/core/runner-instances.js.map +1 -0
  75. package/dist/core/runner-sessions.d.ts +8 -2
  76. package/dist/core/runner-sessions.d.ts.map +1 -1
  77. package/dist/core/runner-sessions.js +8 -5
  78. package/dist/core/runner-sessions.js.map +1 -1
  79. package/dist/db/db.d.ts +2698 -1054
  80. package/dist/db/db.d.ts.map +1 -1
  81. package/dist/db/db.js +9 -2
  82. package/dist/db/db.js.map +1 -1
  83. package/dist/db/ephemeral-registration-tokens.d.ts +2 -2
  84. package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -1
  85. package/dist/db/ephemeral-registration-tokens.js +21 -21
  86. package/dist/db/ephemeral-registration-tokens.js.map +1 -1
  87. package/dist/db/index.d.ts +7 -4
  88. package/dist/db/index.d.ts.map +1 -1
  89. package/dist/db/index.js +5 -2
  90. package/dist/db/index.js.map +1 -1
  91. package/dist/db/job-executions.d.ts +6 -6
  92. package/dist/db/job-executions.d.ts.map +1 -1
  93. package/dist/db/job-executions.js +20 -19
  94. package/dist/db/job-executions.js.map +1 -1
  95. package/dist/db/provisioner-capability-snapshots.d.ts +21 -0
  96. package/dist/db/provisioner-capability-snapshots.d.ts.map +1 -0
  97. package/dist/db/provisioner-capability-snapshots.js +57 -0
  98. package/dist/db/provisioner-capability-snapshots.js.map +1 -0
  99. package/dist/db/provisioner-tokens.d.ts +7 -2
  100. package/dist/db/provisioner-tokens.d.ts.map +1 -1
  101. package/dist/db/provisioner-tokens.js +59 -10
  102. package/dist/db/provisioner-tokens.js.map +1 -1
  103. package/dist/db/reservations.d.ts +18 -0
  104. package/dist/db/reservations.d.ts.map +1 -1
  105. package/dist/db/reservations.js +88 -1
  106. package/dist/db/reservations.js.map +1 -1
  107. package/dist/db/runner-assignments.d.ts +7 -0
  108. package/dist/db/runner-assignments.d.ts.map +1 -0
  109. package/dist/db/runner-assignments.js +55 -0
  110. package/dist/db/runner-assignments.js.map +1 -0
  111. package/dist/db/runner-instances.d.ts +84 -0
  112. package/dist/db/runner-instances.d.ts.map +1 -0
  113. package/dist/db/runner-instances.js +497 -0
  114. package/dist/db/runner-instances.js.map +1 -0
  115. package/dist/db/runner-sessions.d.ts +5 -0
  116. package/dist/db/runner-sessions.d.ts.map +1 -1
  117. package/dist/db/runner-sessions.js +48 -2
  118. package/dist/db/runner-sessions.js.map +1 -1
  119. package/dist/db/schema/ephemeral-registration-tokens.d.ts +2 -2
  120. package/dist/db/schema/ephemeral-registration-tokens.js +3 -3
  121. package/dist/db/schema/ephemeral-registration-tokens.js.map +1 -1
  122. package/dist/db/schema/provisioner-capability-snapshots.d.ts +207 -0
  123. package/dist/db/schema/provisioner-capability-snapshots.d.ts.map +1 -0
  124. package/dist/db/schema/provisioner-capability-snapshots.js +38 -0
  125. package/dist/db/schema/provisioner-capability-snapshots.js.map +1 -0
  126. package/dist/db/schema/provisioner-tokens.d.ts +19 -1
  127. package/dist/db/schema/provisioner-tokens.d.ts.map +1 -1
  128. package/dist/db/schema/provisioner-tokens.js +29 -3
  129. package/dist/db/schema/provisioner-tokens.js.map +1 -1
  130. package/dist/db/schema/runner-activation-tokens.d.ts +161 -0
  131. package/dist/db/schema/runner-activation-tokens.d.ts.map +1 -0
  132. package/dist/db/schema/runner-activation-tokens.js +27 -0
  133. package/dist/db/schema/runner-activation-tokens.js.map +1 -0
  134. package/dist/db/schema/runner-control-sessions.d.ts +338 -0
  135. package/dist/db/schema/runner-control-sessions.d.ts.map +1 -0
  136. package/dist/db/schema/runner-control-sessions.js +52 -0
  137. package/dist/db/schema/runner-control-sessions.js.map +1 -0
  138. package/dist/db/schema/{provisioned-runners.d.ts → runner-instances.d.ts} +105 -34
  139. package/dist/db/schema/runner-instances.d.ts.map +1 -0
  140. package/dist/db/schema/{provisioned-runners.js → runner-instances.js} +23 -14
  141. package/dist/db/schema/runner-instances.js.map +1 -0
  142. package/dist/db/schema/runner-sessions.d.ts +39 -5
  143. package/dist/db/schema/runner-sessions.d.ts.map +1 -1
  144. package/dist/db/schema/runner-sessions.js +13 -6
  145. package/dist/db/schema/runner-sessions.js.map +1 -1
  146. package/dist/db/schema/running-job-executions.d.ts +2 -2
  147. package/dist/db/schema/running-job-executions.js +4 -4
  148. package/dist/db/schema/running-job-executions.js.map +1 -1
  149. package/dist/index.d.ts +6 -2
  150. package/dist/index.d.ts.map +1 -1
  151. package/dist/index.js +45 -39
  152. package/dist/index.js.map +1 -1
  153. package/dist/installation-provisioning.d.ts +14 -0
  154. package/dist/installation-provisioning.d.ts.map +1 -0
  155. package/dist/installation-provisioning.js +7 -0
  156. package/dist/installation-provisioning.js.map +1 -0
  157. package/dist/metrics/index.d.ts +1 -1
  158. package/dist/metrics/index.d.ts.map +1 -1
  159. package/dist/metrics/index.js +1 -1
  160. package/dist/metrics/index.js.map +1 -1
  161. package/dist/metrics/instance.d.ts +11 -7
  162. package/dist/metrics/instance.d.ts.map +1 -1
  163. package/dist/metrics/instance.js +13 -7
  164. package/dist/metrics/instance.js.map +1 -1
  165. package/dist/presentation/auth/index.d.ts +1 -0
  166. package/dist/presentation/auth/index.d.ts.map +1 -1
  167. package/dist/presentation/auth/index.js +1 -0
  168. package/dist/presentation/auth/index.js.map +1 -1
  169. package/dist/presentation/auth/provisioner-token-auth.d.ts.map +1 -1
  170. package/dist/presentation/auth/provisioner-token-auth.js +5 -1
  171. package/dist/presentation/auth/provisioner-token-auth.js.map +1 -1
  172. package/dist/presentation/auth/runner-control-session-auth.d.ts +4 -0
  173. package/dist/presentation/auth/runner-control-session-auth.d.ts.map +1 -0
  174. package/dist/presentation/auth/runner-control-session-auth.js +29 -0
  175. package/dist/presentation/auth/runner-control-session-auth.js.map +1 -0
  176. package/dist/presentation/auth/runner-registration-token-auth.d.ts +5 -1
  177. package/dist/presentation/auth/runner-registration-token-auth.d.ts.map +1 -1
  178. package/dist/presentation/auth/runner-registration-token-auth.js +21 -1
  179. package/dist/presentation/auth/runner-registration-token-auth.js.map +1 -1
  180. package/dist/presentation/dto/index.d.ts +1 -2
  181. package/dist/presentation/dto/index.d.ts.map +1 -1
  182. package/dist/presentation/dto/index.js +1 -2
  183. package/dist/presentation/dto/index.js.map +1 -1
  184. package/dist/presentation/dto/poll-demand.d.ts.map +1 -1
  185. package/dist/presentation/dto/poll-demand.js +7 -1
  186. package/dist/presentation/dto/poll-demand.js.map +1 -1
  187. package/dist/presentation/dto/provisioner-token.d.ts +1 -0
  188. package/dist/presentation/dto/provisioner-token.d.ts.map +1 -1
  189. package/dist/presentation/dto/provisioner-token.js +5 -1
  190. package/dist/presentation/dto/provisioner-token.js.map +1 -1
  191. package/dist/presentation/dto/{provisioned-runners.d.ts → runner-instances.d.ts} +9 -9
  192. package/dist/presentation/dto/runner-instances.d.ts.map +1 -0
  193. package/dist/presentation/dto/{provisioned-runners.js → runner-instances.js} +6 -6
  194. package/dist/presentation/dto/runner-instances.js.map +1 -0
  195. package/dist/presentation/index.d.ts +2 -2
  196. package/dist/presentation/index.d.ts.map +1 -1
  197. package/dist/presentation/index.js +2 -2
  198. package/dist/presentation/index.js.map +1 -1
  199. package/dist/presentation/inter-module.d.ts +5 -0
  200. package/dist/presentation/inter-module.d.ts.map +1 -0
  201. package/dist/presentation/inter-module.js +43 -0
  202. package/dist/presentation/inter-module.js.map +1 -0
  203. package/dist/presentation/routes/assign-runner-instances.d.ts +2 -0
  204. package/dist/presentation/routes/assign-runner-instances.d.ts.map +1 -0
  205. package/dist/presentation/routes/assign-runner-instances.js +40 -0
  206. package/dist/presentation/routes/assign-runner-instances.js.map +1 -0
  207. package/dist/presentation/routes/get-provisioner-me.d.ts.map +1 -1
  208. package/dist/presentation/routes/get-provisioner-me.js +5 -1
  209. package/dist/presentation/routes/get-provisioner-me.js.map +1 -1
  210. package/dist/presentation/routes/heartbeat.d.ts +2 -1
  211. package/dist/presentation/routes/heartbeat.d.ts.map +1 -1
  212. package/dist/presentation/routes/heartbeat.js +60 -50
  213. package/dist/presentation/routes/heartbeat.js.map +1 -1
  214. package/dist/presentation/routes/index.d.ts +3 -1
  215. package/dist/presentation/routes/index.d.ts.map +1 -1
  216. package/dist/presentation/routes/index.js +80 -51
  217. package/dist/presentation/routes/index.js.map +1 -1
  218. package/dist/presentation/routes/poll-demand.d.ts +2 -0
  219. package/dist/presentation/routes/poll-demand.d.ts.map +1 -1
  220. package/dist/presentation/routes/poll-demand.js +86 -45
  221. package/dist/presentation/routes/poll-demand.js.map +1 -1
  222. package/dist/presentation/routes/reconcile-runner-instances.d.ts +2 -0
  223. package/dist/presentation/routes/reconcile-runner-instances.d.ts.map +1 -0
  224. package/dist/presentation/routes/reconcile-runner-instances.js +27 -0
  225. package/dist/presentation/routes/reconcile-runner-instances.js.map +1 -0
  226. package/dist/presentation/routes/register.d.ts +2 -1
  227. package/dist/presentation/routes/register.d.ts.map +1 -1
  228. package/dist/presentation/routes/register.js +55 -52
  229. package/dist/presentation/routes/register.js.map +1 -1
  230. package/dist/presentation/routes/report-runner-instances.d.ts +2 -0
  231. package/dist/presentation/routes/report-runner-instances.d.ts.map +1 -0
  232. package/dist/presentation/routes/report-runner-instances.js +37 -0
  233. package/dist/presentation/routes/report-runner-instances.js.map +1 -0
  234. package/dist/presentation/routes/request-job.d.ts +2 -1
  235. package/dist/presentation/routes/request-job.d.ts.map +1 -1
  236. package/dist/presentation/routes/request-job.js +39 -36
  237. package/dist/presentation/routes/request-job.js.map +1 -1
  238. package/dist/presentation/routes/runner-enrollment.d.ts +8 -0
  239. package/dist/presentation/routes/runner-enrollment.d.ts.map +1 -0
  240. package/dist/presentation/routes/runner-enrollment.js +212 -0
  241. package/dist/presentation/routes/runner-enrollment.js.map +1 -0
  242. package/dist/temporal/activities/index.d.ts +2 -2
  243. package/dist/temporal/activities/index.d.ts.map +1 -1
  244. package/dist/temporal/activities/index.js +2 -2
  245. package/dist/temporal/activities/index.js.map +1 -1
  246. package/dist/temporal/activities/maintenance-activities.d.ts +1 -1
  247. package/dist/temporal/activities/maintenance-activities.d.ts.map +1 -1
  248. package/dist/temporal/activities/maintenance-activities.js +3 -3
  249. package/dist/temporal/activities/maintenance-activities.js.map +1 -1
  250. package/dist/temporal/workflows/index.bundle.js +3 -3
  251. package/dist/temporal/workflows/stuck-job-detector.js +2 -2
  252. package/dist/temporal/workflows/stuck-job-detector.js.map +1 -1
  253. package/dist/tsconfig.test.tsbuildinfo +1 -1
  254. package/drizzle/0000_initial.sql +91 -24
  255. package/drizzle/meta/0000_snapshot.json +392 -63
  256. package/package.json +21 -16
  257. package/src/config.test.ts +0 -26
  258. package/src/config.ts +48 -7
  259. package/src/core/demand.test.ts +29 -29
  260. package/src/core/demand.ts +22 -22
  261. package/src/core/entities/ephemeral-registration-token.ts +1 -1
  262. package/src/core/entities/provisioner-capability-snapshot.ts +12 -0
  263. package/src/core/entities/provisioner-token.ts +8 -2
  264. package/src/core/entities/{provisioned-runner.ts → runner-instance.ts} +6 -5
  265. package/src/core/entities/runner-session.ts +4 -2
  266. package/src/core/ephemeral-registration-tokens.test.ts +11 -11
  267. package/src/core/ephemeral-registration-tokens.ts +18 -20
  268. package/src/core/errors.ts +23 -4
  269. package/src/core/index.ts +37 -22
  270. package/src/core/job-executions.ts +3 -2
  271. package/src/core/maintenance.test.ts +13 -17
  272. package/src/core/maintenance.ts +5 -5
  273. package/src/core/provisioner-capability-snapshots.test.ts +35 -0
  274. package/src/core/provisioner-capability-snapshots.ts +12 -0
  275. package/src/core/provisioner-tokens.test.ts +20 -0
  276. package/src/core/provisioner-tokens.ts +33 -0
  277. package/src/core/rate-limit.test.ts +11 -27
  278. package/src/core/rate-limit.ts +3 -11
  279. package/src/core/runner-activation.ts +72 -0
  280. package/src/core/runner-assignments.ts +9 -0
  281. package/src/core/runner-control-sessions.ts +190 -0
  282. package/src/core/{provisioned-runners.test.ts → runner-instances.test.ts} +24 -24
  283. package/src/core/runner-instances.ts +301 -0
  284. package/src/core/runner-sessions.test.ts +112 -22
  285. package/src/core/runner-sessions.ts +25 -14
  286. package/src/db/db.ts +9 -2
  287. package/src/db/ephemeral-registration-tokens.test.ts +9 -9
  288. package/src/db/ephemeral-registration-tokens.ts +26 -26
  289. package/src/db/index.ts +30 -21
  290. package/src/db/job-executions.test.ts +13 -11
  291. package/src/db/job-executions.ts +33 -27
  292. package/src/db/provisioner-capability-snapshots.test.ts +175 -0
  293. package/src/db/provisioner-capability-snapshots.ts +130 -0
  294. package/src/db/provisioner-tokens.test.ts +139 -2
  295. package/src/db/provisioner-tokens.ts +123 -15
  296. package/src/db/reservations.test.ts +49 -0
  297. package/src/db/reservations.ts +146 -1
  298. package/src/db/runner-assignments.test.ts +163 -0
  299. package/src/db/runner-assignments.ts +115 -0
  300. package/src/db/{provisioned-runners.test.ts → runner-instances.test.ts} +442 -353
  301. package/src/db/runner-instances.ts +922 -0
  302. package/src/db/runner-sessions.test.ts +19 -6
  303. package/src/db/runner-sessions.ts +78 -2
  304. package/src/db/schema/ephemeral-registration-tokens.ts +4 -4
  305. package/src/db/schema/provisioner-capability-snapshots.ts +46 -0
  306. package/src/db/schema/provisioner-tokens.ts +33 -2
  307. package/src/db/schema/runner-activation-tokens.ts +22 -0
  308. package/src/db/schema/runner-control-sessions.ts +46 -0
  309. package/src/db/schema/{provisioned-runners.ts → runner-instances.ts} +28 -27
  310. package/src/db/schema/runner-sessions.ts +11 -7
  311. package/src/db/schema/running-job-executions.ts +5 -5
  312. package/src/index.test.ts +26 -0
  313. package/src/index.ts +38 -27
  314. package/src/installation-provisioning.ts +14 -0
  315. package/src/metrics/index.ts +5 -5
  316. package/src/metrics/instance.ts +25 -14
  317. package/src/presentation/auth/index.ts +4 -0
  318. package/src/presentation/auth/provisioner-token-auth.ts +8 -4
  319. package/src/presentation/auth/runner-control-session-auth.ts +31 -0
  320. package/src/presentation/auth/runner-registration-token-auth.ts +39 -3
  321. package/src/presentation/dto/index.ts +4 -8
  322. package/src/presentation/dto/poll-demand.ts +3 -1
  323. package/src/presentation/dto/provisioner-token.ts +5 -0
  324. package/src/presentation/dto/{provisioned-runners.ts → runner-instances.ts} +13 -15
  325. package/src/presentation/index.ts +2 -1
  326. package/src/presentation/inter-module.test.ts +15 -0
  327. package/src/presentation/inter-module.ts +53 -0
  328. package/src/presentation/routes/assign-runner-instances.ts +49 -0
  329. package/src/presentation/routes/get-provisioner-me.ts +3 -4
  330. package/src/presentation/routes/heartbeat.test.ts +4 -3
  331. package/src/presentation/routes/heartbeat.ts +67 -64
  332. package/src/presentation/routes/index.ts +91 -44
  333. package/src/presentation/routes/list-active-runners.test.ts +23 -23
  334. package/src/presentation/routes/manual-registration-tokens.test.ts +4 -3
  335. package/src/presentation/routes/poll-demand-release.test.ts +4 -4
  336. package/src/presentation/routes/poll-demand.test.ts +161 -22
  337. package/src/presentation/routes/poll-demand.ts +105 -45
  338. package/src/presentation/routes/provisioner-me.test.ts +37 -3
  339. package/src/presentation/routes/{reconcile-provisioned-runners.test.ts → reconcile-runner-instances.test.ts} +66 -61
  340. package/src/presentation/routes/reconcile-runner-instances.ts +30 -0
  341. package/src/presentation/routes/register.test.ts +9 -5
  342. package/src/presentation/routes/register.ts +61 -57
  343. package/src/presentation/routes/{report-provisioned-runners.test.ts → report-runner-instances.test.ts} +28 -28
  344. package/src/presentation/routes/{report-provisioned-runners.ts → report-runner-instances.ts} +14 -14
  345. package/src/presentation/routes/request-job.test.ts +5 -3
  346. package/src/presentation/routes/request-job.ts +41 -37
  347. package/src/presentation/routes/runner-enrollment.test.ts +319 -0
  348. package/src/presentation/routes/runner-enrollment.ts +196 -0
  349. package/src/temporal/activities/index.ts +2 -2
  350. package/src/temporal/activities/maintenance-activities.test.ts +7 -7
  351. package/src/temporal/activities/maintenance-activities.ts +3 -3
  352. package/src/temporal/workflows/stuck-job-detector.test.ts +3 -3
  353. package/src/temporal/workflows/stuck-job-detector.ts +2 -2
  354. package/test/env.ts +1 -3
  355. package/test/factories/ephemeral-registration-token.ts +2 -2
  356. package/test/factories/provisioner-token.ts +3 -1
  357. package/test/factories/runner-instance.ts +57 -0
  358. package/test/factories/runner-session.ts +3 -1
  359. package/test/fixtures/auth-inter-module.ts +30 -0
  360. package/test/globalSetup.ts +4 -1
  361. package/test/index.ts +2 -1
  362. package/tsconfig.build.tsbuildinfo +1 -1
  363. package/dist/core/entities/provisioned-runner.d.ts.map +0 -1
  364. package/dist/core/entities/provisioned-runner.js +0 -3
  365. package/dist/core/entities/provisioned-runner.js.map +0 -1
  366. package/dist/core/provisioned-runners.d.ts +0 -67
  367. package/dist/core/provisioned-runners.d.ts.map +0 -1
  368. package/dist/core/provisioned-runners.js +0 -162
  369. package/dist/core/provisioned-runners.js.map +0 -1
  370. package/dist/db/provisioned-runners.d.ts +0 -79
  371. package/dist/db/provisioned-runners.d.ts.map +0 -1
  372. package/dist/db/provisioned-runners.js +0 -466
  373. package/dist/db/provisioned-runners.js.map +0 -1
  374. package/dist/db/schema/provisioned-runners.d.ts.map +0 -1
  375. package/dist/db/schema/provisioned-runners.js.map +0 -1
  376. package/dist/presentation/dto/mint-registration-tokens.d.ts +0 -5
  377. package/dist/presentation/dto/mint-registration-tokens.d.ts.map +0 -1
  378. package/dist/presentation/dto/mint-registration-tokens.js +0 -19
  379. package/dist/presentation/dto/mint-registration-tokens.js.map +0 -1
  380. package/dist/presentation/dto/provisioned-runners.d.ts.map +0 -1
  381. package/dist/presentation/dto/provisioned-runners.js.map +0 -1
  382. package/dist/presentation/routes/mint-registration-tokens.d.ts +0 -2
  383. package/dist/presentation/routes/mint-registration-tokens.d.ts.map +0 -1
  384. package/dist/presentation/routes/mint-registration-tokens.js +0 -74
  385. package/dist/presentation/routes/mint-registration-tokens.js.map +0 -1
  386. package/dist/presentation/routes/reconcile-provisioned-runners.d.ts +0 -2
  387. package/dist/presentation/routes/reconcile-provisioned-runners.d.ts.map +0 -1
  388. package/dist/presentation/routes/reconcile-provisioned-runners.js +0 -27
  389. package/dist/presentation/routes/reconcile-provisioned-runners.js.map +0 -1
  390. package/dist/presentation/routes/report-provisioned-runners.d.ts +0 -2
  391. package/dist/presentation/routes/report-provisioned-runners.d.ts.map +0 -1
  392. package/dist/presentation/routes/report-provisioned-runners.js +0 -37
  393. package/dist/presentation/routes/report-provisioned-runners.js.map +0 -1
  394. package/src/core/provisioned-runners.ts +0 -301
  395. package/src/db/provisioned-runners.ts +0 -886
  396. package/src/presentation/dto/mint-registration-tokens.ts +0 -35
  397. package/src/presentation/routes/mint-registration-tokens.test.ts +0 -453
  398. package/src/presentation/routes/mint-registration-tokens.ts +0 -82
  399. package/src/presentation/routes/reconcile-provisioned-runners.ts +0 -30
  400. 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,7 @@
1
+ /** Atomically consumes reservation units by writing the immutable assignment on each runner. */
2
+ export declare function assignRunnerInstances(params: {
3
+ provisionerId: string;
4
+ reservationId: string;
5
+ runnerInstanceIds: string[];
6
+ }): Promise<string[]>;
7
+ //# sourceMappingURL=runner-assignments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-assignments.d.ts","sourceRoot":"","sources":["../../src/db/runner-assignments.ts"],"names":[],"mappings":"AAYA,gGAAgG;AAChG,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAiGpB"}
@@ -0,0 +1,55 @@
1
+ import { and, eq, inArray, isNull, sql } from 'drizzle-orm';
2
+ import { ReservationExpiredError, ReservationNotFoundError, RunnerInstanceAlreadyAssignedError, RunnerInstanceNotAssignableError } from '#core/errors.js';
3
+ import { db } from './db.js';
4
+ import { reservations } from './schema/reservations.js';
5
+ import { runnerControlSessions } from './schema/runner-control-sessions.js';
6
+ import { providerRunners } from './schema/runner-instances.js';
7
+ /** Atomically consumes reservation units by writing the immutable assignment on each runner. */ export async function assignRunnerInstances(params) {
8
+ const runnerInstanceIds = [
9
+ ...params.runnerInstanceIds
10
+ ].sort();
11
+ return await db().transaction(async (tx)=>{
12
+ await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${`runners_assignment:${params.provisionerId}:${params.reservationId}`}))`);
13
+ const [reservation] = await tx.select().from(reservations).where(and(eq(reservations.id, params.reservationId), eq(reservations.provisionerId, params.provisionerId))).limit(1).for('update');
14
+ if (!reservation) throw new ReservationNotFoundError(params.reservationId);
15
+ if (reservation.expiresAt <= new Date()) throw new ReservationExpiredError(params.reservationId);
16
+ const runnerRows = await tx.select({
17
+ id: providerRunners.id,
18
+ reservationId: providerRunners.reservationId,
19
+ workspaceId: providerRunners.workspaceId,
20
+ providerRunnerId: providerRunners.providerRunnerId,
21
+ labels: providerRunners.labels,
22
+ state: providerRunners.state
23
+ }).from(providerRunners).where(and(eq(providerRunners.provisionerId, params.provisionerId), inArray(providerRunners.id, runnerInstanceIds))).for('update');
24
+ const activeControlSessions = await tx.select({
25
+ runnerInstanceId: runnerControlSessions.runnerInstanceId
26
+ }).from(runnerControlSessions).where(and(inArray(runnerControlSessions.runnerInstanceId, runnerRows.map((runner)=>runner.id)), isNull(runnerControlSessions.closedAt)));
27
+ const runnerInstanceIdsWithControlSession = new Set(activeControlSessions.map((session)=>session.runnerInstanceId));
28
+ const runners = runnerRows.map((runner)=>({
29
+ ...runner,
30
+ controlSessionId: runnerInstanceIdsWithControlSession.has(runner.id) ? runner.id : null
31
+ }));
32
+ if (runners.length !== runnerInstanceIds.length) throw new RunnerInstanceNotAssignableError(runnerInstanceIds[0] ?? '');
33
+ const alreadyAssigned = runners.filter((runner)=>runner.reservationId !== null);
34
+ if (alreadyAssigned.some((runner)=>runner.reservationId !== reservation.id)) throw new RunnerInstanceAlreadyAssignedError(alreadyAssigned[0]?.id ?? '');
35
+ const newRunners = runners.filter((runner)=>runner.reservationId === null);
36
+ const assignedCount = await tx.select({
37
+ count: sql`count(*)::int`
38
+ }).from(providerRunners).where(eq(providerRunners.reservationId, reservation.id));
39
+ if ((assignedCount[0]?.count ?? 0) + newRunners.length > reservation.count) throw new RunnerInstanceNotAssignableError(newRunners[0]?.id ?? '');
40
+ for (const runner of newRunners){
41
+ if (runner.state !== 'running' || !runner.providerRunnerId || !runner.controlSessionId || !reservation.requiredLabels.every((label)=>runner.labels.includes(label))) throw new RunnerInstanceNotAssignableError(runner.id);
42
+ }
43
+ if (newRunners.length > 0) {
44
+ await tx.update(providerRunners).set({
45
+ workspaceId: reservation.workspaceId,
46
+ reservationId: reservation.id,
47
+ assignedAt: sql`now()`,
48
+ updatedAt: sql`now()`
49
+ }).where(inArray(providerRunners.id, newRunners.map((runner)=>runner.id)));
50
+ }
51
+ return params.runnerInstanceIds;
52
+ });
53
+ }
54
+
55
+ //# sourceMappingURL=runner-assignments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/runner-assignments.ts"],"sourcesContent":["import {and, eq, inArray, isNull, sql} from 'drizzle-orm';\nimport {\n ReservationExpiredError,\n ReservationNotFoundError,\n RunnerInstanceAlreadyAssignedError,\n RunnerInstanceNotAssignableError,\n} from '#core/errors.js';\nimport {db} from './db.js';\nimport {reservations} from './schema/reservations.js';\nimport {runnerControlSessions} from './schema/runner-control-sessions.js';\nimport {providerRunners} from './schema/runner-instances.js';\n\n/** Atomically consumes reservation units by writing the immutable assignment on each runner. */\nexport async function assignRunnerInstances(params: {\n provisionerId: string;\n reservationId: string;\n runnerInstanceIds: string[];\n}): Promise<string[]> {\n const runnerInstanceIds = [...params.runnerInstanceIds].sort();\n return await db().transaction(async (tx) => {\n await tx.execute(\n sql`select pg_advisory_xact_lock(hashtext(${`runners_assignment:${params.provisionerId}:${params.reservationId}`}))`,\n );\n const [reservation] = await tx\n .select()\n .from(reservations)\n .where(\n and(\n eq(reservations.id, params.reservationId),\n eq(reservations.provisionerId, params.provisionerId),\n ),\n )\n .limit(1)\n .for('update');\n if (!reservation) throw new ReservationNotFoundError(params.reservationId);\n if (reservation.expiresAt <= new Date())\n throw new ReservationExpiredError(params.reservationId);\n\n const runnerRows = await tx\n .select({\n id: providerRunners.id,\n reservationId: providerRunners.reservationId,\n workspaceId: providerRunners.workspaceId,\n providerRunnerId: providerRunners.providerRunnerId,\n labels: providerRunners.labels,\n state: providerRunners.state,\n })\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.provisionerId, params.provisionerId),\n inArray(providerRunners.id, runnerInstanceIds),\n ),\n )\n .for('update');\n const activeControlSessions = await tx\n .select({runnerInstanceId: runnerControlSessions.runnerInstanceId})\n .from(runnerControlSessions)\n .where(\n and(\n inArray(\n runnerControlSessions.runnerInstanceId,\n runnerRows.map((runner) => runner.id),\n ),\n isNull(runnerControlSessions.closedAt),\n ),\n );\n const runnerInstanceIdsWithControlSession = new Set(\n activeControlSessions.map((session) => session.runnerInstanceId),\n );\n const runners = runnerRows.map((runner) => ({\n ...runner,\n controlSessionId: runnerInstanceIdsWithControlSession.has(runner.id) ? runner.id : null,\n }));\n if (runners.length !== runnerInstanceIds.length)\n throw new RunnerInstanceNotAssignableError(runnerInstanceIds[0] ?? '');\n\n const alreadyAssigned = runners.filter((runner) => runner.reservationId !== null);\n if (alreadyAssigned.some((runner) => runner.reservationId !== reservation.id))\n throw new RunnerInstanceAlreadyAssignedError(alreadyAssigned[0]?.id ?? '');\n const newRunners = runners.filter((runner) => runner.reservationId === null);\n const assignedCount = await tx\n .select({count: sql<number>`count(*)::int`})\n .from(providerRunners)\n .where(eq(providerRunners.reservationId, reservation.id));\n if ((assignedCount[0]?.count ?? 0) + newRunners.length > reservation.count)\n throw new RunnerInstanceNotAssignableError(newRunners[0]?.id ?? '');\n for (const runner of newRunners) {\n if (\n runner.state !== 'running' ||\n !runner.providerRunnerId ||\n !runner.controlSessionId ||\n !reservation.requiredLabels.every((label) => runner.labels.includes(label))\n )\n throw new RunnerInstanceNotAssignableError(runner.id);\n }\n if (newRunners.length > 0) {\n await tx\n .update(providerRunners)\n .set({\n workspaceId: reservation.workspaceId,\n reservationId: reservation.id,\n assignedAt: sql`now()`,\n updatedAt: sql`now()`,\n })\n .where(\n inArray(\n providerRunners.id,\n newRunners.map((runner) => runner.id),\n ),\n );\n }\n return params.runnerInstanceIds;\n });\n}\n"],"names":["and","eq","inArray","isNull","sql","ReservationExpiredError","ReservationNotFoundError","RunnerInstanceAlreadyAssignedError","RunnerInstanceNotAssignableError","db","reservations","runnerControlSessions","providerRunners","assignRunnerInstances","params","runnerInstanceIds","sort","transaction","tx","execute","provisionerId","reservationId","reservation","select","from","where","id","limit","for","expiresAt","Date","runnerRows","workspaceId","providerRunnerId","labels","state","activeControlSessions","runnerInstanceId","map","runner","closedAt","runnerInstanceIdsWithControlSession","Set","session","runners","controlSessionId","has","length","alreadyAssigned","filter","some","newRunners","assignedCount","count","requiredLabels","every","label","includes","update","set","assignedAt","updatedAt"],"mappings":"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"}
@@ -0,0 +1,84 @@
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 attachRunnerInstanceProviderId(params: {
50
+ runnerInstanceId: string;
51
+ provisionerId: string;
52
+ providerRunnerId: string;
53
+ }): Promise<boolean>;
54
+ export declare function reportRunnerInstances(params: ReportRunnerInstancesParams): Promise<{
55
+ accepted: number;
56
+ reservationsReleased: number;
57
+ terminateIntentsHonored: RunnerInstanceTerminateIntent[];
58
+ }>;
59
+ export declare function listActiveRunnerInstanceCountsByTemplateTx(tx: Tx, params: {
60
+ workspaceId: string;
61
+ provisionerId: string;
62
+ }): Promise<ActiveRunnerInstanceTemplateCount[]>;
63
+ export declare function listActiveRunnerInstances(params: {
64
+ workspaceId: string;
65
+ windowSeconds: number;
66
+ limit?: number;
67
+ }): Promise<RunnerInstance[]>;
68
+ export declare function listProvisionerTerminateIntents(params: {
69
+ workspaceId: string;
70
+ provisionerId: string;
71
+ limit: number;
72
+ }): Promise<string[]>;
73
+ export declare function listProvisionerTerminateIntentRowsTx(tx: Tx, params: {
74
+ workspaceId: string;
75
+ provisionerId: string;
76
+ limit: number;
77
+ }): Promise<RunnerInstanceTerminateIntent[]>;
78
+ export declare function reconcileRunnerInstances(params: ReconcileRunnerInstancesParams): Promise<ReconcileRunnerInstancesDbResult>;
79
+ export declare function reapStaleRunnerInstances(params: {
80
+ thresholdSeconds: number;
81
+ limit: number;
82
+ }): Promise<ReapStaleRunnerInstancesResult>;
83
+ export declare function isTerminalState(state: RunnerInstanceState): boolean;
84
+ //# 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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"}