@shipfox/api-runners 5.0.0 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (400) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/CHANGELOG.md +80 -0
  3. package/dist/config.d.ts +5 -1
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +54 -7
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/demand.d.ts +6 -6
  8. package/dist/core/demand.d.ts.map +1 -1
  9. package/dist/core/demand.js +10 -10
  10. package/dist/core/demand.js.map +1 -1
  11. package/dist/core/entities/ephemeral-registration-token.d.ts +1 -1
  12. package/dist/core/entities/ephemeral-registration-token.d.ts.map +1 -1
  13. package/dist/core/entities/ephemeral-registration-token.js.map +1 -1
  14. package/dist/core/entities/provisioner-capability-snapshot.d.ts +13 -0
  15. package/dist/core/entities/provisioner-capability-snapshot.d.ts.map +1 -0
  16. package/dist/core/entities/provisioner-capability-snapshot.js +3 -0
  17. package/dist/core/entities/provisioner-capability-snapshot.js.map +1 -0
  18. package/dist/core/entities/provisioner-token.d.ts +10 -2
  19. package/dist/core/entities/provisioner-token.d.ts.map +1 -1
  20. package/dist/core/entities/provisioner-token.js.map +1 -1
  21. package/dist/core/entities/{provisioned-runner.d.ts → runner-instance.d.ts} +7 -6
  22. package/dist/core/entities/runner-instance.d.ts.map +1 -0
  23. package/dist/core/entities/runner-instance.js +3 -0
  24. package/dist/core/entities/runner-instance.js.map +1 -0
  25. package/dist/core/entities/runner-session.d.ts +4 -2
  26. package/dist/core/entities/runner-session.d.ts.map +1 -1
  27. package/dist/core/entities/runner-session.js.map +1 -1
  28. package/dist/core/ephemeral-registration-tokens.d.ts +5 -5
  29. package/dist/core/ephemeral-registration-tokens.d.ts.map +1 -1
  30. package/dist/core/ephemeral-registration-tokens.js +12 -12
  31. package/dist/core/ephemeral-registration-tokens.js.map +1 -1
  32. package/dist/core/errors.d.ts +16 -4
  33. package/dist/core/errors.d.ts.map +1 -1
  34. package/dist/core/errors.js +22 -4
  35. package/dist/core/errors.js.map +1 -1
  36. package/dist/core/index.d.ts +9 -5
  37. package/dist/core/index.d.ts.map +1 -1
  38. package/dist/core/index.js +6 -3
  39. package/dist/core/index.js.map +1 -1
  40. package/dist/core/job-executions.d.ts +2 -0
  41. package/dist/core/job-executions.d.ts.map +1 -1
  42. package/dist/core/job-executions.js +1 -2
  43. package/dist/core/job-executions.js.map +1 -1
  44. package/dist/core/maintenance.d.ts +1 -1
  45. package/dist/core/maintenance.d.ts.map +1 -1
  46. package/dist/core/maintenance.js +5 -5
  47. package/dist/core/maintenance.js.map +1 -1
  48. package/dist/core/provisioner-capability-snapshots.d.ts +5 -0
  49. package/dist/core/provisioner-capability-snapshots.d.ts.map +1 -0
  50. package/dist/core/provisioner-capability-snapshots.js +10 -0
  51. package/dist/core/provisioner-capability-snapshots.js.map +1 -0
  52. package/dist/core/provisioner-tokens.d.ts +10 -0
  53. package/dist/core/provisioner-tokens.d.ts.map +1 -1
  54. package/dist/core/provisioner-tokens.js +23 -1
  55. package/dist/core/provisioner-tokens.js.map +1 -1
  56. package/dist/core/rate-limit.d.ts.map +1 -1
  57. package/dist/core/rate-limit.js +2 -7
  58. package/dist/core/rate-limit.js.map +1 -1
  59. package/dist/core/runner-activation.d.ts +13 -0
  60. package/dist/core/runner-activation.d.ts.map +1 -0
  61. package/dist/core/runner-activation.js +36 -0
  62. package/dist/core/runner-activation.js.map +1 -0
  63. package/dist/core/runner-assignments.d.ts +6 -0
  64. package/dist/core/runner-assignments.d.ts.map +1 -0
  65. package/dist/core/runner-assignments.js +6 -0
  66. package/dist/core/runner-assignments.js.map +1 -0
  67. package/dist/core/runner-control-sessions.d.ts +46 -0
  68. package/dist/core/runner-control-sessions.d.ts.map +1 -0
  69. package/dist/core/runner-control-sessions.js +121 -0
  70. package/dist/core/runner-control-sessions.js.map +1 -0
  71. package/dist/core/runner-instances.d.ts +72 -0
  72. package/dist/core/runner-instances.d.ts.map +1 -0
  73. package/dist/core/runner-instances.js +165 -0
  74. package/dist/core/runner-instances.js.map +1 -0
  75. package/dist/core/runner-sessions.d.ts +8 -2
  76. package/dist/core/runner-sessions.d.ts.map +1 -1
  77. package/dist/core/runner-sessions.js +8 -5
  78. package/dist/core/runner-sessions.js.map +1 -1
  79. package/dist/db/db.d.ts +2698 -1054
  80. package/dist/db/db.d.ts.map +1 -1
  81. package/dist/db/db.js +9 -2
  82. package/dist/db/db.js.map +1 -1
  83. package/dist/db/ephemeral-registration-tokens.d.ts +2 -2
  84. package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -1
  85. package/dist/db/ephemeral-registration-tokens.js +21 -21
  86. package/dist/db/ephemeral-registration-tokens.js.map +1 -1
  87. package/dist/db/index.d.ts +7 -4
  88. package/dist/db/index.d.ts.map +1 -1
  89. package/dist/db/index.js +5 -2
  90. package/dist/db/index.js.map +1 -1
  91. package/dist/db/job-executions.d.ts +6 -6
  92. package/dist/db/job-executions.d.ts.map +1 -1
  93. package/dist/db/job-executions.js +20 -19
  94. package/dist/db/job-executions.js.map +1 -1
  95. package/dist/db/provisioner-capability-snapshots.d.ts +21 -0
  96. package/dist/db/provisioner-capability-snapshots.d.ts.map +1 -0
  97. package/dist/db/provisioner-capability-snapshots.js +57 -0
  98. package/dist/db/provisioner-capability-snapshots.js.map +1 -0
  99. package/dist/db/provisioner-tokens.d.ts +7 -2
  100. package/dist/db/provisioner-tokens.d.ts.map +1 -1
  101. package/dist/db/provisioner-tokens.js +59 -10
  102. package/dist/db/provisioner-tokens.js.map +1 -1
  103. package/dist/db/reservations.d.ts +18 -0
  104. package/dist/db/reservations.d.ts.map +1 -1
  105. package/dist/db/reservations.js +88 -1
  106. package/dist/db/reservations.js.map +1 -1
  107. package/dist/db/runner-assignments.d.ts +7 -0
  108. package/dist/db/runner-assignments.d.ts.map +1 -0
  109. package/dist/db/runner-assignments.js +55 -0
  110. package/dist/db/runner-assignments.js.map +1 -0
  111. package/dist/db/runner-instances.d.ts +84 -0
  112. package/dist/db/runner-instances.d.ts.map +1 -0
  113. package/dist/db/runner-instances.js +497 -0
  114. package/dist/db/runner-instances.js.map +1 -0
  115. package/dist/db/runner-sessions.d.ts +5 -0
  116. package/dist/db/runner-sessions.d.ts.map +1 -1
  117. package/dist/db/runner-sessions.js +48 -2
  118. package/dist/db/runner-sessions.js.map +1 -1
  119. package/dist/db/schema/ephemeral-registration-tokens.d.ts +2 -2
  120. package/dist/db/schema/ephemeral-registration-tokens.js +3 -3
  121. package/dist/db/schema/ephemeral-registration-tokens.js.map +1 -1
  122. package/dist/db/schema/provisioner-capability-snapshots.d.ts +207 -0
  123. package/dist/db/schema/provisioner-capability-snapshots.d.ts.map +1 -0
  124. package/dist/db/schema/provisioner-capability-snapshots.js +38 -0
  125. package/dist/db/schema/provisioner-capability-snapshots.js.map +1 -0
  126. package/dist/db/schema/provisioner-tokens.d.ts +19 -1
  127. package/dist/db/schema/provisioner-tokens.d.ts.map +1 -1
  128. package/dist/db/schema/provisioner-tokens.js +29 -3
  129. package/dist/db/schema/provisioner-tokens.js.map +1 -1
  130. package/dist/db/schema/runner-activation-tokens.d.ts +161 -0
  131. package/dist/db/schema/runner-activation-tokens.d.ts.map +1 -0
  132. package/dist/db/schema/runner-activation-tokens.js +27 -0
  133. package/dist/db/schema/runner-activation-tokens.js.map +1 -0
  134. package/dist/db/schema/runner-control-sessions.d.ts +338 -0
  135. package/dist/db/schema/runner-control-sessions.d.ts.map +1 -0
  136. package/dist/db/schema/runner-control-sessions.js +52 -0
  137. package/dist/db/schema/runner-control-sessions.js.map +1 -0
  138. package/dist/db/schema/{provisioned-runners.d.ts → runner-instances.d.ts} +105 -34
  139. package/dist/db/schema/runner-instances.d.ts.map +1 -0
  140. package/dist/db/schema/{provisioned-runners.js → runner-instances.js} +23 -14
  141. package/dist/db/schema/runner-instances.js.map +1 -0
  142. package/dist/db/schema/runner-sessions.d.ts +39 -5
  143. package/dist/db/schema/runner-sessions.d.ts.map +1 -1
  144. package/dist/db/schema/runner-sessions.js +13 -6
  145. package/dist/db/schema/runner-sessions.js.map +1 -1
  146. package/dist/db/schema/running-job-executions.d.ts +2 -2
  147. package/dist/db/schema/running-job-executions.js +4 -4
  148. package/dist/db/schema/running-job-executions.js.map +1 -1
  149. package/dist/index.d.ts +6 -2
  150. package/dist/index.d.ts.map +1 -1
  151. package/dist/index.js +45 -39
  152. package/dist/index.js.map +1 -1
  153. package/dist/installation-provisioning.d.ts +14 -0
  154. package/dist/installation-provisioning.d.ts.map +1 -0
  155. package/dist/installation-provisioning.js +7 -0
  156. package/dist/installation-provisioning.js.map +1 -0
  157. package/dist/metrics/index.d.ts +1 -1
  158. package/dist/metrics/index.d.ts.map +1 -1
  159. package/dist/metrics/index.js +1 -1
  160. package/dist/metrics/index.js.map +1 -1
  161. package/dist/metrics/instance.d.ts +11 -7
  162. package/dist/metrics/instance.d.ts.map +1 -1
  163. package/dist/metrics/instance.js +13 -7
  164. package/dist/metrics/instance.js.map +1 -1
  165. package/dist/presentation/auth/index.d.ts +1 -0
  166. package/dist/presentation/auth/index.d.ts.map +1 -1
  167. package/dist/presentation/auth/index.js +1 -0
  168. package/dist/presentation/auth/index.js.map +1 -1
  169. package/dist/presentation/auth/provisioner-token-auth.d.ts.map +1 -1
  170. package/dist/presentation/auth/provisioner-token-auth.js +5 -1
  171. package/dist/presentation/auth/provisioner-token-auth.js.map +1 -1
  172. package/dist/presentation/auth/runner-control-session-auth.d.ts +4 -0
  173. package/dist/presentation/auth/runner-control-session-auth.d.ts.map +1 -0
  174. package/dist/presentation/auth/runner-control-session-auth.js +29 -0
  175. package/dist/presentation/auth/runner-control-session-auth.js.map +1 -0
  176. package/dist/presentation/auth/runner-registration-token-auth.d.ts +5 -1
  177. package/dist/presentation/auth/runner-registration-token-auth.d.ts.map +1 -1
  178. package/dist/presentation/auth/runner-registration-token-auth.js +21 -1
  179. package/dist/presentation/auth/runner-registration-token-auth.js.map +1 -1
  180. package/dist/presentation/dto/index.d.ts +1 -2
  181. package/dist/presentation/dto/index.d.ts.map +1 -1
  182. package/dist/presentation/dto/index.js +1 -2
  183. package/dist/presentation/dto/index.js.map +1 -1
  184. package/dist/presentation/dto/poll-demand.d.ts.map +1 -1
  185. package/dist/presentation/dto/poll-demand.js +7 -1
  186. package/dist/presentation/dto/poll-demand.js.map +1 -1
  187. package/dist/presentation/dto/provisioner-token.d.ts +1 -0
  188. package/dist/presentation/dto/provisioner-token.d.ts.map +1 -1
  189. package/dist/presentation/dto/provisioner-token.js +5 -1
  190. package/dist/presentation/dto/provisioner-token.js.map +1 -1
  191. package/dist/presentation/dto/{provisioned-runners.d.ts → runner-instances.d.ts} +9 -9
  192. package/dist/presentation/dto/runner-instances.d.ts.map +1 -0
  193. package/dist/presentation/dto/{provisioned-runners.js → runner-instances.js} +6 -6
  194. package/dist/presentation/dto/runner-instances.js.map +1 -0
  195. package/dist/presentation/index.d.ts +2 -2
  196. package/dist/presentation/index.d.ts.map +1 -1
  197. package/dist/presentation/index.js +2 -2
  198. package/dist/presentation/index.js.map +1 -1
  199. package/dist/presentation/inter-module.d.ts +5 -0
  200. package/dist/presentation/inter-module.d.ts.map +1 -0
  201. package/dist/presentation/inter-module.js +43 -0
  202. package/dist/presentation/inter-module.js.map +1 -0
  203. package/dist/presentation/routes/assign-runner-instances.d.ts +2 -0
  204. package/dist/presentation/routes/assign-runner-instances.d.ts.map +1 -0
  205. package/dist/presentation/routes/assign-runner-instances.js +40 -0
  206. package/dist/presentation/routes/assign-runner-instances.js.map +1 -0
  207. package/dist/presentation/routes/get-provisioner-me.d.ts.map +1 -1
  208. package/dist/presentation/routes/get-provisioner-me.js +5 -1
  209. package/dist/presentation/routes/get-provisioner-me.js.map +1 -1
  210. package/dist/presentation/routes/heartbeat.d.ts +2 -1
  211. package/dist/presentation/routes/heartbeat.d.ts.map +1 -1
  212. package/dist/presentation/routes/heartbeat.js +60 -50
  213. package/dist/presentation/routes/heartbeat.js.map +1 -1
  214. package/dist/presentation/routes/index.d.ts +3 -1
  215. package/dist/presentation/routes/index.d.ts.map +1 -1
  216. package/dist/presentation/routes/index.js +80 -51
  217. package/dist/presentation/routes/index.js.map +1 -1
  218. package/dist/presentation/routes/poll-demand.d.ts +2 -0
  219. package/dist/presentation/routes/poll-demand.d.ts.map +1 -1
  220. package/dist/presentation/routes/poll-demand.js +86 -45
  221. package/dist/presentation/routes/poll-demand.js.map +1 -1
  222. package/dist/presentation/routes/reconcile-runner-instances.d.ts +2 -0
  223. package/dist/presentation/routes/reconcile-runner-instances.d.ts.map +1 -0
  224. package/dist/presentation/routes/reconcile-runner-instances.js +27 -0
  225. package/dist/presentation/routes/reconcile-runner-instances.js.map +1 -0
  226. package/dist/presentation/routes/register.d.ts +2 -1
  227. package/dist/presentation/routes/register.d.ts.map +1 -1
  228. package/dist/presentation/routes/register.js +55 -52
  229. package/dist/presentation/routes/register.js.map +1 -1
  230. package/dist/presentation/routes/report-runner-instances.d.ts +2 -0
  231. package/dist/presentation/routes/report-runner-instances.d.ts.map +1 -0
  232. package/dist/presentation/routes/report-runner-instances.js +37 -0
  233. package/dist/presentation/routes/report-runner-instances.js.map +1 -0
  234. package/dist/presentation/routes/request-job.d.ts +2 -1
  235. package/dist/presentation/routes/request-job.d.ts.map +1 -1
  236. package/dist/presentation/routes/request-job.js +39 -36
  237. package/dist/presentation/routes/request-job.js.map +1 -1
  238. package/dist/presentation/routes/runner-enrollment.d.ts +8 -0
  239. package/dist/presentation/routes/runner-enrollment.d.ts.map +1 -0
  240. package/dist/presentation/routes/runner-enrollment.js +212 -0
  241. package/dist/presentation/routes/runner-enrollment.js.map +1 -0
  242. package/dist/temporal/activities/index.d.ts +2 -2
  243. package/dist/temporal/activities/index.d.ts.map +1 -1
  244. package/dist/temporal/activities/index.js +2 -2
  245. package/dist/temporal/activities/index.js.map +1 -1
  246. package/dist/temporal/activities/maintenance-activities.d.ts +1 -1
  247. package/dist/temporal/activities/maintenance-activities.d.ts.map +1 -1
  248. package/dist/temporal/activities/maintenance-activities.js +3 -3
  249. package/dist/temporal/activities/maintenance-activities.js.map +1 -1
  250. package/dist/temporal/workflows/index.bundle.js +3 -3
  251. package/dist/temporal/workflows/stuck-job-detector.js +2 -2
  252. package/dist/temporal/workflows/stuck-job-detector.js.map +1 -1
  253. package/dist/tsconfig.test.tsbuildinfo +1 -1
  254. package/drizzle/0000_initial.sql +91 -24
  255. package/drizzle/meta/0000_snapshot.json +392 -63
  256. package/package.json +21 -16
  257. package/src/config.test.ts +0 -26
  258. package/src/config.ts +48 -7
  259. package/src/core/demand.test.ts +29 -29
  260. package/src/core/demand.ts +22 -22
  261. package/src/core/entities/ephemeral-registration-token.ts +1 -1
  262. package/src/core/entities/provisioner-capability-snapshot.ts +12 -0
  263. package/src/core/entities/provisioner-token.ts +8 -2
  264. package/src/core/entities/{provisioned-runner.ts → runner-instance.ts} +6 -5
  265. package/src/core/entities/runner-session.ts +4 -2
  266. package/src/core/ephemeral-registration-tokens.test.ts +11 -11
  267. package/src/core/ephemeral-registration-tokens.ts +18 -20
  268. package/src/core/errors.ts +23 -4
  269. package/src/core/index.ts +37 -22
  270. package/src/core/job-executions.ts +3 -2
  271. package/src/core/maintenance.test.ts +13 -17
  272. package/src/core/maintenance.ts +5 -5
  273. package/src/core/provisioner-capability-snapshots.test.ts +35 -0
  274. package/src/core/provisioner-capability-snapshots.ts +12 -0
  275. package/src/core/provisioner-tokens.test.ts +20 -0
  276. package/src/core/provisioner-tokens.ts +33 -0
  277. package/src/core/rate-limit.test.ts +11 -27
  278. package/src/core/rate-limit.ts +3 -11
  279. package/src/core/runner-activation.ts +72 -0
  280. package/src/core/runner-assignments.ts +9 -0
  281. package/src/core/runner-control-sessions.ts +190 -0
  282. package/src/core/{provisioned-runners.test.ts → runner-instances.test.ts} +24 -24
  283. package/src/core/runner-instances.ts +301 -0
  284. package/src/core/runner-sessions.test.ts +112 -22
  285. package/src/core/runner-sessions.ts +25 -14
  286. package/src/db/db.ts +9 -2
  287. package/src/db/ephemeral-registration-tokens.test.ts +9 -9
  288. package/src/db/ephemeral-registration-tokens.ts +26 -26
  289. package/src/db/index.ts +30 -21
  290. package/src/db/job-executions.test.ts +13 -11
  291. package/src/db/job-executions.ts +33 -27
  292. package/src/db/provisioner-capability-snapshots.test.ts +175 -0
  293. package/src/db/provisioner-capability-snapshots.ts +130 -0
  294. package/src/db/provisioner-tokens.test.ts +139 -2
  295. package/src/db/provisioner-tokens.ts +123 -15
  296. package/src/db/reservations.test.ts +49 -0
  297. package/src/db/reservations.ts +146 -1
  298. package/src/db/runner-assignments.test.ts +163 -0
  299. package/src/db/runner-assignments.ts +115 -0
  300. package/src/db/{provisioned-runners.test.ts → runner-instances.test.ts} +442 -353
  301. package/src/db/runner-instances.ts +922 -0
  302. package/src/db/runner-sessions.test.ts +19 -6
  303. package/src/db/runner-sessions.ts +78 -2
  304. package/src/db/schema/ephemeral-registration-tokens.ts +4 -4
  305. package/src/db/schema/provisioner-capability-snapshots.ts +46 -0
  306. package/src/db/schema/provisioner-tokens.ts +33 -2
  307. package/src/db/schema/runner-activation-tokens.ts +22 -0
  308. package/src/db/schema/runner-control-sessions.ts +46 -0
  309. package/src/db/schema/{provisioned-runners.ts → runner-instances.ts} +28 -27
  310. package/src/db/schema/runner-sessions.ts +11 -7
  311. package/src/db/schema/running-job-executions.ts +5 -5
  312. package/src/index.test.ts +26 -0
  313. package/src/index.ts +38 -27
  314. package/src/installation-provisioning.ts +14 -0
  315. package/src/metrics/index.ts +5 -5
  316. package/src/metrics/instance.ts +25 -14
  317. package/src/presentation/auth/index.ts +4 -0
  318. package/src/presentation/auth/provisioner-token-auth.ts +8 -4
  319. package/src/presentation/auth/runner-control-session-auth.ts +31 -0
  320. package/src/presentation/auth/runner-registration-token-auth.ts +39 -3
  321. package/src/presentation/dto/index.ts +4 -8
  322. package/src/presentation/dto/poll-demand.ts +3 -1
  323. package/src/presentation/dto/provisioner-token.ts +5 -0
  324. package/src/presentation/dto/{provisioned-runners.ts → runner-instances.ts} +13 -15
  325. package/src/presentation/index.ts +2 -1
  326. package/src/presentation/inter-module.test.ts +15 -0
  327. package/src/presentation/inter-module.ts +53 -0
  328. package/src/presentation/routes/assign-runner-instances.ts +49 -0
  329. package/src/presentation/routes/get-provisioner-me.ts +3 -4
  330. package/src/presentation/routes/heartbeat.test.ts +4 -3
  331. package/src/presentation/routes/heartbeat.ts +67 -64
  332. package/src/presentation/routes/index.ts +91 -44
  333. package/src/presentation/routes/list-active-runners.test.ts +23 -23
  334. package/src/presentation/routes/manual-registration-tokens.test.ts +4 -3
  335. package/src/presentation/routes/poll-demand-release.test.ts +4 -4
  336. package/src/presentation/routes/poll-demand.test.ts +161 -22
  337. package/src/presentation/routes/poll-demand.ts +105 -45
  338. package/src/presentation/routes/provisioner-me.test.ts +37 -3
  339. package/src/presentation/routes/{reconcile-provisioned-runners.test.ts → reconcile-runner-instances.test.ts} +66 -61
  340. package/src/presentation/routes/reconcile-runner-instances.ts +30 -0
  341. package/src/presentation/routes/register.test.ts +9 -5
  342. package/src/presentation/routes/register.ts +61 -57
  343. package/src/presentation/routes/{report-provisioned-runners.test.ts → report-runner-instances.test.ts} +28 -28
  344. package/src/presentation/routes/{report-provisioned-runners.ts → report-runner-instances.ts} +14 -14
  345. package/src/presentation/routes/request-job.test.ts +5 -3
  346. package/src/presentation/routes/request-job.ts +41 -37
  347. package/src/presentation/routes/runner-enrollment.test.ts +319 -0
  348. package/src/presentation/routes/runner-enrollment.ts +196 -0
  349. package/src/temporal/activities/index.ts +2 -2
  350. package/src/temporal/activities/maintenance-activities.test.ts +7 -7
  351. package/src/temporal/activities/maintenance-activities.ts +3 -3
  352. package/src/temporal/workflows/stuck-job-detector.test.ts +3 -3
  353. package/src/temporal/workflows/stuck-job-detector.ts +2 -2
  354. package/test/env.ts +1 -3
  355. package/test/factories/ephemeral-registration-token.ts +2 -2
  356. package/test/factories/provisioner-token.ts +3 -1
  357. package/test/factories/runner-instance.ts +57 -0
  358. package/test/factories/runner-session.ts +3 -1
  359. package/test/fixtures/auth-inter-module.ts +30 -0
  360. package/test/globalSetup.ts +4 -1
  361. package/test/index.ts +2 -1
  362. package/tsconfig.build.tsbuildinfo +1 -1
  363. package/dist/core/entities/provisioned-runner.d.ts.map +0 -1
  364. package/dist/core/entities/provisioned-runner.js +0 -3
  365. package/dist/core/entities/provisioned-runner.js.map +0 -1
  366. package/dist/core/provisioned-runners.d.ts +0 -67
  367. package/dist/core/provisioned-runners.d.ts.map +0 -1
  368. package/dist/core/provisioned-runners.js +0 -162
  369. package/dist/core/provisioned-runners.js.map +0 -1
  370. package/dist/db/provisioned-runners.d.ts +0 -79
  371. package/dist/db/provisioned-runners.d.ts.map +0 -1
  372. package/dist/db/provisioned-runners.js +0 -466
  373. package/dist/db/provisioned-runners.js.map +0 -1
  374. package/dist/db/schema/provisioned-runners.d.ts.map +0 -1
  375. package/dist/db/schema/provisioned-runners.js.map +0 -1
  376. package/dist/presentation/dto/mint-registration-tokens.d.ts +0 -5
  377. package/dist/presentation/dto/mint-registration-tokens.d.ts.map +0 -1
  378. package/dist/presentation/dto/mint-registration-tokens.js +0 -19
  379. package/dist/presentation/dto/mint-registration-tokens.js.map +0 -1
  380. package/dist/presentation/dto/provisioned-runners.d.ts.map +0 -1
  381. package/dist/presentation/dto/provisioned-runners.js.map +0 -1
  382. package/dist/presentation/routes/mint-registration-tokens.d.ts +0 -2
  383. package/dist/presentation/routes/mint-registration-tokens.d.ts.map +0 -1
  384. package/dist/presentation/routes/mint-registration-tokens.js +0 -74
  385. package/dist/presentation/routes/mint-registration-tokens.js.map +0 -1
  386. package/dist/presentation/routes/reconcile-provisioned-runners.d.ts +0 -2
  387. package/dist/presentation/routes/reconcile-provisioned-runners.d.ts.map +0 -1
  388. package/dist/presentation/routes/reconcile-provisioned-runners.js +0 -27
  389. package/dist/presentation/routes/reconcile-provisioned-runners.js.map +0 -1
  390. package/dist/presentation/routes/report-provisioned-runners.d.ts +0 -2
  391. package/dist/presentation/routes/report-provisioned-runners.d.ts.map +0 -1
  392. package/dist/presentation/routes/report-provisioned-runners.js +0 -37
  393. package/dist/presentation/routes/report-provisioned-runners.js.map +0 -1
  394. package/src/core/provisioned-runners.ts +0 -301
  395. package/src/db/provisioned-runners.ts +0 -886
  396. package/src/presentation/dto/mint-registration-tokens.ts +0 -35
  397. package/src/presentation/routes/mint-registration-tokens.test.ts +0 -453
  398. package/src/presentation/routes/mint-registration-tokens.ts +0 -82
  399. package/src/presentation/routes/reconcile-provisioned-runners.ts +0 -30
  400. package/test/factories/provisioned-runner.ts +0 -78
