@shipfox/api-runners 5.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 +61 -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 +21 -16
  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
@@ -0,0 +1,168 @@
1
+ import { attachProviderRunnerId as attachProviderRunnerIdDb, createPlannedProvisionedCapacity as createPlannedProvisionedCapacityDb, isTerminalState, listActiveRunnerInstances, listActiveRunningJobExecutions, reconcileRunnerInstances as reconcileRunnerInstancesDb, reportRunnerInstances as reportRunnerInstancesDb } from '#db/index.js';
2
+ import { providerRunnerAbsentTerminatedCount, providerRunnerReconcileCallCount, providerRunnerReportCount, providerRunnerTerminateIntentHonoredCount, providerRunnerTerminateIntentIssuedCount, reservationReleasedCount } from '#metrics/instance.js';
3
+ import { config } from '../config.js';
4
+ export function createPlannedProvisionedCapacity(params) {
5
+ return createPlannedProvisionedCapacityDb(params);
6
+ }
7
+ export function attachProviderRunnerId(params) {
8
+ return attachProviderRunnerIdDb(params);
9
+ }
10
+ export async function reportRunnerInstances(params) {
11
+ const result = await reportRunnerInstancesDb(params);
12
+ for (const event of params.events){
13
+ providerRunnerReportCount.add(1, {
14
+ state: event.state
15
+ });
16
+ }
17
+ for (const intent of result.terminateIntentsHonored){
18
+ providerRunnerTerminateIntentHonoredCount.add(1, {
19
+ reason: intent.reason
20
+ });
21
+ }
22
+ if (result.reservationsReleased > 0) reservationReleasedCount.add(result.reservationsReleased);
23
+ return result;
24
+ }
25
+ export async function reconcileRunnerInstances(params) {
26
+ const result = await reconcileRunnerInstancesDb({
27
+ ...params,
28
+ terminateGraceSeconds: config.RUNNER_RECONCILE_TERMINATE_GRACE_SECONDS
29
+ });
30
+ if (result.reservationsReleased > 0) reservationReleasedCount.add(result.reservationsReleased);
31
+ providerRunnerReconcileCallCount.add(1);
32
+ if (result.absentIds.length > 0) providerRunnerAbsentTerminatedCount.add(result.absentIds.length);
33
+ const runners = reconcileRunnerInstancesFromDbResult({
34
+ observedRunnerInstanceIds: params.observedRunnerInstanceIds,
35
+ observedRows: result.observedRows,
36
+ boundJobExecutionsByRunnerInstanceId: result.boundJobExecutionsByRunnerInstanceId
37
+ });
38
+ for (const runner of runners){
39
+ if (runner.desiredIntentReason) {
40
+ providerRunnerTerminateIntentIssuedCount.add(1, {
41
+ surface: 'reconcile',
42
+ reason: runner.desiredIntentReason
43
+ });
44
+ }
45
+ }
46
+ return {
47
+ runners,
48
+ terminatedAbsentRunnerInstanceIds: result.absentIds
49
+ };
50
+ }
51
+ export function reconcileRunnerInstancesFromDbResult(params) {
52
+ const rowsByRunnerInstanceId = new Map(params.observedRows.map((row)=>[
53
+ row.providerRunnerId,
54
+ row
55
+ ]));
56
+ return params.observedRunnerInstanceIds.map((providerRunnerId)=>{
57
+ const row = rowsByRunnerInstanceId.get(providerRunnerId);
58
+ const boundJobExecution = params.boundJobExecutionsByRunnerInstanceId.get(providerRunnerId);
59
+ const desiredIntentReason = getDesiredIntentReason(row, boundJobExecution);
60
+ return {
61
+ providerRunnerId,
62
+ state: row?.state ?? null,
63
+ reservationId: row?.reservationId ?? null,
64
+ runnerSessionId: row?.runnerSessionId ?? null,
65
+ boundJobExecution: boundJobExecution ? toReconciledBoundJobExecution(boundJobExecution) : null,
66
+ desiredIntent: desiredIntentReason ? 'terminate' : 'keep',
67
+ desiredIntentReason
68
+ };
69
+ });
70
+ }
71
+ export async function listActiveRunners(params) {
72
+ const [providerRunnerRows, jobExecutionRows] = await Promise.all([
73
+ listActiveRunnerInstances({
74
+ workspaceId: params.workspaceId,
75
+ windowSeconds: config.RUNNER_ACTIVE_WINDOW_SECONDS
76
+ }),
77
+ listActiveRunningJobExecutions({
78
+ workspaceId: params.workspaceId,
79
+ windowSeconds: config.RUNNER_ACTIVE_WINDOW_SECONDS
80
+ })
81
+ ]);
82
+ return mergeActiveRunners(providerRunnerRows, jobExecutionRows);
83
+ }
84
+ function toReconciledBoundJobExecution(jobExecution) {
85
+ return {
86
+ jobId: jobExecution.jobId,
87
+ jobExecutionId: jobExecution.jobExecutionId,
88
+ workflowRunAttemptId: jobExecution.workflowRunAttemptId,
89
+ lastHeartbeatAt: jobExecution.lastHeartbeatAt,
90
+ cancellationRequestedAt: jobExecution.cancellationRequestedAt
91
+ };
92
+ }
93
+ export function desiredIntent(row, boundJobExecution) {
94
+ return getDesiredIntentReason(row, boundJobExecution) ? 'terminate' : 'keep';
95
+ }
96
+ function getDesiredIntentReason(row, boundJobExecution) {
97
+ if (!row) return null;
98
+ if (isTerminalState(row.state)) return 'terminal-state';
99
+ if (boundJobExecution?.cancellationRequestedAt) return 'job-cancelled';
100
+ return null;
101
+ }
102
+ function mergeActiveRunners(providerRunners, jobExecutions) {
103
+ const jobExecutionsByRunnerSessionId = new Map();
104
+ const jobExecutionsByRunnerInstanceId = new Map();
105
+ for (const jobExecution of jobExecutions){
106
+ const runnerJobExecutions = jobExecutionsByRunnerSessionId.get(jobExecution.runnerSessionId) ?? [];
107
+ runnerJobExecutions.push(jobExecution);
108
+ jobExecutionsByRunnerSessionId.set(jobExecution.runnerSessionId, runnerJobExecutions);
109
+ if (jobExecution.provisionerId && jobExecution.providerRunnerId) {
110
+ const key = providerRunnerKey(jobExecution.provisionerId, jobExecution.providerRunnerId);
111
+ const providerRunnerJobExecutions = jobExecutionsByRunnerInstanceId.get(key) ?? [];
112
+ providerRunnerJobExecutions.push(jobExecution);
113
+ jobExecutionsByRunnerInstanceId.set(key, providerRunnerJobExecutions);
114
+ }
115
+ }
116
+ const merged = [];
117
+ const usedJobExecutionIds = new Set();
118
+ for (const providerRunner of providerRunners){
119
+ const providerRunnerJobExecutions = jobExecutionsByRunnerInstanceId.get(providerRunnerKey(providerRunner.provisionerId, providerRunner.providerRunnerId)) ?? (providerRunner.runnerSessionId ? jobExecutionsByRunnerSessionId.get(providerRunner.runnerSessionId) : undefined);
120
+ if (!providerRunnerJobExecutions || providerRunnerJobExecutions.length === 0) {
121
+ merged.push(toActiveRunner(providerRunner, undefined));
122
+ continue;
123
+ }
124
+ let emitted = false;
125
+ for (const jobExecution of providerRunnerJobExecutions){
126
+ if (usedJobExecutionIds.has(jobExecution.jobExecutionId)) continue;
127
+ usedJobExecutionIds.add(jobExecution.jobExecutionId);
128
+ merged.push(toActiveRunner(providerRunner, jobExecution));
129
+ emitted = true;
130
+ }
131
+ if (!emitted) merged.push(toActiveRunner(providerRunner, undefined));
132
+ }
133
+ for (const jobExecution of jobExecutions){
134
+ if (usedJobExecutionIds.has(jobExecution.jobExecutionId)) continue;
135
+ merged.push(toActiveRunner(undefined, jobExecution));
136
+ }
137
+ return merged.sort(compareActiveRunners);
138
+ }
139
+ function providerRunnerKey(provisionerId, providerRunnerId) {
140
+ return `${provisionerId}:${providerRunnerId}`;
141
+ }
142
+ function toActiveRunner(providerRunner, jobExecution) {
143
+ return {
144
+ runnerSessionId: providerRunner?.runnerSessionId ?? jobExecution?.runnerSessionId ?? null,
145
+ providerRunnerId: providerRunner?.providerRunnerId ?? jobExecution?.providerRunnerId ?? null,
146
+ provisionerId: providerRunner?.provisionerId ?? jobExecution?.provisionerId ?? null,
147
+ state: jobExecution ? 'busy' : toActiveRunnerState(providerRunner?.state ?? 'running'),
148
+ labels: providerRunner?.labels ?? jobExecution?.runnerLabels ?? [],
149
+ templateKey: providerRunner?.templateKey ?? null,
150
+ providerKind: providerRunner?.providerKind ?? null,
151
+ jobId: jobExecution?.jobId ?? null,
152
+ workflowRunAttemptId: jobExecution?.workflowRunAttemptId ?? null,
153
+ projectId: jobExecution?.projectId ?? null,
154
+ reportedAt: providerRunner?.reportedAt ?? null,
155
+ lastHeartbeatAt: jobExecution?.lastHeartbeatAt ?? null
156
+ };
157
+ }
158
+ function toActiveRunnerState(state) {
159
+ if (state === 'starting' || state === 'stopping') return state;
160
+ return 'running';
161
+ }
162
+ function compareActiveRunners(a, b) {
163
+ const aTime = Math.max(a.lastHeartbeatAt?.getTime() ?? 0, a.reportedAt?.getTime() ?? 0);
164
+ const bTime = Math.max(b.lastHeartbeatAt?.getTime() ?? 0, b.reportedAt?.getTime() ?? 0);
165
+ return bTime - aTime;
166
+ }
167
+
168
+ //# sourceMappingURL=runner-instances.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/runner-instances.ts"],"sourcesContent":["import type {RunnerInstance, RunnerInstanceState} from '#core/entities/runner-instance.js';\nimport {\n attachProviderRunnerId as attachProviderRunnerIdDb,\n createPlannedProvisionedCapacity as createPlannedProvisionedCapacityDb,\n isTerminalState,\n listActiveRunnerInstances,\n listActiveRunningJobExecutions,\n type RunnerInstanceReportEvent,\n reconcileRunnerInstances as reconcileRunnerInstancesDb,\n reportRunnerInstances as reportRunnerInstancesDb,\n} from '#db/index.js';\nimport type {\n ActiveRunningJobExecution,\n RunnerInstanceBoundJobExecution,\n} from '#db/job-executions.js';\nimport {\n providerRunnerAbsentTerminatedCount,\n providerRunnerReconcileCallCount,\n providerRunnerReportCount,\n providerRunnerTerminateIntentHonoredCount,\n providerRunnerTerminateIntentIssuedCount,\n reservationReleasedCount,\n} from '#metrics/instance.js';\nimport {config} from '../config.js';\n\nexport interface ReportRunnerInstancesParams {\n workspaceId: string | null;\n provisionerId: string;\n events: RunnerInstanceReportEvent[];\n}\n\nexport function createPlannedProvisionedCapacity(params: {\n provisionerId: string;\n providerKind: string | null;\n templateKey: string | null;\n}): Promise<{capacityId: string}> {\n return createPlannedProvisionedCapacityDb(params);\n}\n\nexport function attachProviderRunnerId(params: {\n capacityId: string;\n provisionerId: string;\n providerRunnerId: string;\n}): Promise<boolean> {\n return attachProviderRunnerIdDb(params);\n}\n\nexport interface ReportRunnerInstancesResult {\n accepted: number;\n reservationsReleased: number;\n}\n\nexport interface ReconcileRunnerInstancesParams {\n workspaceId: string | null;\n provisionerId: string;\n observedRunnerInstanceIds: string[];\n}\n\nexport type ReconcileDesiredIntent = 'keep' | 'terminate';\ntype ReconcileDesiredIntentReason = 'job-cancelled' | 'terminal-state';\n\nexport interface ReconciledBoundJobExecution {\n jobId: string;\n jobExecutionId: string;\n workflowRunAttemptId: string;\n lastHeartbeatAt: Date;\n cancellationRequestedAt: Date | null;\n}\n\nexport interface ReconciledRunnerInstance {\n providerRunnerId: string;\n state: RunnerInstanceState | null;\n reservationId: string | null;\n runnerSessionId: string | null;\n boundJobExecution: ReconciledBoundJobExecution | null;\n desiredIntent: ReconcileDesiredIntent;\n desiredIntentReason: ReconcileDesiredIntentReason | null;\n}\n\nexport interface ReconcileRunnerInstancesResult {\n runners: ReconciledRunnerInstance[];\n terminatedAbsentRunnerInstanceIds: string[];\n}\n\nexport type ActiveRunnerState = 'starting' | 'running' | 'stopping' | 'busy';\n\nexport interface ActiveRunner {\n runnerSessionId: string | null;\n providerRunnerId: string | null;\n provisionerId: string | null;\n state: ActiveRunnerState;\n labels: string[];\n templateKey: string | null;\n providerKind: string | null;\n jobId: string | null;\n workflowRunAttemptId: string | null;\n projectId: string | null;\n reportedAt: Date | null;\n lastHeartbeatAt: Date | null;\n}\n\nexport async function reportRunnerInstances(\n params: ReportRunnerInstancesParams,\n): Promise<ReportRunnerInstancesResult> {\n const result = await reportRunnerInstancesDb(params);\n\n for (const event of params.events) {\n providerRunnerReportCount.add(1, {state: event.state});\n }\n for (const intent of result.terminateIntentsHonored) {\n providerRunnerTerminateIntentHonoredCount.add(1, {reason: intent.reason});\n }\n if (result.reservationsReleased > 0) reservationReleasedCount.add(result.reservationsReleased);\n\n return result;\n}\n\nexport async function reconcileRunnerInstances(\n params: ReconcileRunnerInstancesParams,\n): Promise<ReconcileRunnerInstancesResult> {\n const result = await reconcileRunnerInstancesDb({\n ...params,\n terminateGraceSeconds: config.RUNNER_RECONCILE_TERMINATE_GRACE_SECONDS,\n });\n\n if (result.reservationsReleased > 0) reservationReleasedCount.add(result.reservationsReleased);\n providerRunnerReconcileCallCount.add(1);\n if (result.absentIds.length > 0) providerRunnerAbsentTerminatedCount.add(result.absentIds.length);\n\n const runners = reconcileRunnerInstancesFromDbResult({\n observedRunnerInstanceIds: params.observedRunnerInstanceIds,\n observedRows: result.observedRows,\n boundJobExecutionsByRunnerInstanceId: result.boundJobExecutionsByRunnerInstanceId,\n });\n for (const runner of runners) {\n if (runner.desiredIntentReason) {\n providerRunnerTerminateIntentIssuedCount.add(1, {\n surface: 'reconcile',\n reason: runner.desiredIntentReason,\n });\n }\n }\n\n return {\n runners,\n terminatedAbsentRunnerInstanceIds: result.absentIds,\n };\n}\n\nexport function reconcileRunnerInstancesFromDbResult(params: {\n observedRunnerInstanceIds: string[];\n observedRows: RunnerInstance[];\n boundJobExecutionsByRunnerInstanceId: Map<string, RunnerInstanceBoundJobExecution>;\n}): ReconciledRunnerInstance[] {\n const rowsByRunnerInstanceId = new Map(\n params.observedRows.map((row) => [row.providerRunnerId, row]),\n );\n\n return params.observedRunnerInstanceIds.map((providerRunnerId) => {\n const row = rowsByRunnerInstanceId.get(providerRunnerId);\n const boundJobExecution = params.boundJobExecutionsByRunnerInstanceId.get(providerRunnerId);\n\n const desiredIntentReason = getDesiredIntentReason(row, boundJobExecution);\n\n return {\n providerRunnerId,\n state: row?.state ?? null,\n reservationId: row?.reservationId ?? null,\n runnerSessionId: row?.runnerSessionId ?? null,\n boundJobExecution: boundJobExecution\n ? toReconciledBoundJobExecution(boundJobExecution)\n : null,\n desiredIntent: desiredIntentReason ? 'terminate' : 'keep',\n desiredIntentReason,\n };\n });\n}\n\nexport async function listActiveRunners(params: {workspaceId: string}): Promise<ActiveRunner[]> {\n const [providerRunnerRows, jobExecutionRows] = await Promise.all([\n listActiveRunnerInstances({\n workspaceId: params.workspaceId,\n windowSeconds: config.RUNNER_ACTIVE_WINDOW_SECONDS,\n }),\n listActiveRunningJobExecutions({\n workspaceId: params.workspaceId,\n windowSeconds: config.RUNNER_ACTIVE_WINDOW_SECONDS,\n }),\n ]);\n\n return mergeActiveRunners(providerRunnerRows, jobExecutionRows);\n}\n\nfunction toReconciledBoundJobExecution(\n jobExecution: RunnerInstanceBoundJobExecution,\n): ReconciledBoundJobExecution {\n return {\n jobId: jobExecution.jobId,\n jobExecutionId: jobExecution.jobExecutionId,\n workflowRunAttemptId: jobExecution.workflowRunAttemptId,\n lastHeartbeatAt: jobExecution.lastHeartbeatAt,\n cancellationRequestedAt: jobExecution.cancellationRequestedAt,\n };\n}\n\nexport function desiredIntent(\n row: RunnerInstance | undefined,\n boundJobExecution: RunnerInstanceBoundJobExecution | undefined,\n): ReconcileDesiredIntent {\n return getDesiredIntentReason(row, boundJobExecution) ? 'terminate' : 'keep';\n}\n\nfunction getDesiredIntentReason(\n row: RunnerInstance | undefined,\n boundJobExecution: RunnerInstanceBoundJobExecution | undefined,\n): ReconcileDesiredIntentReason | null {\n if (!row) return null;\n if (isTerminalState(row.state)) return 'terminal-state';\n if (boundJobExecution?.cancellationRequestedAt) return 'job-cancelled';\n return null;\n}\n\nfunction mergeActiveRunners(\n providerRunners: RunnerInstance[],\n jobExecutions: ActiveRunningJobExecution[],\n): ActiveRunner[] {\n const jobExecutionsByRunnerSessionId = new Map<string, ActiveRunningJobExecution[]>();\n const jobExecutionsByRunnerInstanceId = new Map<string, ActiveRunningJobExecution[]>();\n for (const jobExecution of jobExecutions) {\n const runnerJobExecutions =\n jobExecutionsByRunnerSessionId.get(jobExecution.runnerSessionId) ?? [];\n runnerJobExecutions.push(jobExecution);\n jobExecutionsByRunnerSessionId.set(jobExecution.runnerSessionId, runnerJobExecutions);\n\n if (jobExecution.provisionerId && jobExecution.providerRunnerId) {\n const key = providerRunnerKey(jobExecution.provisionerId, jobExecution.providerRunnerId);\n const providerRunnerJobExecutions = jobExecutionsByRunnerInstanceId.get(key) ?? [];\n providerRunnerJobExecutions.push(jobExecution);\n jobExecutionsByRunnerInstanceId.set(key, providerRunnerJobExecutions);\n }\n }\n\n const merged: ActiveRunner[] = [];\n const usedJobExecutionIds = new Set<string>();\n\n for (const providerRunner of providerRunners) {\n const providerRunnerJobExecutions =\n jobExecutionsByRunnerInstanceId.get(\n providerRunnerKey(providerRunner.provisionerId, providerRunner.providerRunnerId),\n ) ??\n (providerRunner.runnerSessionId\n ? jobExecutionsByRunnerSessionId.get(providerRunner.runnerSessionId)\n : undefined);\n if (!providerRunnerJobExecutions || providerRunnerJobExecutions.length === 0) {\n merged.push(toActiveRunner(providerRunner, undefined));\n continue;\n }\n\n let emitted = false;\n for (const jobExecution of providerRunnerJobExecutions) {\n if (usedJobExecutionIds.has(jobExecution.jobExecutionId)) continue;\n usedJobExecutionIds.add(jobExecution.jobExecutionId);\n merged.push(toActiveRunner(providerRunner, jobExecution));\n emitted = true;\n }\n if (!emitted) merged.push(toActiveRunner(providerRunner, undefined));\n }\n\n for (const jobExecution of jobExecutions) {\n if (usedJobExecutionIds.has(jobExecution.jobExecutionId)) continue;\n merged.push(toActiveRunner(undefined, jobExecution));\n }\n\n return merged.sort(compareActiveRunners);\n}\n\nfunction providerRunnerKey(provisionerId: string, providerRunnerId: string): string {\n return `${provisionerId}:${providerRunnerId}`;\n}\n\nfunction toActiveRunner(\n providerRunner: RunnerInstance | undefined,\n jobExecution: ActiveRunningJobExecution | undefined,\n): ActiveRunner {\n return {\n runnerSessionId: providerRunner?.runnerSessionId ?? jobExecution?.runnerSessionId ?? null,\n providerRunnerId: providerRunner?.providerRunnerId ?? jobExecution?.providerRunnerId ?? null,\n provisionerId: providerRunner?.provisionerId ?? jobExecution?.provisionerId ?? null,\n state: jobExecution ? 'busy' : toActiveRunnerState(providerRunner?.state ?? 'running'),\n labels: providerRunner?.labels ?? jobExecution?.runnerLabels ?? [],\n templateKey: providerRunner?.templateKey ?? null,\n providerKind: providerRunner?.providerKind ?? null,\n jobId: jobExecution?.jobId ?? null,\n workflowRunAttemptId: jobExecution?.workflowRunAttemptId ?? null,\n projectId: jobExecution?.projectId ?? null,\n reportedAt: providerRunner?.reportedAt ?? null,\n lastHeartbeatAt: jobExecution?.lastHeartbeatAt ?? null,\n };\n}\n\nfunction toActiveRunnerState(state: RunnerInstanceState): ActiveRunnerState {\n if (state === 'starting' || state === 'stopping') return state;\n return 'running';\n}\n\nfunction compareActiveRunners(a: ActiveRunner, b: ActiveRunner): number {\n const aTime = Math.max(a.lastHeartbeatAt?.getTime() ?? 0, a.reportedAt?.getTime() ?? 0);\n const bTime = Math.max(b.lastHeartbeatAt?.getTime() ?? 0, b.reportedAt?.getTime() ?? 0);\n return bTime - aTime;\n}\n"],"names":["attachProviderRunnerId","attachProviderRunnerIdDb","createPlannedProvisionedCapacity","createPlannedProvisionedCapacityDb","isTerminalState","listActiveRunnerInstances","listActiveRunningJobExecutions","reconcileRunnerInstances","reconcileRunnerInstancesDb","reportRunnerInstances","reportRunnerInstancesDb","providerRunnerAbsentTerminatedCount","providerRunnerReconcileCallCount","providerRunnerReportCount","providerRunnerTerminateIntentHonoredCount","providerRunnerTerminateIntentIssuedCount","reservationReleasedCount","config","params","result","event","events","add","state","intent","terminateIntentsHonored","reason","reservationsReleased","terminateGraceSeconds","RUNNER_RECONCILE_TERMINATE_GRACE_SECONDS","absentIds","length","runners","reconcileRunnerInstancesFromDbResult","observedRunnerInstanceIds","observedRows","boundJobExecutionsByRunnerInstanceId","runner","desiredIntentReason","surface","terminatedAbsentRunnerInstanceIds","rowsByRunnerInstanceId","Map","map","row","providerRunnerId","get","boundJobExecution","getDesiredIntentReason","reservationId","runnerSessionId","toReconciledBoundJobExecution","desiredIntent","listActiveRunners","providerRunnerRows","jobExecutionRows","Promise","all","workspaceId","windowSeconds","RUNNER_ACTIVE_WINDOW_SECONDS","mergeActiveRunners","jobExecution","jobId","jobExecutionId","workflowRunAttemptId","lastHeartbeatAt","cancellationRequestedAt","providerRunners","jobExecutions","jobExecutionsByRunnerSessionId","jobExecutionsByRunnerInstanceId","runnerJobExecutions","push","set","provisionerId","key","providerRunnerKey","providerRunnerJobExecutions","merged","usedJobExecutionIds","Set","providerRunner","undefined","toActiveRunner","emitted","has","sort","compareActiveRunners","toActiveRunnerState","labels","runnerLabels","templateKey","providerKind","projectId","reportedAt","a","b","aTime","Math","max","getTime","bTime"],"mappings":"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"}
@@ -1,9 +1,10 @@
1
+ import type { AuthInterModuleClient } from '@shipfox/api-auth-dto/inter-module';
1
2
  import type { RunnerToolCapabilitiesDto } from '@shipfox/api-runners-dto';
