@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
@@ -14,17 +14,25 @@ import {
14
14
  extractBearerToken,
15
15
  } from '@shipfox/node-fastify';
16
16
  import {vi} from '@shipfox/vitest/vi';
17
+ import {eq} from 'drizzle-orm';
17
18
  import type {FastifyInstance, FastifyRequest} from 'fastify';
18
19
  import {db} from '#db/db.js';
20
+ import {provisionerCapabilitySnapshots} from '#db/schema/provisioner-capability-snapshots.js';
19
21
  import {runningJobExecutions} from '#db/schema/running-job-executions.js';
20
22
  import {
21
- provisionedRunnerCountDivergenceCount,
22
- provisionedRunnerTerminateIntentIssuedCount,
23
+ providerRunnerCountDivergenceCount,
24
+ providerRunnerTerminateIntentIssuedCount,
23
25
  } from '#metrics/instance.js';
24
- import {pendingJobFactory, provisionedRunnerFactory, runnerSessionFactory} from '#test/index.js';
25
- import {runnerRoutes} from './index.js';
26
+ import {
27
+ pendingJobFactory,
28
+ providerRunnerFactory,
29
+ runnerSessionFactory,
30
+ runnersTestAuthClient,
31
+ } from '#test/index.js';
32
+ import {createRunnerRoutes} from './index.js';
26
33
 
27
34
  const VALID_PROVISIONER_TOKEN = 'valid-provisioner-token';
35
+ const INSTALLATION_PROVISIONER_TOKEN = 'installation-provisioner-token';
28
36
 
