@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,175 @@
1
+ import {and, eq} from 'drizzle-orm';
2
+ import {db} from '#db/db.js';
3
+ import {
4
+ hasActiveWorkspaceProvisionerCapability,
5
+ listActiveWorkspaceProvisionerCapabilitySnapshots,
6
+ listStaleWorkspaceProvisionerCapabilitySnapshots,
7
+ publishWorkspaceProvisionerCapabilitySnapshot,
8
+ } from '#db/provisioner-capability-snapshots.js';
9
+ import {provisionerCapabilitySnapshots} from '#db/schema/provisioner-capability-snapshots.js';
10
+ import {provisionerTokens} from '#db/schema/provisioner-tokens.js';
11
+ import {provisionerTokenFactory} from '#test/index.js';
12
+
13
+ describe('workspace provisioner capability snapshots', () => {
14
+ let workspaceId: string;
15
+
16
+ beforeEach(() => {
17
+ workspaceId = crypto.randomUUID();
18
+ });
19
+
20
+ it('atomically replaces one provisioner complete template snapshot', async () => {
21
+ const provisioner = await provisionerTokenFactory.create({workspaceId});
22
+
23
+ await publishWorkspaceProvisionerCapabilitySnapshot({
24
+ workspaceId,
25
+ provisionerId: provisioner.id,
26
+ templates: [template('linux', ['linux'], 4, 1, 3), template('macos', ['macos'], 2, 0, 2)],
27
+ });
28
+ await publishWorkspaceProvisionerCapabilitySnapshot({
29
+ workspaceId,
30
+ provisionerId: provisioner.id,
31
+ templates: [template('linux-arm64', ['linux', 'arm64'], 0, 1, 0)],
32
+ });
33
+
34
+ const rows = await snapshotsFor(provisioner.id);
35
+ expect(rows).toEqual([
36
+ expect.objectContaining({
37
+ templateKey: 'linux-arm64',
38
+ labels: ['arm64', 'linux'],
39
+ availableSlots: 0,
40
+ starting: 1,
41
+ running: 0,
42
+ }),
43
+ ]);
44
+ });
45
+
46
+ it('keeps zero-slot overlapping capability active for every workspace provisioner', async () => {
47
+ const first = await provisionerTokenFactory.create({workspaceId});
48
+ const second = await provisionerTokenFactory.create({workspaceId});
49
+
50
+ await publishWorkspaceProvisionerCapabilitySnapshot({
51
+ workspaceId,
52
+ provisionerId: first.id,
53
+ templates: [template('linux', ['linux', 'x64'], 0, 0, 1)],
54
+ });
55
+ await publishWorkspaceProvisionerCapabilitySnapshot({
56
+ workspaceId,
57
+ provisionerId: second.id,
58
+ templates: [template('linux-gpu', ['linux', 'gpu'], 2, 1, 1)],
59
+ });
60
+
61
+ const active = await listActiveWorkspaceProvisionerCapabilitySnapshots({
62
+ workspaceId,
63
+ windowSeconds: 60,
64
+ });
65
+ const hasLinux = await hasActiveWorkspaceProvisionerCapability({
66
+ workspaceId,
67
+ requiredLabels: ['linux'],
68
+ windowSeconds: 60,
69
+ });
70
+ const hasLinuxX64 = await hasActiveWorkspaceProvisionerCapability({
71
+ workspaceId,
72
+ requiredLabels: ['linux', 'x64'],
73
+ windowSeconds: 60,
74
+ });
75
+
76
+ expect(active).toEqual(
77
+ expect.arrayContaining([
78
+ expect.objectContaining({provisionerId: first.id, availableSlots: 0}),
79
+ expect.objectContaining({provisionerId: second.id, labels: ['gpu', 'linux']}),
80
+ ]),
81
+ );
82
+ expect(hasLinux).toBe(true);
83
+ expect(hasLinuxX64).toBe(true);
84
+ });
85
+
86
+ it('requires every advertised label to be present, not just an overlap', async () => {
87
+ const provisioner = await provisionerTokenFactory.create({workspaceId});
88
+
89
+ await publishWorkspaceProvisionerCapabilitySnapshot({
90
+ workspaceId,
91
+ provisionerId: provisioner.id,
92
+ templates: [template('linux', ['linux'], 1, 0, 0)],
93
+ });
94
+
95
+ const hasLinuxArm64 = await hasActiveWorkspaceProvisionerCapability({
96
+ workspaceId,
97
+ requiredLabels: ['linux', 'arm64'],
98
+ windowSeconds: 60,
99
+ });
100
+
101
+ expect(hasLinuxArm64).toBe(false);
102
+ });
103
+
104
+ it('excludes stale and revoked provisioner capability from active matching', async () => {
105
+ const staleProvisioner = await provisionerTokenFactory.create({workspaceId});
106
+ const revokedProvisioner = await provisionerTokenFactory.create({workspaceId});
107
+
108
+ await publishWorkspaceProvisionerCapabilitySnapshot({
109
+ workspaceId,
110
+ provisionerId: staleProvisioner.id,
111
+ templates: [template('stale-linux', ['linux'], 1, 0, 1)],
112
+ });
113
+ await publishWorkspaceProvisionerCapabilitySnapshot({
114
+ workspaceId,
115
+ provisionerId: revokedProvisioner.id,
116
+ templates: [template('revoked-macos', ['macos'], 1, 0, 1)],
117
+ });
118
+ await db()
119
+ .update(provisionerCapabilitySnapshots)
120
+ .set({advertisedAt: new Date(Date.now() - 61_000)})
121
+ .where(eq(provisionerCapabilitySnapshots.provisionerId, staleProvisioner.id));
122
+ await db()
123
+ .update(provisionerTokens)
124
+ .set({revokedAt: new Date()})
125
+ .where(eq(provisionerTokens.id, revokedProvisioner.id));
126
+
127
+ const active = await listActiveWorkspaceProvisionerCapabilitySnapshots({
128
+ workspaceId,
129
+ windowSeconds: 60,
130
+ });
131
+ const stale = await listStaleWorkspaceProvisionerCapabilitySnapshots({
132
+ workspaceId,
133
+ windowSeconds: 60,
134
+ });
135
+ const hasLinux = await hasActiveWorkspaceProvisionerCapability({
136
+ workspaceId,
137
+ requiredLabels: ['linux'],
138
+ windowSeconds: 60,
139
+ });
140
+ const hasMacos = await hasActiveWorkspaceProvisionerCapability({
141
+ workspaceId,
142
+ requiredLabels: ['macos'],
143
+ windowSeconds: 60,
144
+ });
145
+
146
+ expect(active).toEqual([]);
147
+ expect(stale.map((snapshot) => snapshot.provisionerId).sort()).toEqual(
148
+ [staleProvisioner.id, revokedProvisioner.id].sort(),
149
+ );
150
+ expect(hasLinux).toBe(false);
151
+ expect(hasMacos).toBe(false);
152
+ });
153
+
154
+ function snapshotsFor(provisionerId: string) {
155
+ return db()
156
+ .select()
157
+ .from(provisionerCapabilitySnapshots)
158
+ .where(
159
+ and(
160
+ eq(provisionerCapabilitySnapshots.workspaceId, workspaceId),
161
+ eq(provisionerCapabilitySnapshots.provisionerId, provisionerId),
162
+ ),
163
+ );
164
+ }
165
+
166
+ function template(
167
+ templateKey: string,
168
+ labels: string[],
169
+ availableSlots: number,
170
+ starting: number,
171
+ running: number,
172
+ ) {
173
+ return {templateKey, labels, availableSlots, starting, running};
174
+ }
175
+ });
@@ -0,0 +1,130 @@
1
+ import {canonicalizeLabels} from '@shipfox/runner-labels';
2
+ import {and, arrayContains, eq, gt, isNotNull, isNull, lte, or, sql} from 'drizzle-orm';
3
+ import type {ProvisionerCapabilitySnapshot} from '#core/entities/provisioner-capability-snapshot.js';
4
+ import {db} from './db.js';
5
+ import type {ReservationTemplate} from './reservations.js';
6
+ import {
7
+ provisionerCapabilitySnapshots,
8
+ toProvisionerCapabilitySnapshot,
9
+ } from './schema/provisioner-capability-snapshots.js';
10
+ import {provisionerTokens} from './schema/provisioner-tokens.js';
11
+
12
+ export async function publishWorkspaceProvisionerCapabilitySnapshot(params: {
13
+ workspaceId: string;
14
+ provisionerId: string;
15
+ templates: ReservationTemplate[];
16
+ }): Promise<void> {
17
+ await db().transaction(async (tx) => {
18
+ await tx
19
+ .delete(provisionerCapabilitySnapshots)
20
+ .where(eq(provisionerCapabilitySnapshots.provisionerId, params.provisionerId));
21
+
22
+ if (params.templates.length === 0) return;
23
+
24
+ await tx.insert(provisionerCapabilitySnapshots).values(
25
+ params.templates.map((template) => ({
26
+ workspaceId: params.workspaceId,
27
+ provisionerId: params.provisionerId,
28
+ templateKey: template.templateKey,
29
+ labels: [...canonicalizeLabels(template.labels)],
30
+ availableSlots: template.availableSlots,
31
+ starting: template.starting,
32
+ running: template.running,
33
+ })),
34
+ );
35
+ });
36
+ }
37
+
38
+ export async function listActiveWorkspaceProvisionerCapabilitySnapshots(params: {
39
+ workspaceId: string;
40
+ windowSeconds: number;
41
+ }): Promise<ProvisionerCapabilitySnapshot[]> {
42
+ const rows = await db()
43
+ .select({snapshot: provisionerCapabilitySnapshots})
44
+ .from(provisionerCapabilitySnapshots)
45
+ .innerJoin(
46
+ provisionerTokens,
47
+ eq(provisionerTokens.id, provisionerCapabilitySnapshots.provisionerId),
48
+ )
49
+ .where(and(...activeWorkspaceProvisionerCapabilityConditions(params)));
50
+
51
+ return rows.map(({snapshot}) => toProvisionerCapabilitySnapshot(snapshot));
52
+ }
53
+
54
+ export async function listStaleWorkspaceProvisionerCapabilitySnapshots(params: {
55
+ workspaceId: string;
56
+ windowSeconds: number;
57
+ }): Promise<ProvisionerCapabilitySnapshot[]> {
58
+ const rows = await db()
59
+ .select({snapshot: provisionerCapabilitySnapshots})
60
+ .from(provisionerCapabilitySnapshots)
61
+ .innerJoin(
62
+ provisionerTokens,
63
+ eq(provisionerTokens.id, provisionerCapabilitySnapshots.provisionerId),
64
+ )
65
+ .where(
66
+ and(
67
+ eq(provisionerCapabilitySnapshots.workspaceId, params.workspaceId),
68
+ or(
69
+ lte(
70
+ provisionerCapabilitySnapshots.advertisedAt,
71
+ sql`now() - (${params.windowSeconds} || ' seconds')::interval`,
72
+ ),
73
+ eq(provisionerTokens.scope, 'installation'),
74
+ isNull(provisionerTokens.workspaceId),
75
+ provisionerTokenIsUnavailable(),
76
+ ),
77
+ ),
78
+ );
79
+
80
+ return rows.map(({snapshot}) => toProvisionerCapabilitySnapshot(snapshot));
81
+ }
82
+
83
+ export async function hasActiveWorkspaceProvisionerCapability(params: {
84
+ workspaceId: string;
85
+ requiredLabels: string[];
86
+ windowSeconds: number;
87
+ }): Promise<boolean> {
88
+ const [row] = await db()
89
+ .select({id: provisionerCapabilitySnapshots.id})
90
+ .from(provisionerCapabilitySnapshots)
91
+ .innerJoin(
92
+ provisionerTokens,
93
+ eq(provisionerTokens.id, provisionerCapabilitySnapshots.provisionerId),
94
+ )
95
+ .where(
96
+ and(
97
+ ...activeWorkspaceProvisionerCapabilityConditions(params),
98
+ arrayContains(provisionerCapabilitySnapshots.labels, [
99
+ ...canonicalizeLabels(params.requiredLabels),
100
+ ]),
101
+ ),
102
+ )
103
+ .limit(1);
104
+
105
+ return row !== undefined;
106
+ }
107
+
108
+ function activeWorkspaceProvisionerCapabilityConditions(params: {
109
+ workspaceId: string;
110
+ windowSeconds: number;
111
+ }) {
112
+ return [
113
+ eq(provisionerCapabilitySnapshots.workspaceId, params.workspaceId),
114
+ eq(provisionerTokens.workspaceId, params.workspaceId),
115
+ eq(provisionerTokens.scope, 'workspace' as const),
116
+ isNull(provisionerTokens.revokedAt),
117
+ or(isNull(provisionerTokens.expiresAt), gt(provisionerTokens.expiresAt, sql`now()`)),
118
+ gt(
119
+ provisionerCapabilitySnapshots.advertisedAt,
120
+ sql`now() - (${params.windowSeconds} || ' seconds')::interval`,
121
+ ),
122
+ ];
123
+ }
124
+
125
+ function provisionerTokenIsUnavailable() {
126
+ return or(
127
+ isNotNull(provisionerTokens.revokedAt),
128
+ and(isNotNull(provisionerTokens.expiresAt), lte(provisionerTokens.expiresAt, sql`now()`)),
129
+ );
130
+ }
@@ -1,5 +1,5 @@
1
1
  import {hashOpaqueToken} from '@shipfox/node-tokens';
