@shipfox/api-runners 4.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/CHANGELOG.md +83 -0
  3. package/dist/config.d.ts +5 -1
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +53 -6
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/capacity-assignments.d.ts +7 -0
  8. package/dist/core/capacity-assignments.d.ts.map +1 -0
  9. package/dist/core/capacity-assignments.js +6 -0
  10. package/dist/core/capacity-assignments.js.map +1 -0
  11. package/dist/core/demand.d.ts +6 -6
  12. package/dist/core/demand.d.ts.map +1 -1
  13. package/dist/core/demand.js +10 -10
  14. package/dist/core/demand.js.map +1 -1
  15. package/dist/core/entities/capacity-assignment.d.ts +9 -0
  16. package/dist/core/entities/capacity-assignment.d.ts.map +1 -0
  17. package/dist/core/entities/capacity-assignment.js +3 -0
  18. package/dist/core/entities/capacity-assignment.js.map +1 -0
  19. package/dist/core/entities/ephemeral-registration-token.d.ts +1 -1
  20. package/dist/core/entities/ephemeral-registration-token.d.ts.map +1 -1
  21. package/dist/core/entities/ephemeral-registration-token.js.map +1 -1
  22. package/dist/core/entities/provisioner-capability-snapshot.d.ts +13 -0
  23. package/dist/core/entities/provisioner-capability-snapshot.d.ts.map +1 -0
  24. package/dist/core/entities/provisioner-capability-snapshot.js +3 -0
  25. package/dist/core/entities/provisioner-capability-snapshot.js.map +1 -0
  26. package/dist/core/entities/provisioner-token.d.ts +10 -2
  27. package/dist/core/entities/provisioner-token.d.ts.map +1 -1
  28. package/dist/core/entities/provisioner-token.js.map +1 -1
  29. package/dist/core/entities/{provisioned-runner.d.ts → runner-instance.d.ts} +6 -6
  30. package/dist/core/entities/runner-instance.d.ts.map +1 -0
  31. package/dist/core/entities/runner-instance.js +3 -0
  32. package/dist/core/entities/runner-instance.js.map +1 -0
  33. package/dist/core/entities/runner-session.d.ts +4 -2
  34. package/dist/core/entities/runner-session.d.ts.map +1 -1
  35. package/dist/core/entities/runner-session.js.map +1 -1
  36. package/dist/core/ephemeral-registration-tokens.d.ts +5 -5
  37. package/dist/core/ephemeral-registration-tokens.d.ts.map +1 -1
  38. package/dist/core/ephemeral-registration-tokens.js +12 -12
  39. package/dist/core/ephemeral-registration-tokens.js.map +1 -1
  40. package/dist/core/errors.d.ts +16 -4
  41. package/dist/core/errors.d.ts.map +1 -1
  42. package/dist/core/errors.js +22 -4
  43. package/dist/core/errors.js.map +1 -1
  44. package/dist/core/index.d.ts +10 -4
  45. package/dist/core/index.d.ts.map +1 -1
  46. package/dist/core/index.js +6 -2
  47. package/dist/core/index.js.map +1 -1
  48. package/dist/core/job-executions.d.ts +2 -0
  49. package/dist/core/job-executions.d.ts.map +1 -1
  50. package/dist/core/job-executions.js +1 -2
  51. package/dist/core/job-executions.js.map +1 -1
  52. package/dist/core/maintenance.d.ts +1 -1
  53. package/dist/core/maintenance.d.ts.map +1 -1
  54. package/dist/core/maintenance.js +5 -5
  55. package/dist/core/maintenance.js.map +1 -1
  56. package/dist/core/provisioner-capability-snapshots.d.ts +5 -0
  57. package/dist/core/provisioner-capability-snapshots.d.ts.map +1 -0
  58. package/dist/core/provisioner-capability-snapshots.js +10 -0
  59. package/dist/core/provisioner-capability-snapshots.js.map +1 -0
  60. package/dist/core/provisioner-tokens.d.ts +10 -0
  61. package/dist/core/provisioner-tokens.d.ts.map +1 -1
  62. package/dist/core/provisioner-tokens.js +23 -1
  63. package/dist/core/provisioner-tokens.js.map +1 -1
  64. package/dist/core/rate-limit.d.ts.map +1 -1
  65. package/dist/core/rate-limit.js +2 -7
  66. package/dist/core/rate-limit.js.map +1 -1
  67. package/dist/core/runner-activation.d.ts +13 -0
  68. package/dist/core/runner-activation.d.ts.map +1 -0
  69. package/dist/core/runner-activation.js +36 -0
  70. package/dist/core/runner-activation.js.map +1 -0
  71. package/dist/core/runner-control-sessions.d.ts +46 -0
  72. package/dist/core/runner-control-sessions.d.ts.map +1 -0
  73. package/dist/core/runner-control-sessions.js +121 -0
  74. package/dist/core/runner-control-sessions.js.map +1 -0
  75. package/dist/core/runner-instances.d.ts +79 -0
  76. package/dist/core/runner-instances.d.ts.map +1 -0
  77. package/dist/core/runner-instances.js +168 -0
  78. package/dist/core/runner-instances.js.map +1 -0
  79. package/dist/core/runner-sessions.d.ts +8 -2
  80. package/dist/core/runner-sessions.d.ts.map +1 -1
  81. package/dist/core/runner-sessions.js +8 -5
  82. package/dist/core/runner-sessions.js.map +1 -1
  83. package/dist/db/capacity-assignments.d.ts +7 -0
  84. package/dist/db/capacity-assignments.d.ts.map +1 -0
  85. package/dist/db/capacity-assignments.js +72 -0
  86. package/dist/db/capacity-assignments.js.map +1 -0
  87. package/dist/db/db.d.ts +2837 -1009
  88. package/dist/db/db.d.ts.map +1 -1
  89. package/dist/db/db.js +11 -2
  90. package/dist/db/db.js.map +1 -1
  91. package/dist/db/ephemeral-registration-tokens.d.ts +2 -2
  92. package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -1
  93. package/dist/db/ephemeral-registration-tokens.js +21 -21
  94. package/dist/db/ephemeral-registration-tokens.js.map +1 -1
  95. package/dist/db/index.d.ts +7 -4
  96. package/dist/db/index.d.ts.map +1 -1
  97. package/dist/db/index.js +5 -2
  98. package/dist/db/index.js.map +1 -1
  99. package/dist/db/job-executions.d.ts +6 -6
  100. package/dist/db/job-executions.d.ts.map +1 -1
  101. package/dist/db/job-executions.js +20 -19
  102. package/dist/db/job-executions.js.map +1 -1
  103. package/dist/db/provisioner-capability-snapshots.d.ts +21 -0
  104. package/dist/db/provisioner-capability-snapshots.d.ts.map +1 -0
  105. package/dist/db/provisioner-capability-snapshots.js +57 -0
  106. package/dist/db/provisioner-capability-snapshots.js.map +1 -0
  107. package/dist/db/provisioner-tokens.d.ts +7 -2
  108. package/dist/db/provisioner-tokens.d.ts.map +1 -1
  109. package/dist/db/provisioner-tokens.js +59 -10
  110. package/dist/db/provisioner-tokens.js.map +1 -1
  111. package/dist/db/reservations.d.ts +18 -0
  112. package/dist/db/reservations.d.ts.map +1 -1
  113. package/dist/db/reservations.js +88 -1
  114. package/dist/db/reservations.js.map +1 -1
  115. package/dist/db/runner-instances.d.ts +91 -0
  116. package/dist/db/runner-instances.d.ts.map +1 -0
  117. package/dist/db/runner-instances.js +511 -0
  118. package/dist/db/runner-instances.js.map +1 -0
  119. package/dist/db/runner-sessions.d.ts +5 -0
  120. package/dist/db/runner-sessions.d.ts.map +1 -1
  121. package/dist/db/runner-sessions.js +48 -2
  122. package/dist/db/runner-sessions.js.map +1 -1
  123. package/dist/db/schema/capacity-assignments.d.ts +113 -0
  124. package/dist/db/schema/capacity-assignments.d.ts.map +1 -0
  125. package/dist/db/schema/capacity-assignments.js +29 -0
  126. package/dist/db/schema/capacity-assignments.js.map +1 -0
  127. package/dist/db/schema/ephemeral-registration-tokens.d.ts +2 -2
  128. package/dist/db/schema/ephemeral-registration-tokens.js +3 -3
  129. package/dist/db/schema/ephemeral-registration-tokens.js.map +1 -1
  130. package/dist/db/schema/provisioner-capability-snapshots.d.ts +207 -0
  131. package/dist/db/schema/provisioner-capability-snapshots.d.ts.map +1 -0
  132. package/dist/db/schema/provisioner-capability-snapshots.js +38 -0
  133. package/dist/db/schema/provisioner-capability-snapshots.js.map +1 -0
  134. package/dist/db/schema/provisioner-tokens.d.ts +19 -1
  135. package/dist/db/schema/provisioner-tokens.d.ts.map +1 -1
  136. package/dist/db/schema/provisioner-tokens.js +29 -3
  137. package/dist/db/schema/provisioner-tokens.js.map +1 -1
  138. package/dist/db/schema/runner-activation-tokens.d.ts +161 -0
  139. package/dist/db/schema/runner-activation-tokens.d.ts.map +1 -0
  140. package/dist/db/schema/runner-activation-tokens.js +27 -0
  141. package/dist/db/schema/runner-activation-tokens.js.map +1 -0
  142. package/dist/db/schema/runner-control-sessions.d.ts +338 -0
  143. package/dist/db/schema/runner-control-sessions.d.ts.map +1 -0
  144. package/dist/db/schema/runner-control-sessions.js +52 -0
  145. package/dist/db/schema/runner-control-sessions.js.map +1 -0
  146. package/dist/db/schema/{provisioned-runners.d.ts → runner-instances.d.ts} +88 -34
  147. package/dist/db/schema/runner-instances.d.ts.map +1 -0
  148. package/dist/db/schema/{provisioned-runners.js → runner-instances.js} +19 -14
  149. package/dist/db/schema/runner-instances.js.map +1 -0
  150. package/dist/db/schema/runner-sessions.d.ts +39 -5
  151. package/dist/db/schema/runner-sessions.d.ts.map +1 -1
  152. package/dist/db/schema/runner-sessions.js +13 -6
  153. package/dist/db/schema/runner-sessions.js.map +1 -1
  154. package/dist/db/schema/running-job-executions.d.ts +2 -2
  155. package/dist/db/schema/running-job-executions.js +4 -4
  156. package/dist/db/schema/running-job-executions.js.map +1 -1
  157. package/dist/index.d.ts +7 -2
  158. package/dist/index.d.ts.map +1 -1
  159. package/dist/index.js +45 -38
  160. package/dist/index.js.map +1 -1
  161. package/dist/installation-provisioning.d.ts +14 -0
  162. package/dist/installation-provisioning.d.ts.map +1 -0
  163. package/dist/installation-provisioning.js +7 -0
  164. package/dist/installation-provisioning.js.map +1 -0
  165. package/dist/metrics/index.d.ts +1 -1
  166. package/dist/metrics/index.d.ts.map +1 -1
  167. package/dist/metrics/index.js +1 -1
  168. package/dist/metrics/index.js.map +1 -1
  169. package/dist/metrics/instance.d.ts +11 -7
  170. package/dist/metrics/instance.d.ts.map +1 -1
  171. package/dist/metrics/instance.js +13 -7
  172. package/dist/metrics/instance.js.map +1 -1
  173. package/dist/presentation/auth/index.d.ts +1 -0
  174. package/dist/presentation/auth/index.d.ts.map +1 -1
  175. package/dist/presentation/auth/index.js +1 -0
  176. package/dist/presentation/auth/index.js.map +1 -1
  177. package/dist/presentation/auth/provisioner-token-auth.d.ts.map +1 -1
  178. package/dist/presentation/auth/provisioner-token-auth.js +5 -1
  179. package/dist/presentation/auth/provisioner-token-auth.js.map +1 -1
  180. package/dist/presentation/auth/runner-control-session-auth.d.ts +4 -0
  181. package/dist/presentation/auth/runner-control-session-auth.d.ts.map +1 -0
  182. package/dist/presentation/auth/runner-control-session-auth.js +29 -0
  183. package/dist/presentation/auth/runner-control-session-auth.js.map +1 -0
  184. package/dist/presentation/auth/runner-registration-token-auth.d.ts +5 -1
  185. package/dist/presentation/auth/runner-registration-token-auth.d.ts.map +1 -1
  186. package/dist/presentation/auth/runner-registration-token-auth.js +21 -1
  187. package/dist/presentation/auth/runner-registration-token-auth.js.map +1 -1
  188. package/dist/presentation/dto/index.d.ts +2 -2
  189. package/dist/presentation/dto/index.d.ts.map +1 -1
  190. package/dist/presentation/dto/index.js +2 -2
  191. package/dist/presentation/dto/index.js.map +1 -1
  192. package/dist/presentation/dto/mint-registration-tokens.d.ts +5 -3
  193. package/dist/presentation/dto/mint-registration-tokens.d.ts.map +1 -1
  194. package/dist/presentation/dto/mint-registration-tokens.js +4 -4
  195. package/dist/presentation/dto/mint-registration-tokens.js.map +1 -1
  196. package/dist/presentation/dto/poll-demand.d.ts.map +1 -1
  197. package/dist/presentation/dto/poll-demand.js +7 -1
  198. package/dist/presentation/dto/poll-demand.js.map +1 -1
  199. package/dist/presentation/dto/provisioner-token.d.ts +1 -0
  200. package/dist/presentation/dto/provisioner-token.d.ts.map +1 -1
  201. package/dist/presentation/dto/provisioner-token.js +5 -1
  202. package/dist/presentation/dto/provisioner-token.js.map +1 -1
  203. package/dist/presentation/dto/{provisioned-runners.d.ts → runner-instances.d.ts} +9 -9
  204. package/dist/presentation/dto/runner-instances.d.ts.map +1 -0
  205. package/dist/presentation/dto/{provisioned-runners.js → runner-instances.js} +6 -6
  206. package/dist/presentation/dto/runner-instances.js.map +1 -0
  207. package/dist/presentation/index.d.ts +2 -2
  208. package/dist/presentation/index.d.ts.map +1 -1
  209. package/dist/presentation/index.js +2 -2
  210. package/dist/presentation/index.js.map +1 -1
  211. package/dist/presentation/inter-module.d.ts +5 -0
  212. package/dist/presentation/inter-module.d.ts.map +1 -0
  213. package/dist/presentation/inter-module.js +43 -0
  214. package/dist/presentation/inter-module.js.map +1 -0
  215. package/dist/presentation/routes/assign-capacity.d.ts +2 -0
  216. package/dist/presentation/routes/assign-capacity.d.ts.map +1 -0
  217. package/dist/presentation/routes/assign-capacity.js +44 -0
  218. package/dist/presentation/routes/assign-capacity.js.map +1 -0
  219. package/dist/presentation/routes/get-provisioner-me.d.ts.map +1 -1
  220. package/dist/presentation/routes/get-provisioner-me.js +5 -1
  221. package/dist/presentation/routes/get-provisioner-me.js.map +1 -1
  222. package/dist/presentation/routes/heartbeat.d.ts +2 -1
  223. package/dist/presentation/routes/heartbeat.d.ts.map +1 -1
  224. package/dist/presentation/routes/heartbeat.js +60 -50
  225. package/dist/presentation/routes/heartbeat.js.map +1 -1
  226. package/dist/presentation/routes/index.d.ts +3 -1
  227. package/dist/presentation/routes/index.d.ts.map +1 -1
  228. package/dist/presentation/routes/index.js +83 -50
  229. package/dist/presentation/routes/index.js.map +1 -1
  230. package/dist/presentation/routes/mint-registration-tokens.d.ts.map +1 -1
  231. package/dist/presentation/routes/mint-registration-tokens.js +5 -5
  232. package/dist/presentation/routes/mint-registration-tokens.js.map +1 -1
  233. package/dist/presentation/routes/planned-capacity.d.ts +3 -0
  234. package/dist/presentation/routes/planned-capacity.d.ts.map +1 -0
  235. package/dist/presentation/routes/planned-capacity.js +54 -0
  236. package/dist/presentation/routes/planned-capacity.js.map +1 -0
  237. package/dist/presentation/routes/poll-demand.d.ts +2 -0
  238. package/dist/presentation/routes/poll-demand.d.ts.map +1 -1
  239. package/dist/presentation/routes/poll-demand.js +86 -45
  240. package/dist/presentation/routes/poll-demand.js.map +1 -1
  241. package/dist/presentation/routes/reconcile-runner-instances.d.ts +2 -0
  242. package/dist/presentation/routes/reconcile-runner-instances.d.ts.map +1 -0
  243. package/dist/presentation/routes/reconcile-runner-instances.js +27 -0
  244. package/dist/presentation/routes/reconcile-runner-instances.js.map +1 -0
  245. package/dist/presentation/routes/register.d.ts +2 -1
  246. package/dist/presentation/routes/register.d.ts.map +1 -1
  247. package/dist/presentation/routes/register.js +55 -52
  248. package/dist/presentation/routes/register.js.map +1 -1
  249. package/dist/presentation/routes/report-runner-instances.d.ts +2 -0
  250. package/dist/presentation/routes/report-runner-instances.d.ts.map +1 -0
  251. package/dist/presentation/routes/report-runner-instances.js +37 -0
  252. package/dist/presentation/routes/report-runner-instances.js.map +1 -0
  253. package/dist/presentation/routes/request-job.d.ts +2 -1
  254. package/dist/presentation/routes/request-job.d.ts.map +1 -1
  255. package/dist/presentation/routes/request-job.js +39 -36
  256. package/dist/presentation/routes/request-job.js.map +1 -1
  257. package/dist/presentation/routes/runner-enrollment.d.ts +7 -0
  258. package/dist/presentation/routes/runner-enrollment.d.ts.map +1 -0
  259. package/dist/presentation/routes/runner-enrollment.js +185 -0
  260. package/dist/presentation/routes/runner-enrollment.js.map +1 -0
  261. package/dist/temporal/activities/index.d.ts +2 -2
  262. package/dist/temporal/activities/index.d.ts.map +1 -1
  263. package/dist/temporal/activities/index.js +2 -2
  264. package/dist/temporal/activities/index.js.map +1 -1
  265. package/dist/temporal/activities/maintenance-activities.d.ts +1 -1
  266. package/dist/temporal/activities/maintenance-activities.d.ts.map +1 -1
  267. package/dist/temporal/activities/maintenance-activities.js +3 -3
  268. package/dist/temporal/activities/maintenance-activities.js.map +1 -1
  269. package/dist/temporal/workflows/index.bundle.js +3 -3
  270. package/dist/temporal/workflows/stuck-job-detector.js +2 -2
  271. package/dist/temporal/workflows/stuck-job-detector.js.map +1 -1
  272. package/dist/tsconfig.test.tsbuildinfo +1 -1
  273. package/drizzle/0000_initial.sql +102 -24
  274. package/drizzle/meta/0000_snapshot.json +483 -63
  275. package/package.json +31 -36
  276. package/src/config.test.ts +0 -26
  277. package/src/config.ts +47 -6
  278. package/src/core/capacity-assignments.ts +10 -0
  279. package/src/core/demand.test.ts +29 -29
  280. package/src/core/demand.ts +22 -22
  281. package/src/core/entities/capacity-assignment.ts +8 -0
  282. package/src/core/entities/ephemeral-registration-token.ts +1 -1
  283. package/src/core/entities/provisioner-capability-snapshot.ts +12 -0
  284. package/src/core/entities/provisioner-token.ts +8 -2
  285. package/src/core/entities/{provisioned-runner.ts → runner-instance.ts} +5 -5
  286. package/src/core/entities/runner-session.ts +4 -2
  287. package/src/core/ephemeral-registration-tokens.test.ts +11 -11
  288. package/src/core/ephemeral-registration-tokens.ts +18 -20
  289. package/src/core/errors.ts +23 -4
  290. package/src/core/index.ts +39 -17
  291. package/src/core/job-executions.ts +3 -2
  292. package/src/core/maintenance.test.ts +13 -17
  293. package/src/core/maintenance.ts +5 -5
  294. package/src/core/provisioner-capability-snapshots.test.ts +35 -0
  295. package/src/core/provisioner-capability-snapshots.ts +12 -0
  296. package/src/core/provisioner-tokens.test.ts +20 -0
  297. package/src/core/provisioner-tokens.ts +33 -0
  298. package/src/core/rate-limit.test.ts +11 -27
  299. package/src/core/rate-limit.ts +3 -11
  300. package/src/core/runner-activation.ts +72 -0
  301. package/src/core/runner-control-sessions.ts +190 -0
  302. package/src/core/{provisioned-runners.test.ts → runner-instances.test.ts} +24 -24
  303. package/src/core/runner-instances.ts +310 -0
  304. package/src/core/runner-sessions.test.ts +112 -22
  305. package/src/core/runner-sessions.ts +25 -14
  306. package/src/db/capacity-assignments.test.ts +159 -0
  307. package/src/db/capacity-assignments.ts +116 -0
  308. package/src/db/db.ts +11 -2
  309. package/src/db/ephemeral-registration-tokens.test.ts +9 -9
  310. package/src/db/ephemeral-registration-tokens.ts +26 -26
  311. package/src/db/index.ts +31 -21
  312. package/src/db/job-executions.test.ts +13 -11
  313. package/src/db/job-executions.ts +33 -27
  314. package/src/db/provisioner-capability-snapshots.test.ts +175 -0
  315. package/src/db/provisioner-capability-snapshots.ts +130 -0
  316. package/src/db/provisioner-tokens.test.ts +139 -2
  317. package/src/db/provisioner-tokens.ts +123 -15
  318. package/src/db/reservations.test.ts +49 -0
  319. package/src/db/reservations.ts +146 -1
  320. package/src/db/{provisioned-runners.test.ts → runner-instances.test.ts} +429 -353
  321. package/src/db/runner-instances.ts +942 -0
  322. package/src/db/runner-sessions.test.ts +19 -6
  323. package/src/db/runner-sessions.ts +78 -2
  324. package/src/db/schema/capacity-assignments.ts +34 -0
  325. package/src/db/schema/ephemeral-registration-tokens.ts +4 -4
  326. package/src/db/schema/provisioner-capability-snapshots.ts +46 -0
  327. package/src/db/schema/provisioner-tokens.ts +33 -2
  328. package/src/db/schema/runner-activation-tokens.ts +22 -0
  329. package/src/db/schema/runner-control-sessions.ts +46 -0
  330. package/src/db/schema/{provisioned-runners.ts → runner-instances.ts} +26 -27
  331. package/src/db/schema/runner-sessions.ts +11 -7
  332. package/src/db/schema/running-job-executions.ts +5 -5
  333. package/src/index.test.ts +26 -0
  334. package/src/index.ts +39 -19
  335. package/src/installation-provisioning.ts +14 -0
  336. package/src/metrics/index.ts +5 -5
  337. package/src/metrics/instance.ts +25 -14
  338. package/src/presentation/auth/index.ts +4 -0
  339. package/src/presentation/auth/provisioner-token-auth.ts +8 -4
  340. package/src/presentation/auth/runner-control-session-auth.ts +31 -0
  341. package/src/presentation/auth/runner-registration-token-auth.ts +39 -3
  342. package/src/presentation/dto/index.ts +5 -5
  343. package/src/presentation/dto/mint-registration-tokens.ts +8 -15
  344. package/src/presentation/dto/poll-demand.ts +3 -1
  345. package/src/presentation/dto/provisioner-token.ts +5 -0
  346. package/src/presentation/dto/{provisioned-runners.ts → runner-instances.ts} +13 -15
  347. package/src/presentation/index.ts +2 -1
  348. package/src/presentation/inter-module.test.ts +15 -0
  349. package/src/presentation/inter-module.ts +53 -0
  350. package/src/presentation/routes/assign-capacity.ts +48 -0
  351. package/src/presentation/routes/get-provisioner-me.ts +3 -4
  352. package/src/presentation/routes/heartbeat.test.ts +4 -3
  353. package/src/presentation/routes/heartbeat.ts +67 -64
  354. package/src/presentation/routes/index.ts +93 -43
  355. package/src/presentation/routes/list-active-runners.test.ts +23 -23
  356. package/src/presentation/routes/manual-registration-tokens.test.ts +4 -3
  357. package/src/presentation/routes/mint-registration-tokens.test.ts +12 -12
  358. package/src/presentation/routes/mint-registration-tokens.ts +5 -7
  359. package/src/presentation/routes/planned-capacity.ts +49 -0
  360. package/src/presentation/routes/poll-demand-release.test.ts +4 -4
  361. package/src/presentation/routes/poll-demand.test.ts +161 -22
  362. package/src/presentation/routes/poll-demand.ts +105 -45
  363. package/src/presentation/routes/provisioner-me.test.ts +37 -3
  364. package/src/presentation/routes/{reconcile-provisioned-runners.test.ts → reconcile-runner-instances.test.ts} +66 -61
  365. package/src/presentation/routes/reconcile-runner-instances.ts +30 -0
  366. package/src/presentation/routes/register.test.ts +9 -5
  367. package/src/presentation/routes/register.ts +61 -57
  368. package/src/presentation/routes/{report-provisioned-runners.test.ts → report-runner-instances.test.ts} +28 -28
  369. package/src/presentation/routes/{report-provisioned-runners.ts → report-runner-instances.ts} +14 -14
  370. package/src/presentation/routes/request-job.test.ts +5 -3
  371. package/src/presentation/routes/request-job.ts +41 -37
  372. package/src/presentation/routes/runner-enrollment.test.ts +259 -0
  373. package/src/presentation/routes/runner-enrollment.ts +174 -0
  374. package/src/temporal/activities/index.ts +2 -2
  375. package/src/temporal/activities/maintenance-activities.test.ts +7 -7
  376. package/src/temporal/activities/maintenance-activities.ts +3 -3
  377. package/src/temporal/workflows/stuck-job-detector.test.ts +3 -3
  378. package/src/temporal/workflows/stuck-job-detector.ts +2 -2
  379. package/test/env.ts +1 -3
  380. package/test/factories/ephemeral-registration-token.ts +2 -2
  381. package/test/factories/provisioner-token.ts +3 -1
  382. package/test/factories/runner-instance.ts +57 -0
  383. package/test/factories/runner-session.ts +3 -1
  384. package/test/fixtures/auth-inter-module.ts +30 -0
  385. package/test/globalSetup.ts +5 -1
  386. package/test/index.ts +2 -1
  387. package/tsconfig.build.tsbuildinfo +1 -1
  388. package/dist/core/entities/provisioned-runner.d.ts.map +0 -1
  389. package/dist/core/entities/provisioned-runner.js +0 -3
  390. package/dist/core/entities/provisioned-runner.js.map +0 -1
  391. package/dist/core/provisioned-runners.d.ts +0 -67
  392. package/dist/core/provisioned-runners.d.ts.map +0 -1
  393. package/dist/core/provisioned-runners.js +0 -162
  394. package/dist/core/provisioned-runners.js.map +0 -1
  395. package/dist/db/provisioned-runners.d.ts +0 -79
  396. package/dist/db/provisioned-runners.d.ts.map +0 -1
  397. package/dist/db/provisioned-runners.js +0 -466
  398. package/dist/db/provisioned-runners.js.map +0 -1
  399. package/dist/db/schema/provisioned-runners.d.ts.map +0 -1
  400. package/dist/db/schema/provisioned-runners.js.map +0 -1
  401. package/dist/presentation/dto/provisioned-runners.d.ts.map +0 -1
  402. package/dist/presentation/dto/provisioned-runners.js.map +0 -1
  403. package/dist/presentation/routes/reconcile-provisioned-runners.d.ts +0 -2
  404. package/dist/presentation/routes/reconcile-provisioned-runners.d.ts.map +0 -1
  405. package/dist/presentation/routes/reconcile-provisioned-runners.js +0 -27
  406. package/dist/presentation/routes/reconcile-provisioned-runners.js.map +0 -1
  407. package/dist/presentation/routes/report-provisioned-runners.d.ts +0 -2
  408. package/dist/presentation/routes/report-provisioned-runners.d.ts.map +0 -1
  409. package/dist/presentation/routes/report-provisioned-runners.js +0 -37
  410. package/dist/presentation/routes/report-provisioned-runners.js.map +0 -1
  411. package/src/core/provisioned-runners.ts +0 -301
  412. package/src/db/provisioned-runners.ts +0 -886
  413. package/src/presentation/routes/reconcile-provisioned-runners.ts +0 -30
  414. package/test/factories/provisioned-runner.ts +0 -78
