@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
@@ -0,0 +1,319 @@
1
+ import {createLeaseTokenAuthMethod, createRunnerSessionAuthMethod} from '@shipfox/api-auth';
2
+ import {AUTH_PROVISIONER_TOKEN, AUTH_USER, setProvisionerContext} from '@shipfox/api-auth-context';
3
+ import {
4
+ type AuthMethod,
5
+ ClientError,
6
+ closeApp,
7
+ createApp,
8
+ extractBearerToken,
9
+ } from '@shipfox/node-fastify';
10
+ import {eq} from 'drizzle-orm';
11
+ import type {FastifyInstance, FastifyRequest} from 'fastify';
12
+ import {db} from '#db/db.js';
13
+ import {reservations} from '#db/schema/reservations.js';
14
+ import {runnerActivationTokens} from '#db/schema/runner-activation-tokens.js';
15
+ import {runnerBootstrapTokens, runnerControlSessions} from '#db/schema/runner-control-sessions.js';
16
+ import {providerRunners} from '#db/schema/runner-instances.js';
17
+ import {
18
+ createRunnerControlSessionAuthMethod,
19
+ createRunnerRegistrationTokenAuthMethod,
20
+ } from '#presentation/auth/index.js';
21
+ import {runnersTestAuthClient} from '#test/index.js';
22
+ import {createRunnerRoutes} from './index.js';
23
+
24
+ const token = 'provisioner-test-token';
25
+ const fakeUserAuth: AuthMethod = {name: AUTH_USER, authenticate: () => Promise.resolve()};
26
+
27
+ describe('runner enrollment control plane', () => {
28
+ let app: FastifyInstance;
29
+ let provisionerId: string;
30
+ const provisionerAuth: AuthMethod = {
31
+ name: AUTH_PROVISIONER_TOKEN,
32
+ authenticate: (request: FastifyRequest) => {
33
+ if (extractBearerToken(request.headers.authorization) !== token)
34
+ throw new ClientError('Invalid provisioner token', 'unauthorized', {status: 401});
35
+ setProvisionerContext(request, {scope: 'installation', provisionerTokenId: provisionerId});
36
+ return Promise.resolve();
37
+ },
38
+ };
39
+
40
+ beforeAll(async () => {
41
+ app = await createApp({
42
+ auth: [
43
+ fakeUserAuth,
44
+ provisionerAuth,
45
+ createRunnerRegistrationTokenAuthMethod(),
46
+ createRunnerControlSessionAuthMethod(),
47
+ createRunnerSessionAuthMethod(),
48
+ createLeaseTokenAuthMethod(),
49
+ ],
50
+ routes: createRunnerRoutes(runnersTestAuthClient),
51
+ swagger: false,
52
+ });
53
+ await app.ready();
54
+ });
55
+
56
+ afterAll(async () => {
57
+ await closeApp();
58
+ });
59
+
60
+ beforeEach(() => {
61
+ provisionerId = crypto.randomUUID();
62
+ });
63
+
64
+ it('creates a hashed one-use bootstrap token and restricts its control session to its instance', async () => {
65
+ const created = await app.inject({
66
+ method: 'POST',
67
+ url: '/provisioners/runner-instances/batch',
68
+ headers: {authorization: `Bearer ${token}`},
69
+ payload: {provider_kind: 'docker', runner_instances: [{template_key: 'linux'}]},
70
+ });
71
+
72
+ expect(created.statusCode).toBe(200);
73
+ const runner = created.json().runner_instances[0];
74
+ const [storedBootstrap] = await db()
75
+ .select()
76
+ .from(runnerBootstrapTokens)
77
+ .where(eq(runnerBootstrapTokens.runnerInstanceId, runner.runner_instance_id));
78
+ expect(storedBootstrap?.hashedToken).not.toBe(runner.bootstrap_token);
79
+ expect(storedBootstrap?.consumedAt).toBeNull();
80
+
81
+ const exchanged = await app.inject({
82
+ method: 'POST',
83
+ url: '/runner-enrollment/exchange',
84
+ payload: {bootstrap_token: runner.bootstrap_token},
85
+ });
86
+
87
+ expect(exchanged.statusCode).toBe(200);
88
+ const controlToken = exchanged.json().control_session_token;
89
+ const reused = await app.inject({
90
+ method: 'POST',
91
+ url: '/runner-enrollment/exchange',
92
+ payload: {bootstrap_token: runner.bootstrap_token},
93
+ });
94
+ expect(reused.statusCode).toBe(401);
95
+
96
+ const enrolled = await app.inject({
97
+ method: 'POST',
98
+ url: '/runner-control/enrollment',
99
+ headers: {authorization: `Bearer ${controlToken}`},
100
+ payload: {labels: ['Linux', 'linux'], provider_kind: 'docker', protocol_version: '1'},
101
+ });
102
+ expect(enrolled.statusCode).toBe(204);
103
+
104
+ const attached = await app.inject({
105
+ method: 'POST',
106
+ url: '/runner-control/provider-runner',
107
+ headers: {authorization: `Bearer ${controlToken}`},
108
+ payload: {provider_runner_id: 'container-1'},
109
+ });
110
+ expect(attached.json()).toEqual({attached: true});
111
+
112
+ const [instance] = await db()
113
+ .select()
114
+ .from(providerRunners)
115
+ .where(eq(providerRunners.id, runner.runner_instance_id));
116
+ expect(instance).toMatchObject({
117
+ provisionerId,
118
+ labels: ['linux'],
119
+ providerRunnerId: 'container-1',
120
+ state: 'running',
121
+ protocolVersion: '1',
122
+ });
123
+ const [session] = await db()
124
+ .select()
125
+ .from(runnerControlSessions)
126
+ .where(eq(runnerControlSessions.runnerInstanceId, runner.runner_instance_id));
127
+ expect(session?.hashedToken).not.toBe(controlToken);
128
+
129
+ const jobs = await app.inject({
130
+ method: 'POST',
131
+ url: '/runners/jobs/request',
132
+ headers: {authorization: `Bearer ${controlToken}`},
133
+ });
134
+ expect(jobs.statusCode).toBe(401);
135
+ });
136
+
137
+ it('rejects enrollment when the authenticated runner instance is terminal', async () => {
138
+ const created = await app.inject({
139
+ method: 'POST',
140
+ url: '/provisioners/runner-instances/batch',
141
+ headers: {authorization: `Bearer ${token}`},
142
+ payload: {runner_instances: [{}]},
143
+ });
144
+ const runner = created.json().runner_instances[0];
145
+ const exchanged = await app.inject({
146
+ method: 'POST',
147
+ url: '/runner-enrollment/exchange',
148
+ payload: {bootstrap_token: runner.bootstrap_token},
149
+ });
150
+ await db()
151
+ .update(providerRunners)
152
+ .set({state: 'failed'})
153
+ .where(eq(providerRunners.id, runner.runner_instance_id));
154
+
155
+ const enrolled = await app.inject({
156
+ method: 'POST',
157
+ url: '/runner-control/enrollment',
158
+ headers: {authorization: `Bearer ${exchanged.json().control_session_token}`},
159
+ payload: {labels: ['linux'], provider_kind: 'docker', protocol_version: '1'},
160
+ });
161
+
162
+ expect(enrolled.statusCode).toBe(409);
163
+ expect(enrolled.json()).toMatchObject({code: 'runner-control-session-invalid'});
164
+ });
165
+
166
+ it('assigns an enrolled runner instance only through the owned reservation', async () => {
167
+ const workspaceId = crypto.randomUUID();
168
+ const created = await app.inject({
169
+ method: 'POST',
170
+ url: '/provisioners/runner-instances/batch',
171
+ headers: {authorization: `Bearer ${token}`},
172
+ payload: {runner_instances: [{template_key: 'linux'}]},
173
+ });
174
+ const runner = created.json().runner_instances[0];
175
+ const exchanged = await app.inject({
176
+ method: 'POST',
177
+ url: '/runner-enrollment/exchange',
178
+ payload: {bootstrap_token: runner.bootstrap_token},
179
+ });
180
+ const controlToken = exchanged.json().control_session_token;
181
+ await app.inject({
182
+ method: 'POST',
183
+ url: '/runner-control/enrollment',
184
+ headers: {authorization: `Bearer ${controlToken}`},
185
+ payload: {labels: ['linux'], provider_kind: 'docker', protocol_version: '1'},
186
+ });
187
+ await app.inject({
188
+ method: 'POST',
189
+ url: '/runner-control/provider-runner',
190
+ headers: {authorization: `Bearer ${controlToken}`},
191
+ payload: {provider_runner_id: 'container-assignment-test'},
192
+ });
193
+ const [reservation] = await db()
194
+ .insert(reservations)
195
+ .values({
196
+ workspaceId,
197
+ provisionerId,
198
+ requiredLabels: ['linux'],
199
+ count: 1,
200
+ expiresAt: new Date(Date.now() + 60_000),
201
+ })
202
+ .returning();
203
+ if (!reservation) throw new Error('Reservation insert returned no row');
204
+
205
+ const assigned = await app.inject({
206
+ method: 'POST',
207
+ url: '/provisioners/runner-instances/assignments',
208
+ headers: {authorization: `Bearer ${token}`},
209
+ payload: {reservation_id: reservation.id, runner_instance_ids: [runner.runner_instance_id]},
210
+ });
211
+
212
+ expect(assigned.statusCode).toBe(200);
213
+ expect(assigned.json()).toEqual({runner_instance_ids: [runner.runner_instance_id]});
214
+ const [instance] = await db()
215
+ .select()
216
+ .from(providerRunners)
217
+ .where(eq(providerRunners.id, runner.runner_instance_id));
218
+ expect(instance).toMatchObject({
219
+ workspaceId,
220
+ reservationId: reservation.id,
221
+ assignedAt: expect.any(Date),
222
+ });
223
+ });
224
+
225
+ it('returns an activation token only for its assigned runner and closes control access after registration', async () => {
226
+ const created = await app.inject({
227
+ method: 'POST',
228
+ url: '/provisioners/runner-instances/batch',
229
+ headers: {authorization: `Bearer ${token}`},
230
+ payload: {runner_instances: [{}]},
231
+ });
232
+ const runner = created.json().runner_instances[0];
233
+ const exchanged = await app.inject({
234
+ method: 'POST',
235
+ url: '/runner-enrollment/exchange',
236
+ payload: {bootstrap_token: runner.bootstrap_token},
237
+ });
238
+ const controlToken = exchanged.json().control_session_token;
239
+ const workspaceId = crypto.randomUUID();
240
+ await db()
241
+ .update(providerRunners)
242
+ .set({
243
+ workspaceId,
244
+ reservationId: crypto.randomUUID(),
245
+ providerRunnerId: 'runner-activation-test',
246
+ state: 'running',
247
+ })
248
+ .where(eq(providerRunners.id, runner.runner_instance_id));
249
+
250
+ const assignment = await app.inject({
251
+ method: 'GET',
252
+ url: '/runner-control/assignment',
253
+ headers: {authorization: `Bearer ${controlToken}`},
254
+ });
255
+ expect(assignment.statusCode).toBe(200);
256
+ const activationToken = assignment.json().activation_token;
257
+ expect(typeof activationToken).toBe('string');
258
+
259
+ const registered = await app.inject({
260
+ method: 'POST',
261
+ url: '/runners/register',
262
+ headers: {authorization: `Bearer ${activationToken}`},
263
+ payload: {labels: ['linux']},
264
+ });
265
+ expect(registered.statusCode).toBe(200);
266
+ expect(registered.json()).toMatchObject({mode: 'activation', max_claims: 1});
267
+
268
+ const closed = await app.inject({
269
+ method: 'POST',
270
+ url: '/runner-control/heartbeat',
271
+ headers: {authorization: `Bearer ${controlToken}`},
272
+ });
273
+ expect(closed.statusCode).toBe(401);
274
+ });
275
+
276
+ it('rejects registration with an expired activation token', async () => {
277
+ const created = await app.inject({
278
+ method: 'POST',
279
+ url: '/provisioners/runner-instances/batch',
280
+ headers: {authorization: `Bearer ${token}`},
281
+ payload: {runner_instances: [{}]},
282
+ });
283
+ const runner = created.json().runner_instances[0];
284
+ const exchanged = await app.inject({
285
+ method: 'POST',
286
+ url: '/runner-enrollment/exchange',
287
+ payload: {bootstrap_token: runner.bootstrap_token},
288
+ });
289
+ const workspaceId = crypto.randomUUID();
290
+ await db()
291
+ .update(providerRunners)
292
+ .set({
293
+ workspaceId,
294
+ reservationId: crypto.randomUUID(),
295
+ providerRunnerId: 'runner-expired',
296
+ state: 'running',
297
+ })
298
+ .where(eq(providerRunners.id, runner.runner_instance_id));
299
+ const assignment = await app.inject({
300
+ method: 'GET',
301
+ url: '/runner-control/assignment',
302
+ headers: {authorization: `Bearer ${exchanged.json().control_session_token}`},
303
+ });
304
+ const activationToken = assignment.json().activation_token;
305
+ await db()
306
+ .update(runnerActivationTokens)
307
+ .set({expiresAt: new Date(Date.now() - 1_000)})
308
+ .where(eq(runnerActivationTokens.runnerInstanceId, runner.runner_instance_id));
309
+
310
+ const registered = await app.inject({
311
+ method: 'POST',
312
+ url: '/runners/register',
313
+ headers: {authorization: `Bearer ${activationToken}`},
314
+ payload: {labels: ['linux']},
315
+ });
316
+
317
+ expect(registered.statusCode).toBe(401);
318
+ });
319
+ });
@@ -0,0 +1,196 @@
1
+ import {
2
+ requireProvisionerContext,
3
+ requireRunnerControlSessionContext,
4
+ } from '@shipfox/api-auth-context';
5
+ import {
6
+ attachRunnerControlProviderIdBodySchema,
7
+ createRunnerInstancesBodySchema,
8
+ createRunnerInstancesResponseSchema,
9
+ runnerAssignmentPollResponseSchema,
10
+ runnerBootstrapExchangeBodySchema,
11
+ runnerBootstrapExchangeResponseSchema,
12
+ runnerControlHeartbeatResponseSchema,
13
+ runnerEnrollmentBodySchema,
14
+ } from '@shipfox/api-runners-dto';
15
+ import {ClientError, defineRoute} from '@shipfox/node-fastify';
16
+ import {z} from 'zod';
17
+ import {config} from '#config.js';
18
+ import {getRunnerAssignment, issueRunnerActivationToken} from '#core/runner-activation.js';
19
+ import {
20
+ attachRunnerControlProviderId,
21
+ createRunnerInstancesWithBootstrapTokens,
22
+ enrollRunnerControlSession,
23
+ exchangeRunnerBootstrapToken,
24
+ RunnerBootstrapTokenInvalidError,
25
+ RunnerControlSessionInvalidError,
26
+ touchRunnerControlSession,
27
+ } from '#core/runner-control-sessions.js';
28
+ import {attachRunnerInstanceProviderId} from '#core/runner-instances.js';
29
+ import {runnerBootstrapExchangeCount, runnerControlHeartbeatCount} from '#metrics/instance.js';
30
+ import {authenticateRunnerControlSession} from '#presentation/auth/index.js';
31
+
32
+ export const createRunnerInstancesRoute = defineRoute({
33
+ method: 'POST',
34
+ path: '/runner-instances/batch',
35
+ description:
36
+ 'Create provisioner-owned runner instances and one-use bootstrap tokens before launch',
37
+ schema: {
38
+ body: createRunnerInstancesBodySchema,
39
+ response: {200: createRunnerInstancesResponseSchema},
40
+ },
41
+ handler: async (request) => {
42
+ const {provisionerTokenId} = requireProvisionerContext(request);
43
+ const results = await createRunnerInstancesWithBootstrapTokens({
44
+ provisionerId: provisionerTokenId,
45
+ ...(request.body.provider_kind ? {providerKind: request.body.provider_kind} : {}),
46
+ runnerInstances: request.body.runner_instances.map((runner) =>
47
+ runner.template_key ? {templateKey: runner.template_key} : {},
48
+ ),
49
+ ttlSeconds: config.RUNNER_BOOTSTRAP_TOKEN_TTL_SECONDS,
50
+ });
51
+ return {
52
+ runner_instances: results.map((result) => ({
53
+ runner_instance_id: result.runnerInstanceId,
54
+ bootstrap_token: result.bootstrapToken,
55
+ })),
56
+ };
57
+ },
58
+ });
59
+
60
+ export const attachRunnerInstanceProviderIdRoute = defineRoute({
61
+ method: 'POST',
62
+ path: '/runner-instances/:runnerInstanceId/provider-runner',
63
+ description: 'Attach the provider identity after the provisioner launches a runner instance',
64
+ schema: {
65
+ params: z.object({runnerInstanceId: z.string().uuid()}),
66
+ body: attachRunnerControlProviderIdBodySchema,
67
+ response: {200: z.object({attached: z.boolean()})},
68
+ },
69
+ handler: async (request) => {
70
+ const {provisionerTokenId} = requireProvisionerContext(request);
71
+ return {
72
+ attached: await attachRunnerInstanceProviderId({
73
+ runnerInstanceId: request.params.runnerInstanceId,
74
+ provisionerId: provisionerTokenId,
75
+ providerRunnerId: request.body.provider_runner_id,
76
+ }),
77
+ };
78
+ },
79
+ });
80
+
81
+ export const exchangeRunnerBootstrapRoute = defineRoute({
82
+ method: 'POST',
83
+ path: '/exchange',
84
+ description: 'Exchange a one-use runner bootstrap token for a workspace-neutral control session',
85
+ schema: {
86
+ body: runnerBootstrapExchangeBodySchema,
87
+ response: {200: runnerBootstrapExchangeResponseSchema},
88
+ },
89
+ errorHandler: (error) => {
90
+ if (error instanceof RunnerBootstrapTokenInvalidError)
91
+ runnerBootstrapExchangeCount.add(1, {outcome: 'rejected'});
92
+ if (error instanceof RunnerBootstrapTokenInvalidError)
93
+ throw new ClientError(error.message, 'runner-bootstrap-token-invalid', {status: 401});
94
+ throw error;
95
+ },
96
+ handler: async (request) => {
97
+ const result = await exchangeRunnerBootstrapToken({
98
+ rawToken: request.body.bootstrap_token,
99
+ ttlSeconds: config.RUNNER_CONTROL_SESSION_TTL_SECONDS,
100
+ });
101
+ runnerBootstrapExchangeCount.add(1, {outcome: 'accepted'});
102
+ return {
103
+ runner_instance_id: result.runnerInstanceId,
104
+ control_session_token: result.controlSessionToken,
105
+ expires_at: result.expiresAt.toISOString(),
106
+ };
107
+ },
108
+ });
109
+
110
+ export const enrollRunnerRoute = defineRoute({
111
+ method: 'POST',
112
+ path: '/enrollment',
113
+ description: 'Declare the authenticated runner instance labels and protocol capabilities',
114
+ schema: {body: runnerEnrollmentBodySchema, response: {204: z.null()}},
115
+ preHandler: authenticateRunnerControlSession,
116
+ errorHandler: (error) => {
117
+ if (error instanceof RunnerControlSessionInvalidError)
118
+ throw new ClientError(error.message, 'runner-control-session-invalid', {status: 409});
119
+ throw error;
120
+ },
121
+ handler: async (request, reply) => {
122
+ const session = requireRunnerControlSessionContext(request);
123
+ await enrollRunnerControlSession({
124
+ runnerInstanceId: session.runnerInstanceId,
125
+ provisionerId: session.provisionerId,
126
+ labels: request.body.labels,
127
+ ...(request.body.capabilities ? {capabilities: request.body.capabilities} : {}),
128
+ providerKind: request.body.provider_kind,
129
+ protocolVersion: request.body.protocol_version,
130
+ });
131
+ return await reply.code(204).send();
132
+ },
133
+ });
134
+
135
+ export const attachRunnerControlProviderIdRoute = defineRoute({
136
+ method: 'POST',
137
+ path: '/provider-runner',
138
+ description: 'Attach the authenticated runner instance provider identity once',
139
+ schema: {
140
+ body: attachRunnerControlProviderIdBodySchema,
141
+ response: {200: z.object({attached: z.boolean()})},
142
+ },
143
+ preHandler: authenticateRunnerControlSession,
144
+ handler: async (request) => {
145
+ const session = requireRunnerControlSessionContext(request);
146
+ return {
147
+ attached: await attachRunnerControlProviderId({
148
+ ...session,
149
+ providerRunnerId: request.body.provider_runner_id,
150
+ }),
151
+ };
152
+ },
153
+ });
154
+
155
+ export const runnerControlHeartbeatRoute = defineRoute({
156
+ method: 'POST',
157
+ path: '/heartbeat',
158
+ description: 'Record liveness for only the authenticated runner instance',
159
+ schema: {response: {200: runnerControlHeartbeatResponseSchema}},
160
+ preHandler: authenticateRunnerControlSession,
161
+ handler: async (request) => {
162
+ const session = requireRunnerControlSessionContext(request);
163
+ await touchRunnerControlSession(session.runnerInstanceId, session.provisionerId);
164
+ runnerControlHeartbeatCount.add(1);
165
+ return {ok: true};
166
+ },
167
+ });
168
+
169
+ export const runnerAssignmentPollRoute = defineRoute({
170
+ method: 'GET',
171
+ path: '/assignment',
172
+ description: 'Wait for only the authenticated runner instance assignment',
173
+ schema: {response: {200: runnerAssignmentPollResponseSchema}},
174
+ preHandler: authenticateRunnerControlSession,
175
+ handler: async (request, reply) => {
176
+ const session = requireRunnerControlSessionContext(request);
177
+ const deadline = Date.now() + config.RUNNER_ASSIGNMENT_POLL_MAX_WAIT_SECONDS * 1000;
178
+ const abortController = new AbortController();
179
+ reply.raw.on('close', () => abortController.abort());
180
+ while (Date.now() <= deadline) {
181
+ if (abortController.signal.aborted) return {activation_token: null};
182
+ const assignment = await getRunnerAssignment(session);
183
+ if (assignment) {
184
+ const activationToken = await issueRunnerActivationToken({
185
+ ...session,
186
+ ttlSeconds: config.RUNNER_ACTIVATION_TOKEN_TTL_SECONDS,
187
+ });
188
+ return {activation_token: activationToken};
189
+ }
190
+ await new Promise((resolve) =>
191
+ setTimeout(resolve, config.RUNNER_ASSIGNMENT_POLL_INTERVAL_MS),
192
+ );
193
+ }
194
+ return {activation_token: null};
195
+ },
196
+ });
@@ -3,7 +3,7 @@ import {
3
3
  deleteExpiredReservationsActivity,
4
4
  deleteExpiredRunnerSessionsActivity,
5
5
  detectAndExpireStuckJobsActivity,
6
- reapStaleProvisionedRunnersActivity,
6
+ reapStaleRunnerInstancesActivity,
7
7
  } from './maintenance-activities.js';