29
37
  const passthroughAuth = (name: string): AuthMethod => ({
30
38
  name,
@@ -41,9 +49,13 @@ describe('POST /provisioners/demand/poll', () => {
41
49
  authenticate: (request: FastifyRequest) => {
42
50
  const rawToken = extractBearerToken(request.headers.authorization);
43
51
  if (rawToken !== VALID_PROVISIONER_TOKEN) {
52
+ if (rawToken === INSTALLATION_PROVISIONER_TOKEN) {
53
+ setProvisionerContext(request, {scope: 'installation', provisionerTokenId});
54
+ return Promise.resolve();
55
+ }
44
56
  throw new ClientError('Invalid provisioner token', 'unauthorized', {status: 401});
45
57
  }
46
- setProvisionerContext(request, {workspaceId, provisionerTokenId});
58
+ setProvisionerContext(request, {scope: 'workspace', workspaceId, provisionerTokenId});
47
59
  return Promise.resolve();
48
60
  },
49
61
  };
@@ -57,7 +69,7 @@ describe('POST /provisioners/demand/poll', () => {
57
69
  passthroughAuth(AUTH_LEASED_JOB),
58
70
  fakeProvisionerAuth,
59
71
  ],
60
- routes: runnerRoutes,
72
+ routes: createRunnerRoutes(runnersTestAuthClient),
61
73
  swagger: false,
62
74
  });
63
75
  await app.ready();
@@ -86,12 +98,24 @@ describe('POST /provisioners/demand/poll', () => {
86
98
  expect(res.json()).toMatchObject({
87
99
  stats: [{labels: ['linux'], queued: 1, reserved: 1}],
88
100
  reservations: [{labels: ['linux'], count: 1}],
89
- terminate_provisioned_runner_ids: [],
101
+ terminate_provider_runner_ids: [],
90
102
  });
91
103
  expect(res.json().reservations[0].reservation_id).toEqual(expect.any(String));
92
104
  expect(res.json().reservations[0].expires_at).toEqual(expect.any(String));
93
105
  });
94
106
 
107
+ it('rejects installation provisioner credentials from workspace demand polling', async () => {
108
+ const res = await app.inject({
109
+ method: 'POST',
110
+ url: '/provisioners/demand/poll',
111
+ headers: {authorization: `Bearer ${INSTALLATION_PROVISIONER_TOKEN}`},
112
+ payload: body({max_reservations: 1}),
113
+ });
114
+
115
+ expect(res.statusCode).toBe(403);
116
+ expect(res.json().code).toBe('forbidden');
117
+ });
118
+
95
119
  it('returns stats without reservations in observe-only mode', async () => {
96
120
  await pendingJobFactory.create({workspaceId, requiredLabels: ['linux']});
97
121
 
@@ -118,19 +142,46 @@ describe('POST /provisioners/demand/poll', () => {
118
142
  expect(res.json()).toMatchObject({
119
143
  stats: [{labels: ['linux'], queued: 1, reserved: 0}],
120
144
  reservations: [],
121
- terminate_provisioned_runner_ids: [],
145
+ terminate_provider_runner_ids: [],
122
146
  });
147
+ const snapshots = await db()
148
+ .select()
149
+ .from(provisionerCapabilitySnapshots)
150
+ .where(eq(provisionerCapabilitySnapshots.provisionerId, provisionerTokenId));
151
+ expect(snapshots).toEqual([
152
+ expect.objectContaining({
153
+ workspaceId,
154
+ labels: ['linux'],
155
+ availableSlots: 1,
156
+ starting: 0,
157
+ running: 1,
158
+ }),
159
+ ]);
160
+
161
+ const withdrawal = await app.inject({
162
+ method: 'POST',
163
+ url: '/provisioners/demand/poll',
164
+ headers: {authorization: `Bearer ${VALID_PROVISIONER_TOKEN}`},
165
+ payload: {wait_seconds: 0, max_reservations: 0, templates: []},
166
+ });
167
+ const clearedSnapshots = await db()
168
+ .select()
169
+ .from(provisionerCapabilitySnapshots)
170
+ .where(eq(provisionerCapabilitySnapshots.provisionerId, provisionerTokenId));
171
+
172
+ expect(withdrawal.statusCode).toBe(200);
173
+ expect(clearedSnapshots).toEqual([]);
123
174
  });
124
175
 
125
176
  it('returns terminate intent ids for active provisioned runners with cancelled latest jobs', async () => {
126
- await provisionedRunnerFactory.create({
177
+ await providerRunnerFactory.create({
127
178
  workspaceId,
128
179
  provisionerId: provisionerTokenId,
129
- provisionedRunnerId: 'provisioned-runner-1',
180
+ providerRunnerId: 'provisioned-runner-1',
130
181
  state: 'running',
131
182
  });
132
183
  await insertRunningJob({
133
- provisionedRunnerId: 'provisioned-runner-1',
184
+ providerRunnerId: 'provisioned-runner-1',
134
185
  cancellationRequestedAt: new Date('2025-01-01T00:01:00.000Z'),
135
186
  });
136
187
 
@@ -156,29 +207,29 @@ describe('POST /provisioners/demand/poll', () => {
156
207
  expect(res.statusCode).toBe(200);
157
208
  expect(res.json()).toMatchObject({
158
209
  reservations: [],
159
- terminate_provisioned_runner_ids: ['provisioned-runner-1'],
210
+ terminate_provider_runner_ids: ['provisioned-runner-1'],
160
211
  });
161
212
  });
162
213
 
163
214
  it('records count divergence and terminate-intent metrics for the returned poll result', async () => {
164
- const divergenceSpy = vi.spyOn(provisionedRunnerCountDivergenceCount, 'add');
165
- const intentSpy = vi.spyOn(provisionedRunnerTerminateIntentIssuedCount, 'add');
166
- await provisionedRunnerFactory.create({
215
+ const divergenceSpy = vi.spyOn(providerRunnerCountDivergenceCount, 'add');
216
+ const intentSpy = vi.spyOn(providerRunnerTerminateIntentIssuedCount, 'add');
217
+ await providerRunnerFactory.create({
167
218
  workspaceId,
168
219
  provisionerId: provisionerTokenId,
169
- provisionedRunnerId: 'provisioned-runner-1',
220
+ providerRunnerId: 'provisioned-runner-1',
170
221
  templateKey: 'linux',
171
222
  state: 'running',
172
223
  });
173
- await provisionedRunnerFactory.create({
224
+ await providerRunnerFactory.create({
174
225
  workspaceId,
175
226
  provisionerId: provisionerTokenId,
176
- provisionedRunnerId: 'provisioned-runner-2',
227
+ providerRunnerId: 'provisioned-runner-2',
177
228
  templateKey: 'linux',
178
229
  state: 'running',
179
230
  });
180
231
  await insertRunningJob({
181
- provisionedRunnerId: 'provisioned-runner-1',
232
+ providerRunnerId: 'provisioned-runner-1',
182
233
  cancellationRequestedAt: new Date('2025-01-01T00:01:00.000Z'),
183
234
  });
184
235
  const divergenceCallsBefore = divergenceSpy.mock.calls.length;
@@ -206,7 +257,7 @@ describe('POST /provisioners/demand/poll', () => {
206
257
  expect(res.statusCode).toBe(200);
207
258
  expect(res.json()).toMatchObject({
208
259
  reservations: [],
209
- terminate_provisioned_runner_ids: ['provisioned-runner-1'],
260
+ terminate_provider_runner_ids: ['provisioned-runner-1'],
210
261
  });
211
262
  const divergenceCalls = divergenceSpy.mock.calls
212
263
  .slice(divergenceCallsBefore)
@@ -296,7 +347,7 @@ describe('POST /provisioners/demand/poll', () => {
296
347
  }
297
348
 
298
349
  async function insertRunningJob(params: {
299
- provisionedRunnerId: string;
350
+ providerRunnerId: string;
300
351
  cancellationRequestedAt?: Date | null;
301
352
  }) {
302
353
  const runnerSession = await runnerSessionFactory.create({workspaceId});
@@ -312,7 +363,7 @@ describe('POST /provisioners/demand/poll', () => {
312
363
  projectId: crypto.randomUUID(),
313
364
  runnerSessionId: runnerSession.id,
314
365
  provisionerId: provisionerTokenId,
315
- provisionedRunnerId: params.provisionedRunnerId,
366
+ providerRunnerId: params.providerRunnerId,
316
367
  requiredLabels: ['linux'],
317
368
  runnerLabels: ['linux'],
318
369
  startedAt: new Date('2025-01-01T00:00:00.000Z'),
@@ -321,3 +372,91 @@ describe('POST /provisioners/demand/poll', () => {
321
372
  });
322
373
  }
323
374
  });
375
+
376
+ describe('POST /provisioners/demand/poll with installation provisioning configured', () => {
377
+ let app: FastifyInstance;
378
+ let workspaceId: string;
379
+ let provisionerTokenId: string;
380
+
381
+ const fakeProvisionerAuth: AuthMethod = {
382
+ name: AUTH_PROVISIONER_TOKEN,
383
+ authenticate: (request: FastifyRequest) => {
384
+ const rawToken = extractBearerToken(request.headers.authorization);
385
+ if (rawToken === INSTALLATION_PROVISIONER_TOKEN) {
386
+ setProvisionerContext(request, {scope: 'installation', provisionerTokenId});
387
+ return Promise.resolve();
388
+ }
389
+ setProvisionerContext(request, {scope: 'workspace', workspaceId, provisionerTokenId});
390
+ return Promise.resolve();
391
+ },
392
+ };
393
+
394
+ beforeAll(async () => {
395
+ app = await createApp({
396
+ auth: [
397
+ passthroughAuth(AUTH_USER),
398
+ passthroughAuth(AUTH_RUNNER_REGISTRATION_TOKEN),
399
+ passthroughAuth(AUTH_RUNNER_SESSION),
400
+ passthroughAuth(AUTH_LEASED_JOB),
401
+ fakeProvisionerAuth,
402
+ ],
403
+ routes: createRunnerRoutes(runnersTestAuthClient, {
404
+ installationProvisioning: {
405
+ policy: {filterEligibleWorkspaceIds: vi.fn().mockResolvedValue(new Set())},
406
+ },
407
+ }),
408
+ swagger: false,
409
+ });
410
+ await app.ready();
411
+ });
412
+
413
+ afterAll(async () => {
414
+ await closeApp();
415
+ });
416
+
417
+ beforeEach(() => {
418
+ workspaceId = crypto.randomUUID();
419
+ provisionerTokenId = crypto.randomUUID();
420
+ });
421
+
422
+ it('returns opaque empty demand when no workspace is eligible', async () => {
423
+ const res = await app.inject({
424
+ method: 'POST',
425
+ url: '/provisioners/demand/poll',
426
+ headers: {authorization: `Bearer ${INSTALLATION_PROVISIONER_TOKEN}`},
427
+ payload: body({max_reservations: 1}),
428
+ });
429
+
430
+ expect(res.statusCode).toBe(200);
431
+ expect(res.json()).toEqual({stats: [], reservations: [], terminate_provider_runner_ids: []});
432
+ });
433
+
434
+ it('still serves workspace provisioner credentials', async () => {
435
+ await pendingJobFactory.create({workspaceId, requiredLabels: ['linux']});
436
+
437
+ const res = await app.inject({
438
+ method: 'POST',
439
+ url: '/provisioners/demand/poll',
440
+ headers: {authorization: `Bearer ${VALID_PROVISIONER_TOKEN}`},
441
+ payload: body({max_reservations: 1}),
442
+ });
443
+
444
+ expect(res.statusCode).toBe(200);
445
+ });
446
+
447
+ function body(params: {max_reservations: number}) {
448
+ return {
449
+ wait_seconds: 0,
450
+ max_reservations: params.max_reservations,
451
+ templates: [
452
+ {
453
+ template_key: 'linux',
454
+ labels: ['linux'],
455
+ available_slots: 1,
456
+ starting: 0,
457
+ running: 0,
458
+ },
459
+ ],
460
+ };
461
+ }
462
+ });
@@ -1,56 +1,116 @@
1
- import {requireProvisionerContext} from '@shipfox/api-auth-context';
1
+ import {
2
+ requireProvisionerContext,
3
+ requireWorkspaceProvisionerContext,
4
+ } from '@shipfox/api-auth-context';
5
+ import type {PollDemandTemplateDto} from '@shipfox/api-runners-dto';
2
6
  import {pollDemandBodySchema, pollDemandResponseSchema} from '@shipfox/api-runners-dto';
3
- import {defineRoute} from '@shipfox/node-fastify';
7
+ import {ClientError, defineRoute} from '@shipfox/node-fastify';
4
8
  import {config} from '#config.js';
5
9
  import {pollDemand, releaseReservationGrants} from '#core/demand.js';
6
- import type {ReservationGrant} from '#db/reservations.js';
10
+ import {publishWorkspaceProvisionerCapabilitySnapshot} from '#db/provisioner-capability-snapshots.js';
11
+ import {
12
+ listQueuedDemandWorkspaceIds,
13
+ pollInstallationDemandAndReserve,
14
+ type ReservationGrant,
15
+ type ReservationTemplate,
16
+ } from '#db/reservations.js';
17
+ import type {CreateRunnersModuleOptions} from '#installation-provisioning.js';
7
18
  import {toPollDemandResponseDto} from '#presentation/dto/index.js';
8
19
 
9
20
  const TERMINATE_INTENT_LIMIT = 1000;
10
21
 
11
- export const pollDemandRoute = defineRoute({
12
- method: 'POST',
13
- path: '/demand/poll',
14
- description: 'Poll aggregate runner demand and reserve capacity for a provisioner',
15
- schema: {
16
- body: pollDemandBodySchema,
17
- response: {
18
- 200: pollDemandResponseSchema,
22
+ function toReservationTemplates(templates: PollDemandTemplateDto[]): ReservationTemplate[] {
23
+ return templates.map((template) => ({
24
+ templateKey: template.template_key,
25
+ labels: template.labels,
26
+ availableSlots: template.available_slots,
27
+ starting: template.starting,
28
+ running: template.running,
29
+ }));
30
+ }
31
+
32
+ export function createPollDemandRoute(options: CreateRunnersModuleOptions = {}) {
33
+ return defineRoute({
34
+ method: 'POST',
35
+ path: '/demand/poll',
36
+ description: 'Poll aggregate runner demand and reserve capacity for a provisioner',
37
+ schema: {
38
+ body: pollDemandBodySchema,
39
+ response: {
40
+ 200: pollDemandResponseSchema,
41
+ },
19
42
  },
20
- },
21
- handler: async (request, reply) => {
22
- const {provisionerTokenId, workspaceId} = requireProvisionerContext(request);
23
- const abortController = new AbortController();
24
- let responseFinished = false;
25
- let responseReservations: ReservationGrant[] = [];
26
- reply.raw.on('finish', () => {
27
- responseFinished = true;
28
- });
29
- reply.raw.on('close', () => {
30
- if (!responseFinished) {
31
- abortController.abort();
32
- void releaseReservationGrants(responseReservations).catch(() => undefined);
43
+ handler: async (request, reply) => {
44
+ const abortController = new AbortController();
45
+ let responseFinished = false;
46
+ let responseReservations: ReservationGrant[] = [];
47
+ reply.raw.on('finish', () => {
48
+ responseFinished = true;
49
+ });
50
+ reply.raw.on('close', () => {
51
+ if (!responseFinished) {
52
+ abortController.abort();
53
+ void releaseReservationGrants(responseReservations).catch(() => undefined);
54
+ }
55
+ });
56
+ const provisionerContext = requireProvisionerContext(request);
57
+ if (provisionerContext.scope === 'installation') {
58
+ if (!options.installationProvisioning) {
59
+ throw new ClientError(
60
+ 'Installation provisioner credentials are not accepted on this host',
61
+ 'forbidden',
62
+ {
63
+ status: 403,
64
+ },
65
+ );
66
+ }
67
+ const candidateWorkspaceIds = await listQueuedDemandWorkspaceIds();
68
+ const eligibleWorkspaceIds =
69
+ await options.installationProvisioning.policy.filterEligibleWorkspaceIds(
70
+ candidateWorkspaceIds,
71
+ );
72
+ const templates = toReservationTemplates(request.body.templates);
73
+ const result = await pollInstallationDemandAndReserve({
74
+ provisionerId: provisionerContext.provisionerTokenId,
75
+ maxReservations: request.body.max_reservations,
76
+ ttlSeconds: config.RESERVATION_TTL_SECONDS,
77
+ templates,
78
+ capabilityWindowSeconds: config.PROVISIONER_ACTIVE_WINDOW_SECONDS,
79
+ eligibleWorkspaceIds,
80
+ signal: abortController.signal,
81
+ onReservations: (reservations) => {
82
+ responseReservations.push(...reservations);
83
+ },
84
+ });
85
+ return toPollDemandResponseDto({
86
+ ...result,
87
+ terminateRunnerInstanceIds: [],
88
+ });
33
89
  }
34
- });
90
+ const {provisionerTokenId, workspaceId} = requireWorkspaceProvisionerContext(request);
91
+
92
+ const templates = toReservationTemplates(request.body.templates);
93
+ await publishWorkspaceProvisionerCapabilitySnapshot({
94
+ workspaceId,
95
+ provisionerId: provisionerTokenId,
96
+ templates,
97
+ });
35
98
 
36
- const result = await pollDemand({
37
- workspaceId,
38
- provisionerId: provisionerTokenId,
39
- maxReservations: request.body.max_reservations,
40
- waitSeconds: request.body.wait_seconds,
41
- ttlSeconds: config.RESERVATION_TTL_SECONDS,
42
- terminateIntentLimit: TERMINATE_INTENT_LIMIT,
43
- templates: request.body.templates.map((template) => ({
44
- templateKey: template.template_key,
45
- labels: template.labels,
46
- availableSlots: template.available_slots,
47
- starting: template.starting,
48
- running: template.running,
49
- })),
50
- signal: abortController.signal,
51
- });
52
- responseReservations = result.reservations;
99
+ const result = await pollDemand({
100
+ workspaceId,
101
+ provisionerId: provisionerTokenId,
102
+ maxReservations: request.body.max_reservations,
103
+ waitSeconds: request.body.wait_seconds,
104
+ ttlSeconds: config.RESERVATION_TTL_SECONDS,
105
+ terminateIntentLimit: TERMINATE_INTENT_LIMIT,
106
+ templates,
107
+ signal: abortController.signal,
108
+ });
109
+ responseReservations = result.reservations;
110
+
111
+ return toPollDemandResponseDto(result);
112
+ },
113
+ });
114
+ }
53
115
 
54
- return toPollDemandResponseDto(result);
55
- },
56
- });
116
+ export const pollDemandRoute = createPollDemandRoute();
@@ -3,8 +3,12 @@ import type {AuthMethod} from '@shipfox/node-fastify';
3
3
  import {closeApp, createApp} from '@shipfox/node-fastify';
4
4
  import {generateOpaqueToken} from '@shipfox/node-tokens';
5
5
  import type {FastifyInstance} from 'fastify';
6
+ import {createInstallationProvisionerToken} from '#core/provisioner-tokens.js';
6
7
  import * as provisionerTokenDb from '#db/provisioner-tokens.js';
7
- import {revokeProvisionerToken} from '#db/provisioner-tokens.js';
8
+ import {
9
+ revokeInstallationProvisionerToken,
10
+ revokeProvisionerToken,
11
+ } from '#db/provisioner-tokens.js';
8
12
  import {createProvisionerTokenAuthMethod} from '#presentation/auth/index.js';
9
13
  import {provisionerTokenFactory} from '#test/index.js';
10
14
  import {provisionerRoutes} from './index.js';
@@ -47,11 +51,41 @@ describe('provisioner me route', () => {
47
51
  });
48
52
 
49
53
  expect(res.statusCode).toBe(200);
50
- expect(res.json()).toEqual({id: token.id, workspace_id: workspaceId});
54
+ expect(res.json()).toEqual({id: token.id, scope: 'workspace', workspace_id: workspaceId});
51
55
  const touched = await provisionerTokenDb.resolveProvisionerTokenByHash(token.hashedToken);
52
56
  expect(touched?.lastSeenAt).toBeInstanceOf(Date);
53
57
  });
54
58
 
59
+ it('returns an installation identity without a workspace', async () => {
60
+ const token = await createInstallationProvisionerToken({createdByUserId: crypto.randomUUID()});
61
+
62
+ const res = await app.inject({
63
+ method: 'GET',
64
+ url: '/provisioners/me',
65
+ headers: {authorization: `Bearer ${token.rawToken}`},
66
+ });
67
+
68
+ expect(res.statusCode).toBe(200);
69
+ expect(res.json()).toEqual({id: token.token.id, scope: 'installation'});
70
+ });
71
+
72
+ it('rejects a revoked installation provisioner token', async () => {
73
+ const token = await createInstallationProvisionerToken({createdByUserId: crypto.randomUUID()});
74
+ await revokeInstallationProvisionerToken({
75
+ tokenId: token.token.id,
76
+ revokedByUserId: crypto.randomUUID(),
77
+ });
78
+
79
+ const res = await app.inject({
80
+ method: 'GET',
81
+ url: '/provisioners/me',
82
+ headers: {authorization: `Bearer ${token.rawToken}`},
83
+ });
84
+
85
+ expect(res.statusCode).toBe(401);
86
+ expect(res.json().code).toBe('provisioner-token-revoked');
87
+ });
88
+
55
89
  it('does not reject valid auth when the last-seen write fails', async () => {
56
90
  await closeApp();
57
91
  const workspaceId = crypto.randomUUID();
@@ -74,7 +108,7 @@ describe('provisioner me route', () => {
74
108
  });
75
109
 
76
110
  expect(res.statusCode).toBe(200);
77
- expect(res.json()).toEqual({id: token.id, workspace_id: workspaceId});
111
+ expect(res.json()).toEqual({id: token.id, scope: 'workspace', workspace_id: workspaceId});
78
112
  });
79
113
 
80
114
  it('returns 401 without an authorization header', async () => {