@@ -41,6 +41,19 @@ describe('deleteExpiredRunnerSessions', () => {
41
41
  expect(remaining).toEqual([active]);
42
42
  });
43
43
 
44
+ it('deletes activation sessions using the ephemeral retention window', async () => {
45
+ const expired = await insertRunnerSession({kind: 'activation', createdDaysAgo: 8});
46
+ const active = await insertRunnerSession({kind: 'activation', createdDaysAgo: 6});
47
+
48
+ await deleteExpiredRunnerSessions({
49
+ manualRetentionDays: 30,
50
+ ephemeralRetentionDays: 7,
51
+ limit: 100,
52
+ });
53
+
54
+ expect(await listRunnerSessionIds([expired, active])).toEqual([active]);
55
+ });
56
+
44
57
  it('applies the retention window for each registration token kind independently', async () => {
45
58
  const manualExpired = await insertRunnerSession({kind: 'manual', createdDaysAgo: 31});
46
59
  const manualActive = await insertRunnerSession({kind: 'manual', createdDaysAgo: 8});
@@ -94,15 +107,14 @@ describe('deleteExpiredRunnerSessions', () => {
94
107
  });
95
108
 
96
109
  async function insertRunnerSession(params: {
97
- kind: 'manual' | 'ephemeral';
110
+ kind: 'manual' | 'ephemeral' | 'activation';
98
111
  createdDaysAgo: number;
99
112
  }): Promise<string> {
100
113
  const id = crypto.randomUUID();
101
114
  const registrationTokenId = crypto.randomUUID();
102
115
  const createdAt = new Date(Date.now() - params.createdDaysAgo * 24 * 60 * 60 * 1000);
103
- const provisionerId = params.kind === 'ephemeral' ? crypto.randomUUID() : null;
104
- const provisionedRunnerId =
105
- params.kind === 'ephemeral' ? `provisioned-${crypto.randomUUID()}` : null;
116
+ const provisionerId = params.kind === 'manual' ? null : crypto.randomUUID();
117
+ const providerRunnerId = params.kind === 'manual' ? null : `provisioned-${crypto.randomUUID()}`;
106
118
 
107
119
  await db()
108
120
  .insert(runnerSessions)
@@ -112,10 +124,11 @@ describe('deleteExpiredRunnerSessions', () => {
112
124
  scope: 'workspace',
113
125
  registrationTokenId,
114
126
  registrationTokenKind: params.kind,
127
+ runnerInstanceId: params.kind === 'activation' ? crypto.randomUUID() : null,
115
128
  provisionerId,
116
- provisionedRunnerId,
129
+ providerRunnerId,
117
130
  labels: ['linux'],
118
- maxClaims: params.kind === 'ephemeral' ? 1 : null,
131
+ maxClaims: params.kind === 'manual' ? null : 1,
119
132
  claimsUsed: 0,
120
133
  createdAt,
121
134
  updatedAt: createdAt,
@@ -1,7 +1,10 @@
1
1
  import type {RunnerToolCapabilitiesDto} from '@shipfox/api-runners-dto';
2
- import {and, asc, eq, inArray, lt, notExists, or, sql} from 'drizzle-orm';
2
+ import {and, asc, eq, gt, inArray, isNull, lt, notExists, or, sql} from 'drizzle-orm';
3
3
  import type {RunnerSession} from '#core/entities/runner-session.js';
4
4
  import {db} from './db.js';
5
+ import {runnerActivationTokens} from './schema/runner-activation-tokens.js';
6
+ import {runnerControlSessions} from './schema/runner-control-sessions.js';
7
+ import {providerRunners} from './schema/runner-instances.js';
5
8
  import {runnerSessions, toRunnerSession} from './schema/runner-sessions.js';
6
9
  import {runningJobExecutions} from './schema/running-job-executions.js';
7
10
 
@@ -47,6 +50,79 @@ export async function getRunnerSessionById(runnerSessionId: string): Promise<Run
47
50
  return row ? toRunnerSession(row) : null;
48
51
  }
49
52
 
53
+ export async function createRunnerSessionConsumingActivationToken(params: {
54
+ activationTokenId: string;
55
+ labels: string[];
56
+ toolCapabilities?: RunnerToolCapabilitiesDto | null;
57
+ }) {
58
+ return await db().transaction(async (tx) => {
59
+ const [token] = await tx
60
+ .select({
61
+ id: runnerActivationTokens.id,
62
+ runnerInstanceId: runnerActivationTokens.runnerInstanceId,
63
+ workspaceId: providerRunners.workspaceId,
64
+ provisionerId: providerRunners.provisionerId,
65
+ providerRunnerId: providerRunners.providerRunnerId,
66
+ })
67
+ .from(runnerActivationTokens)
68
+ .innerJoin(providerRunners, eq(providerRunners.id, runnerActivationTokens.runnerInstanceId))
69
+ .where(
70
+ and(
71
+ eq(runnerActivationTokens.id, params.activationTokenId),
72
+ isNull(runnerActivationTokens.consumedAt),
73
+ isNull(runnerActivationTokens.revokedAt),
74
+ gt(runnerActivationTokens.expiresAt, sql`now()`),
75
+ isNull(providerRunners.runnerSessionId),
76
+ ),
77
+ )
78
+ .limit(1)
79
+ .for('update');
80
+ if (!token?.workspaceId || !token.providerRunnerId)
81
+ throw new Error('Runner activation token is invalid, expired, or has already been used');
82
+ const [session] = await tx
83
+ .insert(runnerSessions)
84
+ .values({
85
+ workspaceId: token.workspaceId,
86
+ scope: 'workspace',
87
+ registrationTokenId: token.id,
88
+ registrationTokenKind: 'activation',
89
+ runnerInstanceId: token.runnerInstanceId,
90
+ provisionerId: token.provisionerId,
91
+ providerRunnerId: token.providerRunnerId,
92
+ labels: params.labels,
93
+ toolCapabilities: params.toolCapabilities ?? null,
94
+ toolCapabilitiesReportedAt: params.toolCapabilities ? sql`now()` : null,
95
+ maxClaims: 1,
96
+ claimsUsed: 0,
97
+ })
98
+ .returning();
99
+ if (!session) throw new Error('Runner activation session insert returned no row');
100
+ await tx
101
+ .update(runnerActivationTokens)
102
+ .set({consumedAt: sql`now()`, consumedSessionId: session.id})
103
+ .where(eq(runnerActivationTokens.id, token.id));
104
+ await tx
105
+ .update(providerRunners)
106
+ .set({runnerSessionId: session.id, updatedAt: sql`now()`})
107
+ .where(
108
+ and(
109
+ eq(providerRunners.id, token.runnerInstanceId),
110
+ isNull(providerRunners.runnerSessionId),
111
+ ),
112
+ );
113
+ await tx
114
+ .update(runnerControlSessions)
115
+ .set({closedAt: sql`now()`, closeReason: 'activated'})
116
+ .where(
117
+ and(
118
+ eq(runnerControlSessions.runnerInstanceId, token.runnerInstanceId),
119
+ isNull(runnerControlSessions.closedAt),
120
+ ),
121
+ );
122
+ return toRunnerSession(session);
123
+ });
124
+ }
125
+
50
126
  export interface DeleteExpiredRunnerSessionsParams {
51
127
  manualRetentionDays: number;
52
128
  ephemeralRetentionDays: number;
@@ -70,7 +146,7 @@ export async function deleteExpiredRunnerSessions(
70
146
  ),
71
147
  ),
72
148
  and(
73
- eq(runnerSessions.registrationTokenKind, 'ephemeral'),
149
+ inArray(runnerSessions.registrationTokenKind, ['ephemeral', 'activation']),
74
150
  lt(
75
151
  runnerSessions.createdAt,
76
152
  sql`now() - (${params.ephemeralRetentionDays} || ' days')::interval`,
@@ -0,0 +1,34 @@
1
+ import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';
2
+ import {index, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';
3
+ import type {CapacityAssignment} from '#core/entities/capacity-assignment.js';
4
+ import {pgTable} from './common.js';
5
+
6
+ export const capacityAssignments = pgTable(
7
+ 'capacity_assignments',
8
+ {
9
+ id: uuidv7PrimaryKey(),
10
+ capacityId: uuid('capacity_id').notNull(),
11
+ reservationId: uuid('reservation_id').notNull(),
12
+ workspaceId: uuid('workspace_id').notNull(),
13
+ provisionerId: uuid('provisioner_id').notNull(),
14
+ createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),
15
+ },
16
+ (table) => [
17
+ uniqueIndex('runners_capacity_assignments_capacity_unique').on(table.capacityId),
18
+ index('runners_capacity_assignments_reservation_idx').on(table.reservationId),
19
+ index('runners_capacity_assignments_workspace_idx').on(table.workspaceId),
20
+ ],
21
+ );
22
+
23
+ export type CapacityAssignmentDb = typeof capacityAssignments.$inferSelect;
24
+
25
+ export function toCapacityAssignment(row: CapacityAssignmentDb): CapacityAssignment {
26
+ return {
27
+ id: row.id,
28
+ capacityId: row.capacityId,
29
+ reservationId: row.reservationId,
30
+ workspaceId: row.workspaceId,
31
+ provisionerId: row.provisionerId,
32
+ createdAt: row.createdAt,
33
+ };
34
+ }
@@ -11,7 +11,7 @@ export const ephemeralRegistrationTokens = pgTable(
11
11
  workspaceId: uuid('workspace_id').notNull(),
12
12
  provisionerId: uuid('provisioner_id').notNull(),
13
13
  reservationId: uuid('reservation_id'),
14
- provisionedRunnerId: text('provisioned_runner_id').notNull(),
14
+ providerRunnerId: text('provider_runner_id').notNull(),
15
15
  hashedToken: text('hashed_token').notNull(),
16
16
  prefix: text('prefix').notNull(),
17
17
  expiresAt: timestamp('expires_at', {withTimezone: true}).notNull(),
@@ -22,10 +22,10 @@ export const ephemeralRegistrationTokens = pgTable(
22
22
  (table) => [
23
23
  uniqueIndex('runners_ephemeral_registration_tokens_hashed_token_unique').on(table.hashedToken),
24
24
  index('runners_ephemeral_registration_tokens_reservation_id_idx').on(table.reservationId),
25
- index('runners_ephemeral_registration_tokens_active_provisioned_runner_idx').on(
25
+ index('runners_ephemeral_registration_tokens_active_provider_runner_idx').on(
26
26
  table.workspaceId,
27
27
  table.provisionerId,
28
- table.provisionedRunnerId,
28
+ table.providerRunnerId,
29
29
  table.consumedAt,
30
30
  table.expiresAt,
31
31
  ),
@@ -47,7 +47,7 @@ export function toEphemeralRegistrationToken(
47
47
  workspaceId: row.workspaceId,
48
48
  provisionerId: row.provisionerId,
49
49
  reservationId: row.reservationId,
50
- provisionedRunnerId: row.provisionedRunnerId,
50
+ providerRunnerId: row.providerRunnerId,
51
51
  hashedToken: row.hashedToken,
52
52
  prefix: row.prefix,
53
53
  expiresAt: row.expiresAt,
@@ -0,0 +1,46 @@
1
+ import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';
2
+ import {index, integer, text, timestamp, uuid} from 'drizzle-orm/pg-core';
3
+ import type {ProvisionerCapabilitySnapshot} from '#core/entities/provisioner-capability-snapshot.js';
4
+ import {pgTable} from './common.js';
5
+
6
+ export const provisionerCapabilitySnapshots = pgTable(
7
+ 'provisioner_capability_snapshots',
8
+ {
9
+ id: uuidv7PrimaryKey(),
10
+ workspaceId: uuid('workspace_id').notNull(),
11
+ provisionerId: uuid('provisioner_id').notNull(),
12
+ templateKey: text('template_key').notNull(),
13
+ labels: text('labels').array().notNull(),
14
+ availableSlots: integer('available_slots').notNull(),
15
+ starting: integer('starting').notNull(),
16
+ running: integer('running').notNull(),
17
+ advertisedAt: timestamp('advertised_at', {withTimezone: true}).notNull().defaultNow(),
18
+ createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),
19
+ },
20
+ (table) => [
21
+ index('runners_provisioner_capability_snapshots_workspace_active_idx').on(
22
+ table.workspaceId,
23
+ table.advertisedAt,
24
+ ),
25
+ index('runners_provisioner_capability_snapshots_provisioner_idx').on(table.provisionerId),
26
+ ],
27
+ );
28
+
29
+ export type ProvisionerCapabilitySnapshotDb = typeof provisionerCapabilitySnapshots.$inferSelect;
30
+
31
+ export function toProvisionerCapabilitySnapshot(
32
+ row: ProvisionerCapabilitySnapshotDb,
33
+ ): ProvisionerCapabilitySnapshot {
34
+ return {
35
+ id: row.id,
36
+ workspaceId: row.workspaceId,
37
+ provisionerId: row.provisionerId,
38
+ templateKey: row.templateKey,
39
+ labels: row.labels,
40
+ availableSlots: row.availableSlots,
41
+ starting: row.starting,
42
+ running: row.running,
43
+ advertisedAt: row.advertisedAt,
44
+ createdAt: row.createdAt,
45
+ };
46
+ }
@@ -1,13 +1,20 @@
1
1
  import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';
2
- import {index, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';
2
+ import {sql} from 'drizzle-orm';
3
+ import {check, index, pgEnum, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';
3
4
  import type {ProvisionerToken} from '#core/entities/provisioner-token.js';
4
5
  import {pgTable} from './common.js';
5
6
 
7
+ export const provisionerScopeEnum = pgEnum('runners_provisioner_scope', [
8
+ 'workspace',
9
+ 'installation',
10
+ ]);
11
+
6
12
  export const provisionerTokens = pgTable(
7
13
  'provisioner_tokens',
8
14
  {
9
15
  id: uuidv7PrimaryKey(),
10
- workspaceId: uuid('workspace_id').notNull(),
16
+ scope: provisionerScopeEnum('scope').notNull(),
17
+ workspaceId: uuid('workspace_id'),
11
18
  hashedToken: text('hashed_token').notNull(),
12
19
  prefix: text('prefix').notNull(),
13
20
  name: text('name'),
@@ -26,6 +33,10 @@ export const provisionerTokens = pgTable(
26
33
  table.lastSeenAt.desc(),
27
34
  table.id.desc(),
28
35
  ),
36
+ check(
37
+ 'runners_provisioner_tokens_scope_workspace_ck',
38
+ sql`(scope = 'workspace' AND workspace_id IS NOT NULL) OR (scope = 'installation' AND workspace_id IS NULL)`,
39
+ ),
29
40
  ],
30
41
  );
31
42
 
@@ -33,8 +44,28 @@ export type ProvisionerTokenDb = typeof provisionerTokens.$inferSelect;
33
44
  export type ProvisionerTokenInsertDb = typeof provisionerTokens.$inferInsert;
34
45
 
35
46
  export function toProvisionerToken(row: ProvisionerTokenDb): ProvisionerToken {
47
+ if (row.scope === 'installation') {
48
+ return {
49
+ id: row.id,
50
+ scope: 'installation',
51
+ workspaceId: null,
52
+ hashedToken: row.hashedToken,
53
+ prefix: row.prefix,
54
+ name: row.name,
55
+ createdByUserId: row.createdByUserId,
56
+ revokedByUserId: row.revokedByUserId,
57
+ expiresAt: row.expiresAt,
58
+ revokedAt: row.revokedAt,
59
+ lastSeenAt: row.lastSeenAt,
60
+ createdAt: row.createdAt,
61
+ updatedAt: row.updatedAt,
62
+ };
63
+ }
64
+
65
+ if (!row.workspaceId) throw new Error('Workspace provisioner token has no workspace');
36
66
  return {
37
67
  id: row.id,
68
+ scope: 'workspace',
38
69
  workspaceId: row.workspaceId,
39
70
  hashedToken: row.hashedToken,
40
71
  prefix: row.prefix,
@@ -0,0 +1,22 @@
1
+ import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';
2
+ import {index, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';
3
+ import {pgTable} from './common.js';
4
+
5
+ export const runnerActivationTokens = pgTable(
6
+ 'runner_activation_tokens',
7
+ {
8
+ id: uuidv7PrimaryKey(),
9
+ runnerInstanceId: uuid('runner_instance_id').notNull(),
10
+ hashedToken: text('hashed_token').notNull(),
11
+ prefix: text('prefix').notNull(),
12
+ expiresAt: timestamp('expires_at', {withTimezone: true}).notNull(),
13
+ consumedAt: timestamp('consumed_at', {withTimezone: true}),
14
+ revokedAt: timestamp('revoked_at', {withTimezone: true}),
15
+ consumedSessionId: uuid('consumed_session_id'),
16
+ createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),
17
+ },
18
+ (table) => [
19
+ uniqueIndex('runners_runner_activation_tokens_hashed_token_unique').on(table.hashedToken),
20
+ index('runners_runner_activation_tokens_runner_instance_idx').on(table.runnerInstanceId),
21
+ ],
22
+ );
@@ -0,0 +1,46 @@
1
+ import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';
2
+ import {sql} from 'drizzle-orm';
3
+ import {index, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';
4
+ import {pgTable} from './common.js';
5
+
6
+ export const runnerBootstrapTokens = pgTable(
7
+ 'runner_bootstrap_tokens',
8
+ {
9
+ id: uuidv7PrimaryKey(),
10
+ runnerInstanceId: uuid('runner_instance_id').notNull(),
11
+ provisionerId: uuid('provisioner_id').notNull(),
12
+ hashedToken: text('hashed_token').notNull(),
13
+ prefix: text('prefix').notNull(),
14
+ expiresAt: timestamp('expires_at', {withTimezone: true}).notNull(),
15
+ consumedAt: timestamp('consumed_at', {withTimezone: true}),
16
+ revokedAt: timestamp('revoked_at', {withTimezone: true}),
17
+ createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),
18
+ },
19
+ (table) => [
20
+ uniqueIndex('runners_runner_bootstrap_tokens_hashed_token_unique').on(table.hashedToken),
21
+ index('runners_runner_bootstrap_tokens_runner_instance_idx').on(table.runnerInstanceId),
22
+ ],
23
+ );
24
+
25
+ export const runnerControlSessions = pgTable(
26
+ 'runner_control_sessions',
27
+ {
28
+ id: uuidv7PrimaryKey(),
29
+ runnerInstanceId: uuid('runner_instance_id').notNull(),
30
+ provisionerId: uuid('provisioner_id').notNull(),
31
+ hashedToken: text('hashed_token').notNull(),
32
+ prefix: text('prefix').notNull(),
33
+ expiresAt: timestamp('expires_at', {withTimezone: true}).notNull(),
34
+ lastSeenAt: timestamp('last_seen_at', {withTimezone: true}).notNull().defaultNow(),
35
+ closedAt: timestamp('closed_at', {withTimezone: true}),
36
+ closeReason: text('close_reason'),
37
+ createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),
38
+ },
39
+ (table) => [
40
+ uniqueIndex('runners_runner_control_sessions_hashed_token_unique').on(table.hashedToken),
41
+ uniqueIndex('runners_runner_control_sessions_active_runner_instance_unique')
42
+ .on(table.runnerInstanceId)
43
+ .where(sql`"closed_at" is null`),
44
+ index('runners_runner_control_sessions_runner_instance_idx').on(table.runnerInstanceId),
45
+ ],
46
+ );
@@ -1,10 +1,11 @@
1
+ import type {RunnerToolCapabilitiesDto} from '@shipfox/api-runners-dto';
1
2
  import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';
2
3
  import {sql} from 'drizzle-orm';
3
- import {index, pgEnum, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';
4
- import type {ProvisionedRunner} from '#core/entities/provisioned-runner.js';
4
+ import {index, jsonb, pgEnum, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';
5
+ import type {RunnerInstance} from '#core/entities/runner-instance.js';
5
6
  import {pgTable} from './common.js';
6
7
 
7
- export const provisionedRunnerStateEnum = pgEnum('runners_provisioned_runner_state', [
8
+ export const providerRunnerStateEnum = pgEnum('runners_provider_runner_state', [
8
9
  'starting',
9
10
  'running',
10
11
  'stopping',
@@ -13,20 +14,24 @@ export const provisionedRunnerStateEnum = pgEnum('runners_provisioned_runner_sta
13
14
  'terminated',
14
15
  ]);
15
16
 
16
- export const provisionedRunners = pgTable(
17
- 'provisioned_runners',
17
+ export const providerRunners = pgTable(
18
+ 'runner_instances',
18
19
  {
19
20
  id: uuidv7PrimaryKey(),
20
- workspaceId: uuid('workspace_id').notNull(),
21
+ // Kept during the protocol migration for legacy workspace capacity. New capacity is owned
22
+ // only by its provisioner and has no workspace assignment.
23
+ workspaceId: uuid('workspace_id'),
21
24
  provisionerId: uuid('provisioner_id').notNull(),
22
- provisionedRunnerId: text('provisioned_runner_id').notNull(),
25
+ providerRunnerId: text('provider_runner_id'),
23
26
  reservationId: uuid('reservation_id'),
24
27
  templateKey: text('template_key'),
25
- labels: text('labels').array().notNull(),
26
- state: provisionedRunnerStateEnum('state').notNull(),
28
+ labels: text('labels').array().notNull().default([]),
29
+ state: providerRunnerStateEnum('state').notNull(),
27
30
  reason: text('reason'),
28
31
  runnerSessionId: uuid('runner_session_id'),
29
32
  providerKind: text('provider_kind'),
33
+ protocolVersion: text('protocol_version'),
34
+ capabilities: jsonb('capabilities').$type<RunnerToolCapabilitiesDto | null>(),
30
35
  reportedAt: timestamp('reported_at', {withTimezone: true}).notNull(),
31
36
  startedAt: timestamp('started_at', {withTimezone: true}),
32
37
  stoppingAt: timestamp('stopping_at', {withTimezone: true}),
@@ -38,37 +43,31 @@ export const provisionedRunners = pgTable(
38
43
  updatedAt: timestamp('updated_at', {withTimezone: true}).notNull().defaultNow(),
39
44
  },
40
45
  (table) => [
41
- uniqueIndex('runners_provisioned_runners_workspace_provisioner_runner_unique').on(
42
- table.workspaceId,
43
- table.provisionerId,
44
- table.provisionedRunnerId,
45
- ),
46
- index('runners_provisioned_runners_workspace_state_updated_idx').on(
47
- table.workspaceId,
48
- table.state,
49
- table.updatedAt,
50
- ),
51
- index('runners_provisioned_runners_stale_reaper_idx').on(
46
+ uniqueIndex('runners_runner_instances_provisioner_runner_unique')
47
+ .on(table.provisionerId, table.providerRunnerId)
48
+ .where(sql`${table.providerRunnerId} is not null`),
49
+ index('runners_runner_instances_workspace_state_updated_idx').on(table.state, table.updatedAt),
50
+ index('runners_runner_instances_stale_reaper_idx').on(
52
51
  table.state,
53
52
  table.updatedAt,
54
53
  table.reportedAt,
55
- table.workspaceId,
56
54
  ),
57
- index('runners_provisioned_runners_active_template_counts_idx')
58
- .on(table.workspaceId, table.provisionerId, table.state, table.templateKey)
55
+ index('runners_runner_instances_active_template_counts_idx')
56
+ .on(table.provisionerId, table.state, table.templateKey)
59
57
  .where(sql`"state" in ('starting', 'running') and "template_key" is not null`),
60
58
  ],
61
59
  );
62
60
 
63
- export type ProvisionedRunnerDb = typeof provisionedRunners.$inferSelect;
64
- export type ProvisionedRunnerInsertDb = typeof provisionedRunners.$inferInsert;
61
+ export type RunnerInstanceDb = typeof providerRunners.$inferSelect;
62
+ export type RunnerInstanceInsertDb = typeof providerRunners.$inferInsert;
65
63
 
66
- export function toProvisionedRunner(row: ProvisionedRunnerDb): ProvisionedRunner {
64
+ export function toRunnerInstance(row: RunnerInstanceDb): RunnerInstance {
65
+ if (!row.providerRunnerId) throw new Error('Planned capacity has no provider runner identity');
67
66
  return {
68
67
  id: row.id,
69
68
  workspaceId: row.workspaceId,
70
69
  provisionerId: row.provisionerId,
71
- provisionedRunnerId: row.provisionedRunnerId,
70
+ providerRunnerId: row.providerRunnerId,
72
71
  reservationId: row.reservationId,
73
72
  templateKey: row.templateKey,
74
73
  labels: row.labels,
@@ -8,7 +8,7 @@ import {pgTable} from './common.js';
8
8
  export const runnerSessionScopeEnum = pgEnum('runners_runner_session_scope', ['workspace']);
9
9
  export const runnerSessionRegistrationTokenKindEnum = pgEnum(
10
10
  'runners_runner_session_registration_token_kind',
11
- ['manual', 'ephemeral'],
11
+ ['manual', 'ephemeral', 'activation'],
12
12
  );
13
13
 
14
14
  export const runnerSessions = pgTable(
@@ -20,13 +20,15 @@ export const runnerSessions = pgTable(
20
20
  registrationTokenId: uuid('registration_token_id').notNull(),
21
21
  registrationTokenKind:
22
22
  runnerSessionRegistrationTokenKindEnum('registration_token_kind').notNull(),
23
+ runnerInstanceId: uuid('runner_instance_id'),
23
24
  provisionerId: uuid('provisioner_id'),
24
- provisionedRunnerId: text('provisioned_runner_id'),
25
+ providerRunnerId: text('provider_runner_id'),
25
26
  labels: text('labels').array().notNull(),
26
27
  toolCapabilities: jsonb('tool_capabilities').$type<RunnerToolCapabilitiesDto | null>(),
27
28
  toolCapabilitiesReportedAt: timestamp('tool_capabilities_reported_at', {withTimezone: true}),
28
29
  maxClaims: integer('max_claims'),
29
30
  claimsUsed: integer('claims_used').notNull().default(0),
31
+ revokedAt: timestamp('revoked_at', {withTimezone: true}),
30
32
  createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),
31
33
  updatedAt: timestamp('updated_at', {withTimezone: true}).notNull().defaultNow(),
32
34
  },
@@ -38,14 +40,14 @@ export const runnerSessions = pgTable(
38
40
  ),
39
41
  check(
40
42
  'runners_runner_sessions_claims_ck',
41
- sql`${table.claimsUsed} >= 0 AND ((${table.registrationTokenKind} = 'manual' AND ${table.maxClaims} IS NULL) OR (${table.registrationTokenKind} = 'ephemeral' AND ${table.maxClaims} IS NOT NULL AND ${table.maxClaims} > 0 AND ${table.claimsUsed} <= ${table.maxClaims}))`,
43
+ sql`${table.claimsUsed} >= 0 AND ((${table.registrationTokenKind} = 'manual' AND ${table.maxClaims} IS NULL) OR (${table.registrationTokenKind} in ('ephemeral', 'activation') AND ${table.maxClaims} IS NOT NULL AND ${table.maxClaims} > 0 AND ${table.claimsUsed} <= ${table.maxClaims}))`,
42
44
  ),
43
45
  check(
44
46
  'runners_runner_sessions_link_ck',
45
- sql`((${table.registrationTokenKind} = 'manual' AND ${table.provisionerId} IS NULL AND ${table.provisionedRunnerId} IS NULL) OR (${table.registrationTokenKind} = 'ephemeral' AND ${table.provisionerId} IS NOT NULL AND ${table.provisionedRunnerId} IS NOT NULL))`,
47
+ sql`((${table.registrationTokenKind} = 'manual' AND ${table.runnerInstanceId} IS NULL AND ${table.provisionerId} IS NULL AND ${table.providerRunnerId} IS NULL) OR (${table.registrationTokenKind} = 'ephemeral' AND ${table.runnerInstanceId} IS NULL AND ${table.provisionerId} IS NOT NULL AND ${table.providerRunnerId} IS NOT NULL) OR (${table.registrationTokenKind} = 'activation' AND ${table.runnerInstanceId} IS NOT NULL AND ${table.provisionerId} IS NOT NULL AND ${table.providerRunnerId} IS NOT NULL))`,
46
48
  ),
47
- index('runners_runner_sessions_provisioned_runner_updated_idx')
48
- .on(table.workspaceId, table.provisionerId, table.provisionedRunnerId, table.updatedAt)
49
+ index('runners_runner_sessions_provider_runner_updated_idx')
50
+ .on(table.workspaceId, table.provisionerId, table.providerRunnerId, table.updatedAt)
49
51
  .where(sql`"provisioner_id" IS NOT NULL`),
50
52
  ],
51
53
  );
@@ -64,13 +66,15 @@ export function toRunnerSession(row: RunnerSessionDb): RunnerSession {
64
66
  scope: row.scope,
65
67
  registrationTokenId: row.registrationTokenId,
66
68
  registrationTokenKind: row.registrationTokenKind,
69
+ runnerInstanceId: row.runnerInstanceId,
67
70
  provisionerId: row.provisionerId,
68
- provisionedRunnerId: row.provisionedRunnerId,
71
+ providerRunnerId: row.providerRunnerId,
69
72
  labels: row.labels,
70
73
  toolCapabilities: row.toolCapabilities,
71
74
  toolCapabilitiesReportedAt: row.toolCapabilitiesReportedAt,
72
75
  maxClaims: row.maxClaims,
73
76
  claimsUsed: row.claimsUsed,
77
+ revokedAt: row.revokedAt,
74
78
  createdAt: row.createdAt,
75
79
  updatedAt: row.updatedAt,
76
80
  };
@@ -18,7 +18,7 @@ export const runningJobExecutions = pgTable(
18
18
  .notNull()
19
19
  .references(() => runnerSessions.id),
20
20
  provisionerId: uuid('provisioner_id'),
21
- provisionedRunnerId: text('provisioned_runner_id'),
21
+ providerRunnerId: text('provider_runner_id'),
22
22
  requiredLabels: text('required_labels').array().notNull(),
23
23
  runnerLabels: text('runner_labels').array().notNull(),
24
24
  startedAt: timestamp('started_at', {withTimezone: true}).notNull().defaultNow(),
@@ -31,17 +31,17 @@ export const runningJobExecutions = pgTable(
31
31
  .on(table.startedAt)
32
32
  .where(sql`"first_heartbeat_at" IS NULL`),
33
33
  index('runners_running_jobs_last_heartbeat_at_idx').on(table.lastHeartbeatAt),
34
- index('runners_running_jobs_provisioned_runner_started_idx')
35
- .on(table.workspaceId, table.provisionerId, table.provisionedRunnerId, table.startedAt.desc())
34
+ index('runners_running_jobs_provider_runner_started_idx')
35
+ .on(table.workspaceId, table.provisionerId, table.providerRunnerId, table.startedAt.desc())
36
36
  .where(sql`"provisioner_id" IS NOT NULL`),
37
37
  index('runners_running_jobs_cancellation_requested_idx')
38
- .on(table.workspaceId, table.provisionerId, table.provisionedRunnerId)
38
+ .on(table.workspaceId, table.provisionerId, table.providerRunnerId)
39
39
  .where(sql`"cancellation_requested_at" IS NOT NULL`),
40
40
  index('runners_running_jobs_job_id_idx').on(table.jobId),
41
41
  index('runners_running_jobs_runner_session_id_idx').on(table.runnerSessionId),
42
42
  check(
43
43
  'runners_running_jobs_link_ck',
44
- sql`(${table.provisionerId} IS NULL) = (${table.provisionedRunnerId} IS NULL)`,
44
+ sql`(${table.provisionerId} IS NULL) = (${table.providerRunnerId} IS NULL)`,
45
45
  ),
46
46
  ],
47
47
  );
@@ -0,0 +1,26 @@
1
+ import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';
2
+ import {createRunnersModule} from './index.js';
3
+
4
+ const auth = {} as AuthInterModuleClient;
5
+
6
+ describe('createRunnersModule', () => {
7
+ it('preserves the default module composition when options are absent', () => {
8
+ const module = createRunnersModule({auth});
9
+
10
+ expect(module.name).toBe('runners');
11
+ expect(module.auth).toHaveLength(3);
12
+ expect(module.routes).toHaveLength(11);
13
+ expect(module.workers).toHaveLength(1);
14
+ });
15
+
16
+ it('accepts an instance-local installation provisioning policy', () => {
17
+ const policy = {
18
+ filterEligibleWorkspaceIds: vi.fn().mockResolvedValue(new Set<string>()),
19
+ };
20
+
21
+ const module = createRunnersModule({auth, installationProvisioning: {policy}});
22
+
23
+ expect(module.name).toBe('runners');
24
+ expect(policy.filterEligibleWorkspaceIds).not.toHaveBeenCalled();
25
+ });
26
+ });