2
3
  import type { RunnerSession } from './entities/runner-session.js';
3
4
  export interface RegisterRunnerSessionResult {
4
5
  session: RunnerSession;
5
6
  sessionToken: string;
6
- mode: 'manual' | 'ephemeral';
7
+ mode: 'manual' | 'ephemeral' | 'activation';
7
8
  maxClaims: number | null;
8
9
  }
9
10
  export type RunnerRegistrationCredential = {
@@ -16,9 +17,14 @@ export type RunnerRegistrationCredential = {
16
17
  workspaceId: string;
17
18
  provisionerId: string;
18
19
  reservationId: string | null;
19
- provisionedRunnerId: string;
20
+ providerRunnerId: string;
21
+ } | {
22
+ kind: 'activation';
23
+ activationTokenId: string;
24
+ workspaceId: string;
20
25
  };
21
26
  export declare function registerRunnerSession(params: {
27
+ auth: AuthInterModuleClient;
22
28
  credential: RunnerRegistrationCredential;
23
29
  labels: string[];
24
30
  toolCapabilities?: RunnerToolCapabilitiesDto | null;
@@ -1 +1 @@
1
- {"version":3,"file":"runner-sessions.d.ts","sourceRoot":"","sources":["../../src/core/runner-sessions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,0BAA0B,CAAC;AAIxE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,8BAA8B,CAAC;AAGhE,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,aAAa,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,4BAA4B,GACpC;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB,GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEN,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,UAAU,EAAE,4BAA4B,CAAC;IACzC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,gBAAgB,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC;CACrD,GAAG,OAAO,CAAC,2BAA2B,CAAC,CA+BvC"}
1
+ {"version":3,"file":"runner-sessions.d.ts","sourceRoot":"","sources":["../../src/core/runner-sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,0BAA0B,CAAC;AAOxE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,8BAA8B,CAAC;AAGhE,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,aAAa,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,QAAQ,GAAG,WAAW,GAAG,YAAY,CAAC;IAC5C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,4BAA4B,GACpC;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB,GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;CAC1B,GACD;IAAC,IAAI,EAAE,YAAY,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,CAAC;AAEzE,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,IAAI,EAAE,qBAAqB,CAAC;IAC5B,UAAU,EAAE,4BAA4B,CAAC;IACzC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,gBAAgB,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC;CACrD,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAqCvC"}
@@ -1,7 +1,6 @@
1
- import { issueRunnerSessionToken } from '@shipfox/api-auth';
2
1
  import { canonicalizeLabels } from '@shipfox/runner-labels';
3
2
  import { createRunnerSessionConsumingEphemeralToken } from '#db/ephemeral-registration-tokens.js';
4
- import { createRunnerSession } from '#db/runner-sessions.js';
3
+ import { createRunnerSession, createRunnerSessionConsumingActivationToken } from '#db/runner-sessions.js';
5
4
  import { EmptyRunnerLabelsError } from './errors.js';
6
5
  export async function registerRunnerSession(params) {
7
6
  const labels = [
@@ -9,21 +8,25 @@ export async function registerRunnerSession(params) {
9
8
  ];
10
9
  if (labels.length === 0) throw new EmptyRunnerLabelsError();
11
10
  const mode = params.credential.kind;
12
- const maxClaims = params.credential.kind === 'ephemeral' ? 1 : null;
11
+ const maxClaims = params.credential.kind === 'manual' ? null : 1;
13
12
  const session = params.credential.kind === 'manual' ? await createRunnerSession({
14
13
  workspaceId: params.credential.workspaceId,
15
14
  scope: 'workspace',
16
15
  registrationTokenId: params.credential.registrationTokenId,
17
16
  labels,
18
17
  toolCapabilities: params.toolCapabilities ?? null
19
- }) : await createRunnerSessionConsumingEphemeralToken({
18
+ }) : params.credential.kind === 'ephemeral' ? await createRunnerSessionConsumingEphemeralToken({
20
19
  ephemeralTokenId: params.credential.ephemeralTokenId,
21
20
  workspaceId: params.credential.workspaceId,
22
21
  labels,
23
22
  toolCapabilities: params.toolCapabilities ?? null,
24
23
  maxClaims: 1
24
+ }) : await createRunnerSessionConsumingActivationToken({
25
+ activationTokenId: params.credential.activationTokenId,
26
+ labels,
27
+ toolCapabilities: params.toolCapabilities ?? null
25
28
  });
26
- const sessionToken = await issueRunnerSessionToken({
29
+ const { token: sessionToken } = await params.auth.mintRunnerSessionToken({
27
30
  runnerSessionId: session.id,
28
31
  workspaceId: session.workspaceId,
29
32
  scope: session.scope,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/runner-sessions.ts"],"sourcesContent":["import {issueRunnerSessionToken} from '@shipfox/api-auth';\nimport type {RunnerToolCapabilitiesDto} from '@shipfox/api-runners-dto';\nimport {canonicalizeLabels} from '@shipfox/runner-labels';\nimport {createRunnerSessionConsumingEphemeralToken} from '#db/ephemeral-registration-tokens.js';\nimport {createRunnerSession} from '#db/runner-sessions.js';\nimport type {RunnerSession} from './entities/runner-session.js';\nimport {EmptyRunnerLabelsError} from './errors.js';\n\nexport interface RegisterRunnerSessionResult {\n session: RunnerSession;\n sessionToken: string;\n mode: 'manual' | 'ephemeral';\n maxClaims: number | null;\n}\n\nexport type RunnerRegistrationCredential =\n | {\n kind: 'manual';\n registrationTokenId: string;\n workspaceId: string;\n }\n | {\n kind: 'ephemeral';\n ephemeralTokenId: string;\n workspaceId: string;\n provisionerId: string;\n reservationId: string | null;\n provisionedRunnerId: string;\n };\n\nexport async function registerRunnerSession(params: {\n credential: RunnerRegistrationCredential;\n labels: string[];\n toolCapabilities?: RunnerToolCapabilitiesDto | null;\n}): Promise<RegisterRunnerSessionResult> {\n const labels = [...canonicalizeLabels(params.labels)];\n if (labels.length === 0) throw new EmptyRunnerLabelsError();\n\n const mode = params.credential.kind;\n const maxClaims = params.credential.kind === 'ephemeral' ? 1 : null;\n const session =\n params.credential.kind === 'manual'\n ? await createRunnerSession({\n workspaceId: params.credential.workspaceId,\n scope: 'workspace',\n registrationTokenId: params.credential.registrationTokenId,\n labels,\n toolCapabilities: params.toolCapabilities ?? null,\n })\n : await createRunnerSessionConsumingEphemeralToken({\n ephemeralTokenId: params.credential.ephemeralTokenId,\n workspaceId: params.credential.workspaceId,\n labels,\n toolCapabilities: params.toolCapabilities ?? null,\n maxClaims: 1,\n });\n const sessionToken = await issueRunnerSessionToken({\n runnerSessionId: session.id,\n workspaceId: session.workspaceId,\n scope: session.scope,\n labels: session.labels,\n maxClaims,\n });\n\n return {session, sessionToken, mode, maxClaims};\n}\n"],"names":["issueRunnerSessionToken","canonicalizeLabels","createRunnerSessionConsumingEphemeralToken","createRunnerSession","EmptyRunnerLabelsError","registerRunnerSession","params","labels","length","mode","credential","kind","maxClaims","session","workspaceId","scope","registrationTokenId","toolCapabilities","ephemeralTokenId","sessionToken","runnerSessionId","id"],"mappings":"AAAA,SAAQA,uBAAuB,QAAO,oBAAoB;AAE1D,SAAQC,kBAAkB,QAAO,yBAAyB;AAC1D,SAAQC,0CAA0C,QAAO,uCAAuC;AAChG,SAAQC,mBAAmB,QAAO,yBAAyB;AAE3D,SAAQC,sBAAsB,QAAO,cAAc;AAwBnD,OAAO,eAAeC,sBAAsBC,MAI3C;IACC,MAAMC,SAAS;WAAIN,mBAAmBK,OAAOC,MAAM;KAAE;IACrD,IAAIA,OAAOC,MAAM,KAAK,GAAG,MAAM,IAAIJ;IAEnC,MAAMK,OAAOH,OAAOI,UAAU,CAACC,IAAI;IACnC,MAAMC,YAAYN,OAAOI,UAAU,CAACC,IAAI,KAAK,cAAc,IAAI;IAC/D,MAAME,UACJP,OAAOI,UAAU,CAACC,IAAI,KAAK,WACvB,MAAMR,oBAAoB;QACxBW,aAAaR,OAAOI,UAAU,CAACI,WAAW;QAC1CC,OAAO;QACPC,qBAAqBV,OAAOI,UAAU,CAACM,mBAAmB;QAC1DT;QACAU,kBAAkBX,OAAOW,gBAAgB,IAAI;IAC/C,KACA,MAAMf,2CAA2C;QAC/CgB,kBAAkBZ,OAAOI,UAAU,CAACQ,gBAAgB;QACpDJ,aAAaR,OAAOI,UAAU,CAACI,WAAW;QAC1CP;QACAU,kBAAkBX,OAAOW,gBAAgB,IAAI;QAC7CL,WAAW;IACb;IACN,MAAMO,eAAe,MAAMnB,wBAAwB;QACjDoB,iBAAiBP,QAAQQ,EAAE;QAC3BP,aAAaD,QAAQC,WAAW;QAChCC,OAAOF,QAAQE,KAAK;QACpBR,QAAQM,QAAQN,MAAM;QACtBK;IACF;IAEA,OAAO;QAACC;QAASM;QAAcV;QAAMG;IAAS;AAChD"}
1
+ {"version":3,"sources":["../../src/core/runner-sessions.ts"],"sourcesContent":["import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';\nimport type {RunnerToolCapabilitiesDto} from '@shipfox/api-runners-dto';\nimport {canonicalizeLabels} from '@shipfox/runner-labels';\nimport {createRunnerSessionConsumingEphemeralToken} from '#db/ephemeral-registration-tokens.js';\nimport {\n createRunnerSession,\n createRunnerSessionConsumingActivationToken,\n} from '#db/runner-sessions.js';\nimport type {RunnerSession} from './entities/runner-session.js';\nimport {EmptyRunnerLabelsError} from './errors.js';\n\nexport interface RegisterRunnerSessionResult {\n session: RunnerSession;\n sessionToken: string;\n mode: 'manual' | 'ephemeral' | 'activation';\n maxClaims: number | null;\n}\n\nexport type RunnerRegistrationCredential =\n | {\n kind: 'manual';\n registrationTokenId: string;\n workspaceId: string;\n }\n | {\n kind: 'ephemeral';\n ephemeralTokenId: string;\n workspaceId: string;\n provisionerId: string;\n reservationId: string | null;\n providerRunnerId: string;\n }\n | {kind: 'activation'; activationTokenId: string; workspaceId: string};\n\nexport async function registerRunnerSession(params: {\n auth: AuthInterModuleClient;\n credential: RunnerRegistrationCredential;\n labels: string[];\n toolCapabilities?: RunnerToolCapabilitiesDto | null;\n}): Promise<RegisterRunnerSessionResult> {\n const labels = [...canonicalizeLabels(params.labels)];\n if (labels.length === 0) throw new EmptyRunnerLabelsError();\n\n const mode = params.credential.kind;\n const maxClaims = params.credential.kind === 'manual' ? null : 1;\n const session =\n params.credential.kind === 'manual'\n ? await createRunnerSession({\n workspaceId: params.credential.workspaceId,\n scope: 'workspace',\n registrationTokenId: params.credential.registrationTokenId,\n labels,\n toolCapabilities: params.toolCapabilities ?? null,\n })\n : params.credential.kind === 'ephemeral'\n ? await createRunnerSessionConsumingEphemeralToken({\n ephemeralTokenId: params.credential.ephemeralTokenId,\n workspaceId: params.credential.workspaceId,\n labels,\n toolCapabilities: params.toolCapabilities ?? null,\n maxClaims: 1,\n })\n : await createRunnerSessionConsumingActivationToken({\n activationTokenId: params.credential.activationTokenId,\n labels,\n toolCapabilities: params.toolCapabilities ?? null,\n });\n const {token: sessionToken} = await params.auth.mintRunnerSessionToken({\n runnerSessionId: session.id,\n workspaceId: session.workspaceId,\n scope: session.scope,\n labels: session.labels,\n maxClaims,\n });\n\n return {session, sessionToken, mode, maxClaims};\n}\n"],"names":["canonicalizeLabels","createRunnerSessionConsumingEphemeralToken","createRunnerSession","createRunnerSessionConsumingActivationToken","EmptyRunnerLabelsError","registerRunnerSession","params","labels","length","mode","credential","kind","maxClaims","session","workspaceId","scope","registrationTokenId","toolCapabilities","ephemeralTokenId","activationTokenId","token","sessionToken","auth","mintRunnerSessionToken","runnerSessionId","id"],"mappings":"AAEA,SAAQA,kBAAkB,QAAO,yBAAyB;AAC1D,SAAQC,0CAA0C,QAAO,uCAAuC;AAChG,SACEC,mBAAmB,EACnBC,2CAA2C,QACtC,yBAAyB;AAEhC,SAAQC,sBAAsB,QAAO,cAAc;AAyBnD,OAAO,eAAeC,sBAAsBC,MAK3C;IACC,MAAMC,SAAS;WAAIP,mBAAmBM,OAAOC,MAAM;KAAE;IACrD,IAAIA,OAAOC,MAAM,KAAK,GAAG,MAAM,IAAIJ;IAEnC,MAAMK,OAAOH,OAAOI,UAAU,CAACC,IAAI;IACnC,MAAMC,YAAYN,OAAOI,UAAU,CAACC,IAAI,KAAK,WAAW,OAAO;IAC/D,MAAME,UACJP,OAAOI,UAAU,CAACC,IAAI,KAAK,WACvB,MAAMT,oBAAoB;QACxBY,aAAaR,OAAOI,UAAU,CAACI,WAAW;QAC1CC,OAAO;QACPC,qBAAqBV,OAAOI,UAAU,CAACM,mBAAmB;QAC1DT;QACAU,kBAAkBX,OAAOW,gBAAgB,IAAI;IAC/C,KACAX,OAAOI,UAAU,CAACC,IAAI,KAAK,cACzB,MAAMV,2CAA2C;QAC/CiB,kBAAkBZ,OAAOI,UAAU,CAACQ,gBAAgB;QACpDJ,aAAaR,OAAOI,UAAU,CAACI,WAAW;QAC1CP;QACAU,kBAAkBX,OAAOW,gBAAgB,IAAI;QAC7CL,WAAW;IACb,KACA,MAAMT,4CAA4C;QAChDgB,mBAAmBb,OAAOI,UAAU,CAACS,iBAAiB;QACtDZ;QACAU,kBAAkBX,OAAOW,gBAAgB,IAAI;IAC/C;IACR,MAAM,EAACG,OAAOC,YAAY,EAAC,GAAG,MAAMf,OAAOgB,IAAI,CAACC,sBAAsB,CAAC;QACrEC,iBAAiBX,QAAQY,EAAE;QAC3BX,aAAaD,QAAQC,WAAW;QAChCC,OAAOF,QAAQE,KAAK;QACpBR,QAAQM,QAAQN,MAAM;QACtBK;IACF;IAEA,OAAO;QAACC;QAASQ;QAAcZ;QAAMG;IAAS;AAChD"}
@@ -0,0 +1,7 @@
1
+ import type { CapacityAssignment } from '#core/entities/capacity-assignment.js';
2
+ export declare function assignCapacityBatch(params: {
3
+ provisionerId: string;
4
+ reservationId: string;
5
+ capacityIds: string[];
6
+ }): Promise<CapacityAssignment[]>;
7
+ //# sourceMappingURL=capacity-assignments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capacity-assignments.d.ts","sourceRoot":"","sources":["../../src/db/capacity-assignments.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,uCAAuC,CAAC;AAY9E,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAkGhC"}
@@ -0,0 +1,72 @@
1
+ import { and, eq, inArray, sql } from 'drizzle-orm';
2
+ import { CapacityAlreadyAssignedError, CapacityNotAssignableError, ReservationExpiredError, ReservationNotFoundError } from '#core/errors.js';
3
+ import { db } from './db.js';
4
+ import { capacityAssignments, toCapacityAssignment } from './schema/capacity-assignments.js';
5
+ import { reservations } from './schema/reservations.js';
6
+ import { providerRunners } from './schema/runner-instances.js';
7
+ export async function assignCapacityBatch(params) {
8
+ return await db().transaction(async (tx)=>{
9
+ const lockKey = [
10
+ 'runners_capacity_assignments',
11
+ params.provisionerId,
12
+ params.reservationId
13
+ ].join(':');
14
+ await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${lockKey}))`);
15
+ for (const capacityId of [
16
+ ...params.capacityIds
17
+ ].sort()){
18
+ await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${[
19
+ 'runners_capacity_assignments',
20
+ capacityId
21
+ ].join(':')}))`);
22
+ }
23
+ const [reservation] = await tx.select().from(reservations).where(and(eq(reservations.id, params.reservationId), eq(reservations.provisionerId, params.provisionerId))).limit(1);
24
+ if (!reservation) throw new ReservationNotFoundError(params.reservationId);
25
+ if (reservation.expiresAt <= new Date()) throw new ReservationExpiredError(params.reservationId);
26
+ const existing = await tx.select().from(capacityAssignments).where(eq(capacityAssignments.reservationId, reservation.id));
27
+ const existingByCapacityId = new Map(existing.map((assignment)=>[
28
+ assignment.capacityId,
29
+ assignment
30
+ ]));
31
+ const newCapacityIds = params.capacityIds.filter((capacityId)=>!existingByCapacityId.has(capacityId));
32
+ if (existing.length + newCapacityIds.length > reservation.count) throw new CapacityNotAssignableError(params.capacityIds[0] ?? '');
33
+ if (newCapacityIds.length === 0) {
34
+ return params.capacityIds.flatMap((capacityId)=>{
35
+ const assignment = existingByCapacityId.get(capacityId);
36
+ return assignment ? [
37
+ toCapacityAssignment(assignment)
38
+ ] : [];
39
+ });
40
+ }
41
+ const capacities = await tx.select().from(providerRunners).where(and(eq(providerRunners.provisionerId, params.provisionerId), inArray(providerRunners.id, newCapacityIds)));
42
+ if (capacities.length !== newCapacityIds.length) throw new CapacityNotAssignableError(params.capacityIds[0] ?? '');
43
+ for (const capacity of capacities){
44
+ if (capacity.state !== 'starting' && capacity.state !== 'running') throw new CapacityNotAssignableError(capacity.id);
45
+ if (!capacity.providerRunnerId || capacity.labels.length === 0 || !reservation.requiredLabels.every((label)=>capacity.labels.includes(label))) {
46
+ throw new CapacityNotAssignableError(capacity.id);
47
+ }
48
+ }
49
+ const assignedCapacity = await tx.select({
50
+ capacityId: capacityAssignments.capacityId
51
+ }).from(capacityAssignments).where(inArray(capacityAssignments.capacityId, newCapacityIds));
52
+ if (assignedCapacity.length > 0) throw new CapacityAlreadyAssignedError(assignedCapacity[0]?.capacityId ?? '');
53
+ const inserted = await tx.insert(capacityAssignments).values(newCapacityIds.map((capacityId)=>({
54
+ capacityId,
55
+ reservationId: reservation.id,
56
+ workspaceId: reservation.workspaceId,
57
+ provisionerId: params.provisionerId
58
+ }))).returning();
59
+ const insertedByCapacityId = new Map(inserted.map((assignment)=>[
60
+ assignment.capacityId,
61
+ assignment
62
+ ]));
63
+ return params.capacityIds.flatMap((capacityId)=>{
64
+ const assignment = existingByCapacityId.get(capacityId) ?? insertedByCapacityId.get(capacityId);
65
+ return assignment ? [
66
+ toCapacityAssignment(assignment)
67
+ ] : [];
68
+ });
69
+ });
70
+ }
71
+
72
+ //# sourceMappingURL=capacity-assignments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/capacity-assignments.ts"],"sourcesContent":["import {and, eq, inArray, sql} from 'drizzle-orm';\nimport type {CapacityAssignment} from '#core/entities/capacity-assignment.js';\nimport {\n CapacityAlreadyAssignedError,\n CapacityNotAssignableError,\n ReservationExpiredError,\n ReservationNotFoundError,\n} from '#core/errors.js';\nimport {db} from './db.js';\nimport {capacityAssignments, toCapacityAssignment} from './schema/capacity-assignments.js';\nimport {reservations} from './schema/reservations.js';\nimport {providerRunners} from './schema/runner-instances.js';\n\nexport async function assignCapacityBatch(params: {\n provisionerId: string;\n reservationId: string;\n capacityIds: string[];\n}): Promise<CapacityAssignment[]> {\n return await db().transaction(async (tx) => {\n const lockKey = [\n 'runners_capacity_assignments',\n params.provisionerId,\n params.reservationId,\n ].join(':');\n await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${lockKey}))`);\n for (const capacityId of [...params.capacityIds].sort()) {\n await tx.execute(\n sql`select pg_advisory_xact_lock(hashtext(${['runners_capacity_assignments', capacityId].join(':')}))`,\n );\n }\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 if (!reservation) throw new ReservationNotFoundError(params.reservationId);\n if (reservation.expiresAt <= new Date())\n throw new ReservationExpiredError(params.reservationId);\n\n const existing = await tx\n .select()\n .from(capacityAssignments)\n .where(eq(capacityAssignments.reservationId, reservation.id));\n const existingByCapacityId = new Map(\n existing.map((assignment) => [assignment.capacityId, assignment]),\n );\n const newCapacityIds = params.capacityIds.filter(\n (capacityId) => !existingByCapacityId.has(capacityId),\n );\n if (existing.length + newCapacityIds.length > reservation.count)\n throw new CapacityNotAssignableError(params.capacityIds[0] ?? '');\n\n if (newCapacityIds.length === 0) {\n return params.capacityIds.flatMap((capacityId) => {\n const assignment = existingByCapacityId.get(capacityId);\n return assignment ? [toCapacityAssignment(assignment)] : [];\n });\n }\n\n const capacities = await tx\n .select()\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.provisionerId, params.provisionerId),\n inArray(providerRunners.id, newCapacityIds),\n ),\n );\n if (capacities.length !== newCapacityIds.length)\n throw new CapacityNotAssignableError(params.capacityIds[0] ?? '');\n for (const capacity of capacities) {\n if (capacity.state !== 'starting' && capacity.state !== 'running')\n throw new CapacityNotAssignableError(capacity.id);\n if (\n !capacity.providerRunnerId ||\n capacity.labels.length === 0 ||\n !reservation.requiredLabels.every((label) => capacity.labels.includes(label))\n ) {\n throw new CapacityNotAssignableError(capacity.id);\n }\n }\n\n const assignedCapacity = await tx\n .select({capacityId: capacityAssignments.capacityId})\n .from(capacityAssignments)\n .where(inArray(capacityAssignments.capacityId, newCapacityIds));\n if (assignedCapacity.length > 0)\n throw new CapacityAlreadyAssignedError(assignedCapacity[0]?.capacityId ?? '');\n\n const inserted = await tx\n .insert(capacityAssignments)\n .values(\n newCapacityIds.map((capacityId) => ({\n capacityId,\n reservationId: reservation.id,\n workspaceId: reservation.workspaceId,\n provisionerId: params.provisionerId,\n })),\n )\n .returning();\n const insertedByCapacityId = new Map(\n inserted.map((assignment) => [assignment.capacityId, assignment]),\n );\n return params.capacityIds.flatMap((capacityId) => {\n const assignment =\n existingByCapacityId.get(capacityId) ?? insertedByCapacityId.get(capacityId);\n return assignment ? [toCapacityAssignment(assignment)] : [];\n });\n });\n}\n"],"names":["and","eq","inArray","sql","CapacityAlreadyAssignedError","CapacityNotAssignableError","ReservationExpiredError","ReservationNotFoundError","db","capacityAssignments","toCapacityAssignment","reservations","providerRunners","assignCapacityBatch","params","transaction","tx","lockKey","provisionerId","reservationId","join","execute","capacityId","capacityIds","sort","reservation","select","from","where","id","limit","expiresAt","Date","existing","existingByCapacityId","Map","map","assignment","newCapacityIds","filter","has","length","count","flatMap","get","capacities","capacity","state","providerRunnerId","labels","requiredLabels","every","label","includes","assignedCapacity","inserted","insert","values","workspaceId","returning","insertedByCapacityId"],"mappings":"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"}