8
8
 
9
9
  export function createRunnersMaintenanceActivities() {
@@ -12,6 +12,6 @@ export function createRunnersMaintenanceActivities() {
12
12
  deleteExpiredReservationsActivity,
13
13
  deleteExpiredRunnerSessionsActivity,
14
14
  detectAndExpireStuckJobsActivity,
15
- reapStaleProvisionedRunnersActivity,
15
+ reapStaleRunnerInstancesActivity,
16
16
  };
17
17
  }
@@ -3,14 +3,14 @@ import {
3
3
  deleteExpiredRunnerReservations,
4
4
  deleteExpiredRunnerSessions,
5
5
  detectAndExpireStuckJobs,
6
- reapStaleProvisionedRunners,
6
+ reapStaleRunnerInstances,
7
7
  } from '#core/maintenance.js';
8
8
  import {
9
9
  deleteExpiredEphemeralRegistrationTokensActivity,
10
10
  deleteExpiredReservationsActivity,
11
11
  deleteExpiredRunnerSessionsActivity,
12
12
  detectAndExpireStuckJobsActivity,
13
- reapStaleProvisionedRunnersActivity,
13
+ reapStaleRunnerInstancesActivity,
14
14
  } from './maintenance-activities.js';
15
15
 
16
16
  vi.mock('#core/maintenance.js', () => ({
@@ -18,7 +18,7 @@ vi.mock('#core/maintenance.js', () => ({
18
18
  deleteExpiredRunnerReservations: vi.fn(),
19
19
  deleteExpiredRunnerSessions: vi.fn(),
20
20
  detectAndExpireStuckJobs: vi.fn(),
21
- reapStaleProvisionedRunners: vi.fn(),
21
+ reapStaleRunnerInstances: vi.fn(),
22
22
  }));
23
23
 
24
24
  beforeEach(() => {
@@ -47,20 +47,20 @@ describe('deleteExpiredReservationsActivity', () => {
47
47
  });
48
48
  });
49
49
 
50
- describe('reapStaleProvisionedRunnersActivity', () => {
50
+ describe('reapStaleRunnerInstancesActivity', () => {
51
51
  it('delegates to core maintenance', async () => {
52
- vi.mocked(reapStaleProvisionedRunners).mockResolvedValueOnce({
52
+ vi.mocked(reapStaleRunnerInstances).mockResolvedValueOnce({
53
53
  reaped: 4,
54
54
  reservationsReleased: 2,
55
55
  });
56
56
 
57
- const result = await reapStaleProvisionedRunnersActivity({
57
+ const result = await reapStaleRunnerInstancesActivity({
58
58
  thresholdSeconds: 300,
59
59
  limit: 100,
60
60
  });
61
61
 
62
62
  expect(result).toEqual({reaped: 4, reservationsReleased: 2});
63
- expect(reapStaleProvisionedRunners).toHaveBeenCalledWith({
63
+ expect(reapStaleRunnerInstances).toHaveBeenCalledWith({
64
64
  thresholdSeconds: 300,
65
65
  limit: 100,
66
66
  });
@@ -3,7 +3,7 @@ import {
3
3
  deleteExpiredRunnerReservations,
4
4
  deleteExpiredRunnerSessions,
5
5
  detectAndExpireStuckJobs,
6
- reapStaleProvisionedRunners,
6
+ reapStaleRunnerInstances,
7
7
  } from '#core/maintenance.js';
8
8
 
9
9
  export function detectAndExpireStuckJobsActivity(params: {
@@ -18,11 +18,11 @@ export function deleteExpiredReservationsActivity(params?: {
18
18
  return deleteExpiredRunnerReservations(params);
19
19
  }
20
20
 
21
- export function reapStaleProvisionedRunnersActivity(params?: {
21
+ export function reapStaleRunnerInstancesActivity(params?: {
22
22
  thresholdSeconds: number;
23
23
  limit: number;
24
24
  }): Promise<{reaped: number; reservationsReleased: number}> {
25
- return reapStaleProvisionedRunners(params);
25
+ return reapStaleRunnerInstances(params);
26
26
  }
27
27
 
28
28
  export function deleteExpiredRunnerSessionsActivity(params?: {
@@ -3,7 +3,7 @@ const mocks = vi.hoisted(() => ({
3
3
  deleteExpiredReservationsActivity: vi.fn(),
4
4
  deleteExpiredRunnerSessionsActivity: vi.fn(),
5
5
  detectAndExpireStuckJobsActivity: vi.fn(),
6
- reapStaleProvisionedRunnersActivity: vi.fn(),
6
+ reapStaleRunnerInstancesActivity: vi.fn(),
7
7
  info: vi.fn(),
8
8
  warn: vi.fn(),
9
9
  }));
@@ -19,7 +19,7 @@ vi.mock('@temporalio/workflow', () => ({
19
19
  deleteExpiredReservationsActivity: mocks.deleteExpiredReservationsActivity,
20
20
  deleteExpiredRunnerSessionsActivity: mocks.deleteExpiredRunnerSessionsActivity,
21
21
  detectAndExpireStuckJobsActivity: mocks.detectAndExpireStuckJobsActivity,
22
- reapStaleProvisionedRunnersActivity: mocks.reapStaleProvisionedRunnersActivity,
22
+ reapStaleRunnerInstancesActivity: mocks.reapStaleRunnerInstancesActivity,
23
23
  })),
24
24
  }));
25
25
 
@@ -30,7 +30,7 @@ describe('stuckJobDetector', () => {
30
30
  mocks.deleteExpiredReservationsActivity.mockResolvedValue({deleted: 0});
31
31
  mocks.deleteExpiredRunnerSessionsActivity.mockResolvedValue({deleted: 0});
32
32
  mocks.detectAndExpireStuckJobsActivity.mockResolvedValue({expired: 0});
33
- mocks.reapStaleProvisionedRunnersActivity.mockResolvedValue({
33
+ mocks.reapStaleRunnerInstancesActivity.mockResolvedValue({
34
34
  reaped: 0,
35
35
  reservationsReleased: 0,
36
36
  });
@@ -8,7 +8,7 @@ const {
8
8
  deleteExpiredReservationsActivity,
9
9
  deleteExpiredRunnerSessionsActivity,
10
10
  detectAndExpireStuckJobsActivity,
11
- reapStaleProvisionedRunnersActivity,
11
+ reapStaleRunnerInstancesActivity,
12
12
  } = proxyActivities<ReturnType<typeof createRunnersMaintenanceActivities>>({
13
13
  startToCloseTimeout: '60s',
14
14
  });
@@ -57,7 +57,7 @@ export async function stuckJobDetector(): Promise<void> {
57
57
  });
58
58
  }
59
59
 
60
- const {reaped, reservationsReleased} = await reapStaleProvisionedRunnersActivity();
60
+ const {reaped, reservationsReleased} = await reapStaleRunnerInstancesActivity();
61
61
  if (reaped > 0) {
62
62
  log.info('Stuck-job detector reaped stale provisioned runners', {
63
63
  reaped,
package/test/env.ts CHANGED
@@ -5,7 +5,5 @@ process.env.POSTGRES_PASSWORD ??= 'password';
5
5
  process.env.POSTGRES_DATABASE = 'api_test';
6
6
  process.env.POSTGRES_MAX_CONNECTIONS ??= '5';
7
7
  process.env.WORKSPACE_JWT_SECRET = 'test-secret';
8
- process.env.AUTH_JWT_SECRET = 'test-secret';
9
- process.env.AUTH_JOB_LEASE_TOKEN_SECRET = 'test-lease-secret';
10
- process.env.AUTH_RUNNER_SESSION_TOKEN_SECRET = 'test-runner-session-secret';
8
+ process.env.AUTH_ROOT_KEY = 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=';
11
9
  process.env.TZ = 'UTC';
@@ -18,7 +18,7 @@ export const ephemeralRegistrationTokenFactory = Factory.define<
18
18
  workspaceId: token.workspaceId,
19
19
  provisionerId: token.provisionerId,
20
20
  reservationId: token.reservationId,
21
- provisionedRunnerId: token.provisionedRunnerId,
21
+ providerRunnerId: token.providerRunnerId,
22
22
  hashedToken: token.hashedToken,
23
23
  prefix: token.prefix,
24
24
  expiresAt: token.expiresAt,
@@ -30,7 +30,7 @@ export const ephemeralRegistrationTokenFactory = Factory.define<
30
30
  workspaceId: crypto.randomUUID(),
31
31
  provisionerId: crypto.randomUUID(),
32
32
  reservationId: null,
33
- provisionedRunnerId: `provisioned-runner-${crypto.randomUUID()}`,
33
+ providerRunnerId: `provisioned-runner-${crypto.randomUUID()}`,
34
34
  hashedToken: hashOpaqueToken(rawToken),
35
35
  prefix: extractDisplayPrefix(rawToken),
36
36
  expiresAt: new Date(Date.now() + 60_000),