@@ -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`,
@@ -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,25 @@ 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'),
27
+ assignedAt: timestamp('assigned_at', {withTimezone: true}),
24
28
  templateKey: text('template_key'),
25
- labels: text('labels').array().notNull(),
26
- state: provisionedRunnerStateEnum('state').notNull(),
29
+ labels: text('labels').array().notNull().default([]),
30
+ state: providerRunnerStateEnum('state').notNull(),
27
31
  reason: text('reason'),
28
32
  runnerSessionId: uuid('runner_session_id'),
29
33
  providerKind: text('provider_kind'),
34
+ protocolVersion: text('protocol_version'),
35
+ capabilities: jsonb('capabilities').$type<RunnerToolCapabilitiesDto | null>(),
30
36
  reportedAt: timestamp('reported_at', {withTimezone: true}).notNull(),
31
37
  startedAt: timestamp('started_at', {withTimezone: true}),
32
38
  stoppingAt: timestamp('stopping_at', {withTimezone: true}),
@@ -38,38 +44,33 @@ export const provisionedRunners = pgTable(
38
44
  updatedAt: timestamp('updated_at', {withTimezone: true}).notNull().defaultNow(),
39
45
  },
40
46
  (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(
47
+ uniqueIndex('runners_runner_instances_provisioner_runner_unique')
48
+ .on(table.provisionerId, table.providerRunnerId)
49
+ .where(sql`${table.providerRunnerId} is not null`),
50
+ index('runners_runner_instances_workspace_state_updated_idx').on(table.state, table.updatedAt),
51
+ index('runners_runner_instances_stale_reaper_idx').on(
52
52
  table.state,
53
53
  table.updatedAt,
54
54
  table.reportedAt,
55
- table.workspaceId,
56
55
  ),
57
- index('runners_provisioned_runners_active_template_counts_idx')
58
- .on(table.workspaceId, table.provisionerId, table.state, table.templateKey)
56
+ index('runners_runner_instances_active_template_counts_idx')
57
+ .on(table.provisionerId, table.state, table.templateKey)
59
58
  .where(sql`"state" in ('starting', 'running') and "template_key" is not null`),
60
59
  ],
61
60
  );
62
61
 
63
- export type ProvisionedRunnerDb = typeof provisionedRunners.$inferSelect;
64
- export type ProvisionedRunnerInsertDb = typeof provisionedRunners.$inferInsert;
62
+ export type RunnerInstanceDb = typeof providerRunners.$inferSelect;
63
+ export type RunnerInstanceInsertDb = typeof providerRunners.$inferInsert;
65
64
 
66
- export function toProvisionedRunner(row: ProvisionedRunnerDb): ProvisionedRunner {
65
+ export function toRunnerInstance(row: RunnerInstanceDb): RunnerInstance {
66
+ if (!row.providerRunnerId) throw new Error('Runner instance has no provider runner identity');
67
67
  return {
68
68
  id: row.id,
69
69
  workspaceId: row.workspaceId,
70
70
  provisionerId: row.provisionerId,
71
- provisionedRunnerId: row.provisionedRunnerId,
71
+ providerRunnerId: row.providerRunnerId,
72
72
  reservationId: row.reservationId,
73
+ assignedAt: row.assignedAt,
73
74
  templateKey: row.templateKey,
74
75
  labels: row.labels,
75
76
  state: row.state,
@@ -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
+ });