2
- import {sql} from 'drizzle-orm';
2
+ import {eq, sql} from 'drizzle-orm';
3
3
  import {db} from '#db/db.js';
4
4
  import {
5
5
  createProvisionerToken,
@@ -9,7 +9,12 @@ import {
9
9
  revokeProvisionerToken,
10
10
  touchProvisionerLastSeen,
11
11
  } from '#db/provisioner-tokens.js';
12
- import {provisionerTokenFactory} from '#test/index.js';
12
+ import {provisionerTokens} from '#db/schema/provisioner-tokens.js';
13
+ import {runnerActivationTokens} from '#db/schema/runner-activation-tokens.js';
14
+ import {runnerBootstrapTokens, runnerControlSessions} from '#db/schema/runner-control-sessions.js';
15
+ import {providerRunners} from '#db/schema/runner-instances.js';
16
+ import {runnerSessions} from '#db/schema/runner-sessions.js';
17
+ import {providerRunnerFactory, provisionerTokenFactory} from '#test/index.js';
13
18
 
14
19
  describe('provisioner token db', () => {
15
20
  it('creates and resolves a token', async () => {
@@ -17,6 +22,7 @@ describe('provisioner token db', () => {
17
22
  const rawToken = 'sf_pt_test-token';
18
23
  const createdByUserId = crypto.randomUUID();
19
24
  const token = await createProvisionerToken({
25
+ scope: 'workspace',
20
26
  workspaceId,
21
27
  hashedToken: hashOpaqueToken(rawToken),
22
28
  prefix: rawToken.slice(0, 12),
@@ -37,6 +43,28 @@ describe('provisioner token db', () => {
37
43
  expect(result).toBeUndefined();
38
44
  });
39
45
 
46
+ it('creates installation factory tokens without a workspace', async () => {
47
+ const token = await provisionerTokenFactory.create({scope: 'installation'});
48
+
49
+ expect(token).toMatchObject({scope: 'installation', workspaceId: null});
50
+ });
51
+
52
+ it('enforces valid scope and workspace combinations in PostgreSQL', async () => {
53
+ const insert = (scope: 'installation' | 'workspace', workspaceId: string | null) =>
54
+ db()
55
+ .insert(provisionerTokens)
56
+ .values({
57
+ scope,
58
+ workspaceId,
59
+ hashedToken: hashOpaqueToken(`${scope}-${workspaceId}`),
60
+ prefix: 'sf_pt_invalid',
61
+ createdByUserId: crypto.randomUUID(),
62
+ });
63
+
64
+ await expect(insert('installation', crypto.randomUUID())).rejects.toThrow();
65
+ await expect(insert('workspace', null)).rejects.toThrow();
66
+ });
67
+
40
68
  it('lists usable tokens for a workspace', async () => {
41
69
  const workspaceId = crypto.randomUUID();
42
70
  const usable = await provisionerTokenFactory.create({workspaceId, name: 'usable'});
@@ -74,6 +102,115 @@ describe('provisioner token db', () => {
74
102
  expect(revoked?.revokedByUserId).toBe(revokedByUserId);
75
103
  });
76
104
 
105
+ it("cascades revocation to the provisioner's unclaimed runner credentials and sessions", async () => {
106
+ const workspaceId = crypto.randomUUID();
107
+ const provisioner = await provisionerTokenFactory.create({workspaceId});
108
+ const runner = await providerRunnerFactory.create({
109
+ workspaceId,
110
+ provisionerId: provisioner.id,
111
+ runnerSessionId: null,
112
+ });
113
+ const future = new Date(Date.now() + 60_000);
114
+ const tokenValue = (name: string) => hashOpaqueToken(`${name}-${crypto.randomUUID()}`);
115
+ await db()
116
+ .insert(runnerBootstrapTokens)
117
+ .values({
118
+ runnerInstanceId: runner.id,
119
+ provisionerId: provisioner.id,
120
+ hashedToken: tokenValue('bootstrap'),
121
+ prefix: 'bootstrap',
122
+ expiresAt: future,
123
+ });
124
+ await db()
125
+ .insert(runnerControlSessions)
126
+ .values({
127
+ runnerInstanceId: runner.id,
128
+ provisionerId: provisioner.id,
129
+ hashedToken: tokenValue('control'),
130
+ prefix: 'control',
131
+ expiresAt: future,
132
+ });
133
+ await db()
134
+ .insert(runnerActivationTokens)
135
+ .values({
136
+ runnerInstanceId: runner.id,
137
+ hashedToken: tokenValue('activation'),
138
+ prefix: 'activation',
139
+ expiresAt: future,
140
+ });
141
+ const [unclaimedSession] = await db()
142
+ .insert(runnerSessions)
143
+ .values({
144
+ workspaceId,
145
+ scope: 'workspace',
146
+ registrationTokenId: crypto.randomUUID(),
147
+ registrationTokenKind: 'activation',
148
+ runnerInstanceId: runner.id,
149
+ provisionerId: provisioner.id,
150
+ providerRunnerId: runner.providerRunnerId,
151
+ labels: ['linux'],
152
+ maxClaims: 1,
153
+ claimsUsed: 0,
154
+ })
155
+ .returning();
156
+ const [claimedSession] = await db()
157
+ .insert(runnerSessions)
158
+ .values({
159
+ workspaceId,
160
+ scope: 'workspace',
161
+ registrationTokenId: crypto.randomUUID(),
162
+ registrationTokenKind: 'activation',
163
+ runnerInstanceId: crypto.randomUUID(),
164
+ provisionerId: provisioner.id,
165
+ providerRunnerId: crypto.randomUUID(),
166
+ labels: ['linux'],
167
+ maxClaims: 1,
168
+ claimsUsed: 1,
169
+ })
170
+ .returning();
171
+ if (!unclaimedSession || !claimedSession) throw new Error('Runner sessions were not created');
172
+
173
+ await revokeProvisionerToken({
174
+ tokenId: provisioner.id,
175
+ workspaceId,
176
+ revokedByUserId: crypto.randomUUID(),
177
+ });
178
+
179
+ const [bootstrap] = await db()
180
+ .select()
181
+ .from(runnerBootstrapTokens)
182
+ .where(eq(runnerBootstrapTokens.runnerInstanceId, runner.id));
183
+ const [control] = await db()
184
+ .select()
185
+ .from(runnerControlSessions)
186
+ .where(eq(runnerControlSessions.runnerInstanceId, runner.id));
187
+ const [activation] = await db()
188
+ .select()
189
+ .from(runnerActivationTokens)
190
+ .where(eq(runnerActivationTokens.runnerInstanceId, runner.id));
191
+ const [terminatedRunner] = await db()
192
+ .select()
193
+ .from(providerRunners)
194
+ .where(eq(providerRunners.id, runner.id));
195
+ const [revokedSession] = await db()
196
+ .select()
197
+ .from(runnerSessions)
198
+ .where(eq(runnerSessions.id, unclaimedSession.id));
199
+ const [preservedSession] = await db()
200
+ .select()
201
+ .from(runnerSessions)
202
+ .where(eq(runnerSessions.id, claimedSession.id));
203
+
204
+ expect(bootstrap?.revokedAt).toBeInstanceOf(Date);
205
+ expect(control).toMatchObject({closeReason: 'provisioner-revoked'});
206
+ expect(control?.closedAt).toBeInstanceOf(Date);
207
+ expect(activation?.revokedAt).toBeInstanceOf(Date);
208
+ expect(terminatedRunner).toMatchObject({state: 'terminated'});
209
+ expect(terminatedRunner?.terminatedAt).toBeInstanceOf(Date);
210
+ expect(revokedSession?.revokedAt).toBeInstanceOf(Date);
211
+ expect(preservedSession?.revokedAt).toBeNull();
212
+ });
213
+
77
214
  it('preserves the original revocation audit fields on repeat revoke', async () => {
78
215
  const workspaceId = crypto.randomUUID();
79
216
  const token = await provisionerTokenFactory.create({workspaceId});
@@ -1,10 +1,19 @@
1
- import {and, desc, eq, gt, isNull, or, sql} from 'drizzle-orm';
2
- import type {ActiveProvisionerToken, ProvisionerToken} from '#core/entities/provisioner-token.js';
1
+ import {and, desc, eq, gt, inArray, isNull, or, sql} from 'drizzle-orm';
2
+ import type {
3
+ ActiveProvisionerToken,
4
+ ProvisionerScope,
5
+ ProvisionerToken,
6
+ } from '#core/entities/provisioner-token.js';
3
7
  import {db} from './db.js';
4
8
  import {provisionerTokens, toProvisionerToken} from './schema/provisioner-tokens.js';
9
+ import {runnerActivationTokens} from './schema/runner-activation-tokens.js';
10
+ import {runnerBootstrapTokens, runnerControlSessions} from './schema/runner-control-sessions.js';
11
+ import {providerRunners} from './schema/runner-instances.js';
12
+ import {runnerSessions} from './schema/runner-sessions.js';
5
13
 
6
14
  export interface CreateProvisionerTokenParams {
7
- workspaceId: string;
15
+ scope: ProvisionerScope;
16
+ workspaceId?: string | undefined;
8
17
  hashedToken: string;
9
18
  prefix: string;
10
19
  createdByUserId: string;
@@ -12,13 +21,68 @@ export interface CreateProvisionerTokenParams {
12
21
  expiresAt?: Date | undefined;
13
22
  }
14
23
 
24
+ async function cascadeProvisionerRevocation(
25
+ tx: Parameters<Parameters<ReturnType<typeof db>['transaction']>[0]>[0],
26
+ provisionerId: string,
27
+ ) {
28
+ await tx
29
+ .update(runnerBootstrapTokens)
30
+ .set({revokedAt: sql`now()`})
31
+ .where(eq(runnerBootstrapTokens.provisionerId, provisionerId));
32
+ await tx
33
+ .update(runnerControlSessions)
34
+ .set({closedAt: sql`now()`, closeReason: 'provisioner-revoked'})
35
+ .where(
36
+ and(
37
+ eq(runnerControlSessions.provisionerId, provisionerId),
38
+ isNull(runnerControlSessions.closedAt),
39
+ ),
40
+ );
41
+ await tx
42
+ .update(runnerActivationTokens)
43
+ .set({revokedAt: sql`now()`})
44
+ .where(
45
+ and(
46
+ isNull(runnerActivationTokens.consumedAt),
47
+ isNull(runnerActivationTokens.revokedAt),
48
+ inArray(
49
+ runnerActivationTokens.runnerInstanceId,
50
+ tx
51
+ .select({id: providerRunners.id})
52
+ .from(providerRunners)
53
+ .where(eq(providerRunners.provisionerId, provisionerId)),
54
+ ),
55
+ ),
56
+ );
57
+ await tx
58
+ .update(providerRunners)
59
+ .set({state: 'terminated', terminatedAt: sql`now()`, updatedAt: sql`now()`})
60
+ .where(
61
+ and(
62
+ eq(providerRunners.provisionerId, provisionerId),
63
+ isNull(providerRunners.runnerSessionId),
64
+ ),
65
+ );
66
+ await tx
67
+ .update(runnerSessions)
68
+ .set({revokedAt: sql`now()`})
69
+ .where(
70
+ and(
71
+ eq(runnerSessions.provisionerId, provisionerId),
72
+ eq(runnerSessions.claimsUsed, 0),
73
+ isNull(runnerSessions.revokedAt),
74
+ ),
75
+ );
76
+ }
77
+
15
78
  export async function createProvisionerToken(
16
79
  params: CreateProvisionerTokenParams,
17
80
  ): Promise<ProvisionerToken> {
18
81
  const rows = await db()
19
82
  .insert(provisionerTokens)
20
83
  .values({
21
- workspaceId: params.workspaceId,
84
+ scope: params.scope,
85
+ workspaceId: params.workspaceId ?? null,
22
86
  hashedToken: params.hashedToken,
23
87
  prefix: params.prefix,
24
88
  createdByUserId: params.createdByUserId,
@@ -32,6 +96,42 @@ export async function createProvisionerToken(
32
96
  return toProvisionerToken(row);
33
97
  }
34
98
 
99
+ export async function revokeInstallationProvisionerToken(params: {
100
+ tokenId: string;
101
+ revokedByUserId: string;
102
+ }): Promise<ProvisionerToken | undefined> {
103
+ const rows = await db().transaction(async (tx) => {
104
+ const rows = await tx
105
+ .update(provisionerTokens)
106
+ .set({revokedAt: new Date(), revokedByUserId: params.revokedByUserId, updatedAt: new Date()})
107
+ .where(
108
+ and(
109
+ eq(provisionerTokens.id, params.tokenId),
110
+ eq(provisionerTokens.scope, 'installation'),
111
+ isNull(provisionerTokens.revokedAt),
112
+ ),
113
+ )
114
+ .returning();
115
+ if (rows[0]) {
116
+ await cascadeProvisionerRevocation(tx, params.tokenId);
117
+ }
118
+ return rows;
119
+ });
120
+
121
+ const row = rows[0];
122
+ if (row) return toProvisionerToken(row);
123
+
124
+ const existingRows = await db()
125
+ .select()
126
+ .from(provisionerTokens)
127
+ .where(
128
+ and(eq(provisionerTokens.id, params.tokenId), eq(provisionerTokens.scope, 'installation')),
129
+ )
130
+ .limit(1);
131
+ const existingRow = existingRows[0];
132
+ return existingRow ? toProvisionerToken(existingRow) : undefined;
133
+ }
134
+
35
135
  export async function listUsableProvisionerTokensByWorkspaceId(
36
136
  workspaceId: string,
37
137
  ): Promise<ProvisionerToken[]> {
@@ -42,6 +142,7 @@ export async function listUsableProvisionerTokensByWorkspaceId(
42
142
  .where(
43
143
  and(
44
144
  eq(provisionerTokens.workspaceId, workspaceId),
145
+ eq(provisionerTokens.scope, 'workspace'),
45
146
  isNull(provisionerTokens.revokedAt),
46
147
  or(isNull(provisionerTokens.expiresAt), gt(provisionerTokens.expiresAt, now)),
47
148
  ),
@@ -70,17 +171,22 @@ export async function revokeProvisionerToken(params: {
70
171
  workspaceId: string;
71
172
  revokedByUserId: string;
72
173
  }): Promise<ProvisionerToken | undefined> {
73
- const rows = await db()
74
- .update(provisionerTokens)
75
- .set({revokedAt: new Date(), revokedByUserId: params.revokedByUserId, updatedAt: new Date()})
76
- .where(
77
- and(
78
- eq(provisionerTokens.id, params.tokenId),
79
- eq(provisionerTokens.workspaceId, params.workspaceId),
80
- isNull(provisionerTokens.revokedAt),
81
- ),
82
- )
83
- .returning();
174
+ const rows = await db().transaction(async (tx) => {
175
+ const rows = await tx
176
+ .update(provisionerTokens)
177
+ .set({revokedAt: new Date(), revokedByUserId: params.revokedByUserId, updatedAt: new Date()})
178
+ .where(
179
+ and(
180
+ eq(provisionerTokens.id, params.tokenId),
181
+ eq(provisionerTokens.workspaceId, params.workspaceId),
182
+ eq(provisionerTokens.scope, 'workspace'),
183
+ isNull(provisionerTokens.revokedAt),
184
+ ),
185
+ )
186
+ .returning();
187
+ if (rows[0]) await cascadeProvisionerRevocation(tx, params.tokenId);
188
+ return rows;
189
+ });
84
190
 
85
191
  const row = rows[0];
86
192
  if (row) return toProvisionerToken(row);
@@ -92,6 +198,7 @@ export async function revokeProvisionerToken(params: {
92
198
  and(
93
199
  eq(provisionerTokens.id, params.tokenId),
94
200
  eq(provisionerTokens.workspaceId, params.workspaceId),
201
+ eq(provisionerTokens.scope, 'workspace'),
95
202
  ),
96
203
  )
97
204
  .limit(1);
@@ -129,6 +236,7 @@ export async function listActiveProvisionerTokens(params: {
129
236
  .where(
130
237
  and(
131
238
  eq(provisionerTokens.workspaceId, params.workspaceId),
239
+ eq(provisionerTokens.scope, 'workspace'),
132
240
  isNull(provisionerTokens.revokedAt),
133
241
  or(isNull(provisionerTokens.expiresAt), gt(provisionerTokens.expiresAt, sql`now()`)),
134
242
  sql`${provisionerTokens.lastSeenAt} > now() - (${params.windowSeconds} || ' seconds')::interval`,