@shipfox/api-runners 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/.turbo/turbo-type$colon$emit.log +1 -0
  3. package/.turbo/turbo-type.log +1 -0
  4. package/CHANGELOG.md +187 -0
  5. package/LICENSE +21 -0
  6. package/dist/config.d.ts +30 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +208 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/core/demand.d.ts +33 -0
  11. package/dist/core/demand.d.ts.map +1 -0
  12. package/dist/core/demand.js +169 -0
  13. package/dist/core/demand.js.map +1 -0
  14. package/dist/core/entities/ephemeral-registration-token.d.ts +14 -0
  15. package/dist/core/entities/ephemeral-registration-token.d.ts.map +1 -0
  16. package/dist/core/entities/ephemeral-registration-token.js +3 -0
  17. package/dist/core/entities/ephemeral-registration-token.js.map +1 -0
  18. package/dist/core/entities/manual-registration-token.d.ts +12 -0
  19. package/dist/core/entities/manual-registration-token.d.ts.map +1 -0
  20. package/dist/core/entities/manual-registration-token.js +3 -0
  21. package/dist/core/entities/manual-registration-token.js.map +1 -0
  22. package/dist/core/entities/provisioned-runner.d.ts +24 -0
  23. package/dist/core/entities/provisioned-runner.d.ts.map +1 -0
  24. package/dist/core/entities/provisioned-runner.js +3 -0
  25. package/dist/core/entities/provisioned-runner.js.map +1 -0
  26. package/dist/core/entities/provisioner-token.d.ts +18 -0
  27. package/dist/core/entities/provisioner-token.d.ts.map +1 -0
  28. package/dist/core/entities/provisioner-token.js +3 -0
  29. package/dist/core/entities/provisioner-token.js.map +1 -0
  30. package/dist/core/entities/reservation.d.ts +10 -0
  31. package/dist/core/entities/reservation.d.ts.map +1 -0
  32. package/dist/core/entities/reservation.js +3 -0
  33. package/dist/core/entities/reservation.js.map +1 -0
  34. package/dist/core/entities/runner-session.d.ts +18 -0
  35. package/dist/core/entities/runner-session.d.ts.map +1 -0
  36. package/dist/core/entities/runner-session.js +3 -0
  37. package/dist/core/entities/runner-session.js.map +1 -0
  38. package/dist/core/ephemeral-registration-tokens.d.ts +31 -0
  39. package/dist/core/ephemeral-registration-tokens.d.ts.map +1 -0
  40. package/dist/core/ephemeral-registration-tokens.js +61 -0
  41. package/dist/core/ephemeral-registration-tokens.js.map +1 -0
  42. package/dist/core/errors.d.ts +62 -0
  43. package/dist/core/errors.d.ts.map +1 -0
  44. package/dist/core/errors.js +92 -0
  45. package/dist/core/errors.js.map +1 -0
  46. package/dist/core/index.d.ts +14 -0
  47. package/dist/core/index.d.ts.map +1 -0
  48. package/dist/core/index.js +10 -0
  49. package/dist/core/index.js.map +1 -0
  50. package/dist/core/job-executions.d.ts +14 -0
  51. package/dist/core/job-executions.d.ts.map +1 -0
  52. package/dist/core/job-executions.js +37 -0
  53. package/dist/core/job-executions.js.map +1 -0
  54. package/dist/core/maintenance-policy.d.ts +2 -0
  55. package/dist/core/maintenance-policy.d.ts.map +1 -0
  56. package/dist/core/maintenance-policy.js +3 -0
  57. package/dist/core/maintenance-policy.js.map +1 -0
  58. package/dist/core/maintenance.d.ts +33 -0
  59. package/dist/core/maintenance.d.ts.map +1 -0
  60. package/dist/core/maintenance.js +55 -0
  61. package/dist/core/maintenance.js.map +1 -0
  62. package/dist/core/manual-registration-tokens.d.ts +17 -0
  63. package/dist/core/manual-registration-tokens.d.ts.map +1 -0
  64. package/dist/core/manual-registration-tokens.js +28 -0
  65. package/dist/core/manual-registration-tokens.js.map +1 -0
  66. package/dist/core/provisioned-runners.d.ts +67 -0
  67. package/dist/core/provisioned-runners.d.ts.map +1 -0
  68. package/dist/core/provisioned-runners.js +162 -0
  69. package/dist/core/provisioned-runners.js.map +1 -0
  70. package/dist/core/provisioner-tokens.d.ts +20 -0
  71. package/dist/core/provisioner-tokens.d.ts.map +1 -0
  72. package/dist/core/provisioner-tokens.js +36 -0
  73. package/dist/core/provisioner-tokens.js.map +1 -0
  74. package/dist/core/rate-limit.d.ts +35 -0
  75. package/dist/core/rate-limit.d.ts.map +1 -0
  76. package/dist/core/rate-limit.js +74 -0
  77. package/dist/core/rate-limit.js.map +1 -0
  78. package/dist/core/runner-sessions.d.ts +26 -0
  79. package/dist/core/runner-sessions.d.ts.map +1 -0
  80. package/dist/core/runner-sessions.js +41 -0
  81. package/dist/core/runner-sessions.js.map +1 -0
  82. package/dist/core/runner-tool-capabilities.d.ts +24 -0
  83. package/dist/core/runner-tool-capabilities.d.ts.map +1 -0
  84. package/dist/core/runner-tool-capabilities.js +43 -0
  85. package/dist/core/runner-tool-capabilities.js.map +1 -0
  86. package/dist/db/db.d.ts +4478 -0
  87. package/dist/db/db.d.ts.map +1 -0
  88. package/dist/db/db.js +36 -0
  89. package/dist/db/db.js.map +1 -0
  90. package/dist/db/ephemeral-registration-tokens.d.ts +47 -0
  91. package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -0
  92. package/dist/db/ephemeral-registration-tokens.js +170 -0
  93. package/dist/db/ephemeral-registration-tokens.js.map +1 -0
  94. package/dist/db/index.d.ts +19 -0
  95. package/dist/db/index.d.ts.map +1 -0
  96. package/dist/db/index.js +15 -0
  97. package/dist/db/index.js.map +1 -0
  98. package/dist/db/job-executions.d.ts +128 -0
  99. package/dist/db/job-executions.d.ts.map +1 -0
  100. package/dist/db/job-executions.js +354 -0
  101. package/dist/db/job-executions.js.map +1 -0
  102. package/dist/db/manual-registration-tokens.d.ts +16 -0
  103. package/dist/db/manual-registration-tokens.d.ts.map +1 -0
  104. package/dist/db/manual-registration-tokens.js +37 -0
  105. package/dist/db/manual-registration-tokens.js.map +1 -0
  106. package/dist/db/provisioned-runners.d.ts +79 -0
  107. package/dist/db/provisioned-runners.d.ts.map +1 -0
  108. package/dist/db/provisioned-runners.js +466 -0
  109. package/dist/db/provisioned-runners.js.map +1 -0
  110. package/dist/db/provisioner-tokens.d.ts +26 -0
  111. package/dist/db/provisioner-tokens.d.ts.map +1 -0
  112. package/dist/db/provisioner-tokens.js +54 -0
  113. package/dist/db/provisioner-tokens.js.map +1 -0
  114. package/dist/db/rate-limits.d.ts +18 -0
  115. package/dist/db/rate-limits.d.ts.map +1 -0
  116. package/dist/db/rate-limits.js +44 -0
  117. package/dist/db/rate-limits.js.map +1 -0
  118. package/dist/db/reservations.d.ts +48 -0
  119. package/dist/db/reservations.d.ts.map +1 -0
  120. package/dist/db/reservations.js +162 -0
  121. package/dist/db/reservations.js.map +1 -0
  122. package/dist/db/runner-sessions.d.ts +18 -0
  123. package/dist/db/runner-sessions.d.ts.map +1 -0
  124. package/dist/db/runner-sessions.js +38 -0
  125. package/dist/db/runner-sessions.js.map +1 -0
  126. package/dist/db/schema/common.d.ts +2 -0
  127. package/dist/db/schema/common.d.ts.map +1 -0
  128. package/dist/db/schema/common.js +4 -0
  129. package/dist/db/schema/common.js.map +1 -0
  130. package/dist/db/schema/ephemeral-registration-tokens.d.ts +199 -0
  131. package/dist/db/schema/ephemeral-registration-tokens.d.ts.map +1 -0
  132. package/dist/db/schema/ephemeral-registration-tokens.js +45 -0
  133. package/dist/db/schema/ephemeral-registration-tokens.js.map +1 -0
  134. package/dist/db/schema/manual-registration-tokens.d.ts +165 -0
  135. package/dist/db/schema/manual-registration-tokens.d.ts.map +1 -0
  136. package/dist/db/schema/manual-registration-tokens.js +40 -0
  137. package/dist/db/schema/manual-registration-tokens.js.map +1 -0
  138. package/dist/db/schema/outbox.d.ts +195 -0
  139. package/dist/db/schema/outbox.d.ts.map +1 -0
  140. package/dist/db/schema/outbox.js +5 -0
  141. package/dist/db/schema/outbox.js.map +1 -0
  142. package/dist/db/schema/pending-job-executions.d.ts +189 -0
  143. package/dist/db/schema/pending-job-executions.d.ts.map +1 -0
  144. package/dist/db/schema/pending-job-executions.js +21 -0
  145. package/dist/db/schema/pending-job-executions.js.map +1 -0
  146. package/dist/db/schema/provisioned-runners.d.ts +379 -0
  147. package/dist/db/schema/provisioned-runners.d.ts.map +1 -0
  148. package/dist/db/schema/provisioned-runners.js +83 -0
  149. package/dist/db/schema/provisioned-runners.js.map +1 -0
  150. package/dist/db/schema/provisioner-tokens.d.ts +216 -0
  151. package/dist/db/schema/provisioner-tokens.d.ts.map +1 -0
  152. package/dist/db/schema/provisioner-tokens.js +48 -0
  153. package/dist/db/schema/provisioner-tokens.js.map +1 -0
  154. package/dist/db/schema/rate-limits.d.ts +145 -0
  155. package/dist/db/schema/rate-limits.d.ts.map +1 -0
  156. package/dist/db/schema/rate-limits.js +25 -0
  157. package/dist/db/schema/rate-limits.js.map +1 -0
  158. package/dist/db/schema/reservations.d.ts +157 -0
  159. package/dist/db/schema/reservations.d.ts.map +1 -0
  160. package/dist/db/schema/reservations.js +34 -0
  161. package/dist/db/schema/reservations.js.map +1 -0
  162. package/dist/db/schema/runner-sessions.d.ts +298 -0
  163. package/dist/db/schema/runner-sessions.d.ts.map +1 -0
  164. package/dist/db/schema/runner-sessions.js +61 -0
  165. package/dist/db/schema/runner-sessions.js.map +1 -0
  166. package/dist/db/schema/running-job-executions.d.ts +334 -0
  167. package/dist/db/schema/running-job-executions.d.ts.map +1 -0
  168. package/dist/db/schema/running-job-executions.js +41 -0
  169. package/dist/db/schema/running-job-executions.js.map +1 -0
  170. package/dist/index.d.ts +6 -0
  171. package/dist/index.d.ts.map +1 -0
  172. package/dist/index.js +56 -0
  173. package/dist/index.js.map +1 -0
  174. package/dist/metrics/index.d.ts +3 -0
  175. package/dist/metrics/index.d.ts.map +1 -0
  176. package/dist/metrics/index.js +4 -0
  177. package/dist/metrics/index.js.map +1 -0
  178. package/dist/metrics/instance.d.ts +34 -0
  179. package/dist/metrics/instance.d.ts.map +1 -0
  180. package/dist/metrics/instance.js +60 -0
  181. package/dist/metrics/instance.js.map +1 -0
  182. package/dist/metrics/service.d.ts +2 -0
  183. package/dist/metrics/service.d.ts.map +1 -0
  184. package/dist/metrics/service.js +21 -0
  185. package/dist/metrics/service.js.map +1 -0
  186. package/dist/presentation/auth/index.d.ts +3 -0
  187. package/dist/presentation/auth/index.d.ts.map +1 -0
  188. package/dist/presentation/auth/index.js +4 -0
  189. package/dist/presentation/auth/index.js.map +1 -0
  190. package/dist/presentation/auth/provisioner-token-auth.d.ts +3 -0
  191. package/dist/presentation/auth/provisioner-token-auth.d.ts.map +1 -0
  192. package/dist/presentation/auth/provisioner-token-auth.js +81 -0
  193. package/dist/presentation/auth/provisioner-token-auth.js.map +1 -0
  194. package/dist/presentation/auth/runner-registration-token-auth.d.ts +17 -0
  195. package/dist/presentation/auth/runner-registration-token-auth.d.ts.map +1 -0
  196. package/dist/presentation/auth/runner-registration-token-auth.js +77 -0
  197. package/dist/presentation/auth/runner-registration-token-auth.js.map +1 -0
  198. package/dist/presentation/dto/active-provisioner.d.ts +8 -0
  199. package/dist/presentation/dto/active-provisioner.d.ts.map +1 -0
  200. package/dist/presentation/dto/active-provisioner.js +10 -0
  201. package/dist/presentation/dto/active-provisioner.js.map +1 -0
  202. package/dist/presentation/dto/index.d.ts +7 -0
  203. package/dist/presentation/dto/index.d.ts.map +1 -0
  204. package/dist/presentation/dto/index.js +8 -0
  205. package/dist/presentation/dto/index.js.map +1 -0
  206. package/dist/presentation/dto/manual-registration-token.d.ts +12 -0
  207. package/dist/presentation/dto/manual-registration-token.d.ts.map +1 -0
  208. package/dist/presentation/dto/manual-registration-token.js +14 -0
  209. package/dist/presentation/dto/manual-registration-token.js.map +1 -0
  210. package/dist/presentation/dto/mint-registration-tokens.d.ts +5 -0
  211. package/dist/presentation/dto/mint-registration-tokens.d.ts.map +1 -0
  212. package/dist/presentation/dto/mint-registration-tokens.js +19 -0
  213. package/dist/presentation/dto/mint-registration-tokens.js.map +1 -0
  214. package/dist/presentation/dto/poll-demand.d.ts +4 -0
  215. package/dist/presentation/dto/poll-demand.d.ts.map +1 -0
  216. package/dist/presentation/dto/poll-demand.js +19 -0
  217. package/dist/presentation/dto/poll-demand.js.map +1 -0
  218. package/dist/presentation/dto/provisioned-runners.d.ts +38 -0
  219. package/dist/presentation/dto/provisioned-runners.d.ts.map +1 -0
  220. package/dist/presentation/dto/provisioned-runners.js +44 -0
  221. package/dist/presentation/dto/provisioned-runners.js.map +1 -0
  222. package/dist/presentation/dto/provisioner-token.d.ts +15 -0
  223. package/dist/presentation/dto/provisioner-token.d.ts.map +1 -0
  224. package/dist/presentation/dto/provisioner-token.js +17 -0
  225. package/dist/presentation/dto/provisioner-token.js.map +1 -0
  226. package/dist/presentation/index.d.ts +4 -0
  227. package/dist/presentation/index.d.ts.map +1 -0
  228. package/dist/presentation/index.js +5 -0
  229. package/dist/presentation/index.js.map +1 -0
  230. package/dist/presentation/routes/create-manual-registration-token.d.ts +2 -0
  231. package/dist/presentation/routes/create-manual-registration-token.d.ts.map +1 -0
  232. package/dist/presentation/routes/create-manual-registration-token.js +44 -0
  233. package/dist/presentation/routes/create-manual-registration-token.js.map +1 -0
  234. package/dist/presentation/routes/create-provisioner-token.d.ts +2 -0
  235. package/dist/presentation/routes/create-provisioner-token.d.ts.map +1 -0
  236. package/dist/presentation/routes/create-provisioner-token.js +42 -0
  237. package/dist/presentation/routes/create-provisioner-token.js.map +1 -0
  238. package/dist/presentation/routes/get-provisioner-me.d.ts +2 -0
  239. package/dist/presentation/routes/get-provisioner-me.d.ts.map +1 -0
  240. package/dist/presentation/routes/get-provisioner-me.js +22 -0
  241. package/dist/presentation/routes/get-provisioner-me.js.map +1 -0
  242. package/dist/presentation/routes/heartbeat.d.ts +2 -0
  243. package/dist/presentation/routes/heartbeat.d.ts.map +1 -0
  244. package/dist/presentation/routes/heartbeat.js +64 -0
  245. package/dist/presentation/routes/heartbeat.js.map +1 -0
  246. package/dist/presentation/routes/index.d.ts +4 -0
  247. package/dist/presentation/routes/index.d.ts.map +1 -0
  248. package/dist/presentation/routes/index.js +97 -0
  249. package/dist/presentation/routes/index.js.map +1 -0
  250. package/dist/presentation/routes/list-active-provisioners.d.ts +2 -0
  251. package/dist/presentation/routes/list-active-provisioners.d.ts.map +1 -0
  252. package/dist/presentation/routes/list-active-provisioners.js +32 -0
  253. package/dist/presentation/routes/list-active-provisioners.js.map +1 -0
  254. package/dist/presentation/routes/list-active-runners.d.ts +2 -0
  255. package/dist/presentation/routes/list-active-runners.d.ts.map +1 -0
  256. package/dist/presentation/routes/list-active-runners.js +32 -0
  257. package/dist/presentation/routes/list-active-runners.js.map +1 -0
  258. package/dist/presentation/routes/list-manual-registration-tokens.d.ts +2 -0
  259. package/dist/presentation/routes/list-manual-registration-tokens.d.ts.map +1 -0
  260. package/dist/presentation/routes/list-manual-registration-tokens.js +32 -0
  261. package/dist/presentation/routes/list-manual-registration-tokens.js.map +1 -0
  262. package/dist/presentation/routes/list-provisioner-tokens.d.ts +2 -0
  263. package/dist/presentation/routes/list-provisioner-tokens.d.ts.map +1 -0
  264. package/dist/presentation/routes/list-provisioner-tokens.js +32 -0
  265. package/dist/presentation/routes/list-provisioner-tokens.js.map +1 -0
  266. package/dist/presentation/routes/mint-registration-tokens.d.ts +2 -0
  267. package/dist/presentation/routes/mint-registration-tokens.d.ts.map +1 -0
  268. package/dist/presentation/routes/mint-registration-tokens.js +74 -0
  269. package/dist/presentation/routes/mint-registration-tokens.js.map +1 -0
  270. package/dist/presentation/routes/poll-demand.d.ts +2 -0
  271. package/dist/presentation/routes/poll-demand.d.ts.map +1 -0
  272. package/dist/presentation/routes/poll-demand.js +53 -0
  273. package/dist/presentation/routes/poll-demand.js.map +1 -0
  274. package/dist/presentation/routes/rate-limit.d.ts +4 -0
  275. package/dist/presentation/routes/rate-limit.d.ts.map +1 -0
  276. package/dist/presentation/routes/rate-limit.js +94 -0
  277. package/dist/presentation/routes/rate-limit.js.map +1 -0
  278. package/dist/presentation/routes/reconcile-provisioned-runners.d.ts +2 -0
  279. package/dist/presentation/routes/reconcile-provisioned-runners.d.ts.map +1 -0
  280. package/dist/presentation/routes/reconcile-provisioned-runners.js +27 -0
  281. package/dist/presentation/routes/reconcile-provisioned-runners.js.map +1 -0
  282. package/dist/presentation/routes/register.d.ts +2 -0
  283. package/dist/presentation/routes/register.d.ts.map +1 -0
  284. package/dist/presentation/routes/register.js +63 -0
  285. package/dist/presentation/routes/register.js.map +1 -0
  286. package/dist/presentation/routes/report-provisioned-runners.d.ts +2 -0
  287. package/dist/presentation/routes/report-provisioned-runners.d.ts.map +1 -0
  288. package/dist/presentation/routes/report-provisioned-runners.js +37 -0
  289. package/dist/presentation/routes/report-provisioned-runners.js.map +1 -0
  290. package/dist/presentation/routes/request-job.d.ts +2 -0
  291. package/dist/presentation/routes/request-job.d.ts.map +1 -0
  292. package/dist/presentation/routes/request-job.js +45 -0
  293. package/dist/presentation/routes/request-job.js.map +1 -0
  294. package/dist/presentation/routes/revoke-manual-registration-token.d.ts +2 -0
  295. package/dist/presentation/routes/revoke-manual-registration-token.d.ts.map +1 -0
  296. package/dist/presentation/routes/revoke-manual-registration-token.js +42 -0
  297. package/dist/presentation/routes/revoke-manual-registration-token.js.map +1 -0
  298. package/dist/presentation/routes/revoke-provisioner-token.d.ts +2 -0
  299. package/dist/presentation/routes/revoke-provisioner-token.d.ts.map +1 -0
  300. package/dist/presentation/routes/revoke-provisioner-token.js +44 -0
  301. package/dist/presentation/routes/revoke-provisioner-token.js.map +1 -0
  302. package/dist/presentation/routes/workspace-membership.d.ts +6 -0
  303. package/dist/presentation/routes/workspace-membership.d.ts.map +1 -0
  304. package/dist/presentation/routes/workspace-membership.js +6 -0
  305. package/dist/presentation/routes/workspace-membership.js.map +1 -0
  306. package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.d.ts +3 -0
  307. package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.d.ts.map +1 -0
  308. package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.js +14 -0
  309. package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.js.map +1 -0
  310. package/dist/temporal/activities/index.d.ts +9 -0
  311. package/dist/temporal/activities/index.d.ts.map +1 -0
  312. package/dist/temporal/activities/index.js +12 -0
  313. package/dist/temporal/activities/index.js.map +1 -0
  314. package/dist/temporal/activities/maintenance-activities.d.ts +31 -0
  315. package/dist/temporal/activities/maintenance-activities.d.ts.map +1 -0
  316. package/dist/temporal/activities/maintenance-activities.js +18 -0
  317. package/dist/temporal/activities/maintenance-activities.js.map +1 -0
  318. package/dist/temporal/constants.d.ts +2 -0
  319. package/dist/temporal/constants.d.ts.map +1 -0
  320. package/dist/temporal/constants.js +3 -0
  321. package/dist/temporal/constants.js.map +1 -0
  322. package/dist/temporal/workflows/index.d.ts +2 -0
  323. package/dist/temporal/workflows/index.d.ts.map +1 -0
  324. package/dist/temporal/workflows/index.js +3 -0
  325. package/dist/temporal/workflows/index.js.map +1 -0
  326. package/dist/temporal/workflows/stuck-job-detector.d.ts +2 -0
  327. package/dist/temporal/workflows/stuck-job-detector.d.ts.map +1 -0
  328. package/dist/temporal/workflows/stuck-job-detector.js +61 -0
  329. package/dist/temporal/workflows/stuck-job-detector.js.map +1 -0
  330. package/dist/tsconfig.test.tsbuildinfo +1 -0
  331. package/drizzle/0000_initial.sql +185 -0
  332. package/drizzle/meta/0000_snapshot.json +1582 -0
  333. package/drizzle/meta/_journal.json +13 -0
  334. package/drizzle.config.ts +7 -0
  335. package/package.json +82 -0
  336. package/src/config.test.ts +374 -0
  337. package/src/config.ts +330 -0
  338. package/src/core/demand.test.ts +301 -0
  339. package/src/core/demand.ts +239 -0
  340. package/src/core/entities/ephemeral-registration-token.ts +13 -0
  341. package/src/core/entities/manual-registration-token.ts +11 -0
  342. package/src/core/entities/provisioned-runner.ts +30 -0
  343. package/src/core/entities/provisioner-token.ts +16 -0
  344. package/src/core/entities/reservation.ts +9 -0
  345. package/src/core/entities/runner-session.ts +18 -0
  346. package/src/core/ephemeral-registration-tokens.test.ts +108 -0
  347. package/src/core/ephemeral-registration-tokens.ts +112 -0
  348. package/src/core/errors.ts +128 -0
  349. package/src/core/index.ts +56 -0
  350. package/src/core/job-executions.ts +47 -0
  351. package/src/core/maintenance-policy.ts +1 -0
  352. package/src/core/maintenance.test.ts +251 -0
  353. package/src/core/maintenance.ts +74 -0
  354. package/src/core/manual-registration-tokens.test.ts +44 -0
  355. package/src/core/manual-registration-tokens.ts +51 -0
  356. package/src/core/provisioned-runners.test.ts +101 -0
  357. package/src/core/provisioned-runners.ts +301 -0
  358. package/src/core/provisioner-tokens.test.ts +61 -0
  359. package/src/core/provisioner-tokens.ts +61 -0
  360. package/src/core/rate-limit.test.ts +158 -0
  361. package/src/core/rate-limit.ts +121 -0
  362. package/src/core/runner-sessions.test.ts +209 -0
  363. package/src/core/runner-sessions.ts +66 -0
  364. package/src/core/runner-tool-capabilities.test.ts +141 -0
  365. package/src/core/runner-tool-capabilities.ts +75 -0
  366. package/src/db/db.ts +38 -0
  367. package/src/db/ephemeral-registration-tokens.test.ts +332 -0
  368. package/src/db/ephemeral-registration-tokens.ts +348 -0
  369. package/src/db/index.ts +90 -0
  370. package/src/db/job-executions.test.ts +1500 -0
  371. package/src/db/job-executions.ts +647 -0
  372. package/src/db/manual-registration-tokens.test.ts +99 -0
  373. package/src/db/manual-registration-tokens.ts +87 -0
  374. package/src/db/provisioned-runners.test.ts +1812 -0
  375. package/src/db/provisioned-runners.ts +886 -0
  376. package/src/db/provisioner-tokens.test.ts +133 -0
  377. package/src/db/provisioner-tokens.ts +142 -0
  378. package/src/db/rate-limits.test.ts +74 -0
  379. package/src/db/rate-limits.ts +69 -0
  380. package/src/db/reservations.test.ts +399 -0
  381. package/src/db/reservations.ts +296 -0
  382. package/src/db/runner-sessions.test.ts +153 -0
  383. package/src/db/runner-sessions.ts +97 -0
  384. package/src/db/schema/common.ts +3 -0
  385. package/src/db/schema/ephemeral-registration-tokens.ts +58 -0
  386. package/src/db/schema/manual-registration-tokens.ts +40 -0
  387. package/src/db/schema/outbox.ts +4 -0
  388. package/src/db/schema/pending-job-executions.ts +25 -0
  389. package/src/db/schema/provisioned-runners.ts +89 -0
  390. package/src/db/schema/provisioner-tokens.ts +50 -0
  391. package/src/db/schema/rate-limits.ts +27 -0
  392. package/src/db/schema/reservations.ts +38 -0
  393. package/src/db/schema/runner-sessions.ts +77 -0
  394. package/src/db/schema/running-job-executions.ts +50 -0
  395. package/src/index.ts +63 -0
  396. package/src/metrics/index.ts +13 -0
  397. package/src/metrics/instance.ts +115 -0
  398. package/src/metrics/service.ts +22 -0
  399. package/src/presentation/auth/index.ts +6 -0
  400. package/src/presentation/auth/provisioner-token-auth.ts +76 -0
  401. package/src/presentation/auth/runner-registration-token-auth.ts +105 -0
  402. package/src/presentation/dto/active-provisioner.ts +15 -0
  403. package/src/presentation/dto/index.ts +13 -0
  404. package/src/presentation/dto/manual-registration-token.ts +23 -0
  405. package/src/presentation/dto/mint-registration-tokens.ts +35 -0
  406. package/src/presentation/dto/poll-demand.ts +20 -0
  407. package/src/presentation/dto/provisioned-runners.ts +88 -0
  408. package/src/presentation/dto/provisioner-token.ts +29 -0
  409. package/src/presentation/index.ts +6 -0
  410. package/src/presentation/routes/create-manual-registration-token.ts +43 -0
  411. package/src/presentation/routes/create-provisioner-token.ts +41 -0
  412. package/src/presentation/routes/get-provisioner-me.ts +22 -0
  413. package/src/presentation/routes/heartbeat.test.ts +325 -0
  414. package/src/presentation/routes/heartbeat.ts +80 -0
  415. package/src/presentation/routes/index.ts +86 -0
  416. package/src/presentation/routes/list-active-provisioners.ts +25 -0
  417. package/src/presentation/routes/list-active-runners.test.ts +366 -0
  418. package/src/presentation/routes/list-active-runners.ts +25 -0
  419. package/src/presentation/routes/list-manual-registration-tokens.ts +25 -0
  420. package/src/presentation/routes/list-provisioner-tokens.ts +25 -0
  421. package/src/presentation/routes/manual-registration-tokens.test.ts +213 -0
  422. package/src/presentation/routes/mint-registration-tokens.test.ts +453 -0
  423. package/src/presentation/routes/mint-registration-tokens.ts +82 -0
  424. package/src/presentation/routes/poll-demand-release.test.ts +80 -0
  425. package/src/presentation/routes/poll-demand.test.ts +323 -0
  426. package/src/presentation/routes/poll-demand.ts +56 -0
  427. package/src/presentation/routes/provisioner-me.test.ts +172 -0
  428. package/src/presentation/routes/provisioner-tokens.test.ts +219 -0
  429. package/src/presentation/routes/rate-limit.ts +115 -0
  430. package/src/presentation/routes/reconcile-provisioned-runners.test.ts +380 -0
  431. package/src/presentation/routes/reconcile-provisioned-runners.ts +30 -0
  432. package/src/presentation/routes/register.test.ts +453 -0
  433. package/src/presentation/routes/register.ts +75 -0
  434. package/src/presentation/routes/report-provisioned-runners.test.ts +249 -0
  435. package/src/presentation/routes/report-provisioned-runners.ts +40 -0
  436. package/src/presentation/routes/request-job.test.ts +222 -0
  437. package/src/presentation/routes/request-job.ts +47 -0
  438. package/src/presentation/routes/revoke-manual-registration-token.ts +35 -0
  439. package/src/presentation/routes/revoke-provisioner-token.ts +37 -0
  440. package/src/presentation/routes/workspace-membership.ts +9 -0
  441. package/src/presentation/subscribers/on-workflows-job-execution-timed-out.test.ts +147 -0
  442. package/src/presentation/subscribers/on-workflows-job-execution-timed-out.ts +17 -0
  443. package/src/temporal/activities/index.ts +17 -0
  444. package/src/temporal/activities/maintenance-activities.test.ts +104 -0
  445. package/src/temporal/activities/maintenance-activities.ts +41 -0
  446. package/src/temporal/constants.ts +1 -0
  447. package/src/temporal/workflows/index.ts +1 -0
  448. package/src/temporal/workflows/stuck-job-detector.test.ts +93 -0
  449. package/src/temporal/workflows/stuck-job-detector.ts +67 -0
  450. package/test/env.ts +11 -0
  451. package/test/factories/ephemeral-registration-token.ts +41 -0
  452. package/test/factories/manual-registration-token.ts +37 -0
  453. package/test/factories/pending-job.ts +44 -0
  454. package/test/factories/provisioned-runner.ts +78 -0
  455. package/test/factories/provisioner-token.ts +41 -0
  456. package/test/factories/reservation.ts +33 -0
  457. package/test/factories/runner-session.ts +32 -0
  458. package/test/globalSetup.ts +23 -0
  459. package/test/index.ts +16 -0
  460. package/test/setup.ts +17 -0
  461. package/tsconfig.build.json +9 -0
  462. package/tsconfig.build.tsbuildinfo +1 -0
  463. package/tsconfig.json +3 -0
  464. package/tsconfig.test.json +8 -0
  465. package/vitest.config.ts +14 -0
@@ -0,0 +1,50 @@
1
+ import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';
2
+ import {sql} from 'drizzle-orm';
3
+ import {check, index, text, timestamp, uuid} from 'drizzle-orm/pg-core';
4
+ import {pgTable} from './common.js';
5
+ import {runnerSessions} from './runner-sessions.js';
6
+
7
+ export const runningJobExecutions = pgTable(
8
+ 'running_jobs',
9
+ {
10
+ id: uuidv7PrimaryKey(),
11
+ workspaceId: uuid('workspace_id').notNull(),
12
+ workflowRunId: uuid('workflow_run_id').notNull(),
13
+ workflowRunAttemptId: uuid('workflow_run_attempt_id').notNull(),
14
+ jobId: uuid('job_id').notNull(),
15
+ jobExecutionId: uuid('job_execution_id').notNull().unique(),
16
+ projectId: uuid('project_id').notNull(),
17
+ runnerSessionId: uuid('runner_session_id')
18
+ .notNull()
19
+ .references(() => runnerSessions.id),
20
+ provisionerId: uuid('provisioner_id'),
21
+ provisionedRunnerId: text('provisioned_runner_id'),
22
+ requiredLabels: text('required_labels').array().notNull(),
23
+ runnerLabels: text('runner_labels').array().notNull(),
24
+ startedAt: timestamp('started_at', {withTimezone: true}).notNull().defaultNow(),
25
+ firstHeartbeatAt: timestamp('first_heartbeat_at', {withTimezone: true}),
26
+ lastHeartbeatAt: timestamp('last_heartbeat_at', {withTimezone: true}).notNull().defaultNow(),
27
+ cancellationRequestedAt: timestamp('cancellation_requested_at', {withTimezone: true}),
28
+ },
29
+ (table) => [
30
+ index('runners_running_jobs_no_first_heartbeat_started_idx')
31
+ .on(table.startedAt)
32
+ .where(sql`"first_heartbeat_at" IS NULL`),
33
+ index('runners_running_jobs_last_heartbeat_at_idx').on(table.lastHeartbeatAt),
34
+ index('runners_running_jobs_provisioned_runner_started_idx')
35
+ .on(table.workspaceId, table.provisionerId, table.provisionedRunnerId, table.startedAt.desc())
36
+ .where(sql`"provisioner_id" IS NOT NULL`),
37
+ index('runners_running_jobs_cancellation_requested_idx')
38
+ .on(table.workspaceId, table.provisionerId, table.provisionedRunnerId)
39
+ .where(sql`"cancellation_requested_at" IS NOT NULL`),
40
+ index('runners_running_jobs_job_id_idx').on(table.jobId),
41
+ index('runners_running_jobs_runner_session_id_idx').on(table.runnerSessionId),
42
+ check(
43
+ 'runners_running_jobs_link_ck',
44
+ sql`(${table.provisionerId} IS NULL) = (${table.provisionedRunnerId} IS NULL)`,
45
+ ),
46
+ ],
47
+ );
48
+
49
+ export type RunningJobExecutionDb = typeof runningJobExecutions.$inferSelect;
50
+ export type RunningJobExecutionInsertDb = typeof runningJobExecutions.$inferInsert;
package/src/index.ts ADDED
@@ -0,0 +1,63 @@
1
+ import {dirname, resolve} from 'node:path';
2
+ import {fileURLToPath} from 'node:url';
3
+ import {runnersEventSchemas} from '@shipfox/api-runners-dto';
4
+ import {
5
+ WORKFLOWS_JOB_EXECUTION_TIMED_OUT,
6
+ type WorkflowsEventMapDto,
7
+ } from '@shipfox/api-workflows-dto';
8
+ import {type ShipfoxModule, subscriberFactory} from '@shipfox/node-module';
9
+ import {db, migrationsPath, runnersOutbox} from '#db/index.js';
10
+ import {registerRunnersServiceMetrics} from '#metrics/index.js';
11
+ import {
12
+ createProvisionerTokenAuthMethod,
13
+ createRunnerRegistrationTokenAuthMethod,
14
+ onWorkflowsJobExecutionTimedOut,
15
+ routes,
16
+ } from '#presentation/index.js';
17
+ import {createRunnersMaintenanceActivities} from '#temporal/activities/index.js';
18
+ import {RUNNERS_MAINTENANCE_TASK_QUEUE} from '#temporal/constants.js';
19
+
20
+ export {
21
+ type MintEphemeralRegistrationTokenParams,
22
+ type MintEphemeralRegistrationTokenResult,
23
+ type MintEphemeralRegistrationTokensBatchParams,
24
+ type MintEphemeralRegistrationTokensBatchProvisionedRunner,
25
+ type MintEphemeralRegistrationTokensBatchResult,
26
+ mintEphemeralRegistrationToken,
27
+ mintEphemeralRegistrationTokensBatch,
28
+ } from '#core/ephemeral-registration-tokens.js';
29
+ export {
30
+ type EffectiveRunnerToolCapabilitiesResult,
31
+ getEffectiveRunnerToolCapabilities,
32
+ unadvertisedRunnerTools,
33
+ } from '#core/runner-tool-capabilities.js';
34
+ export {
35
+ cancelRunnerJobs,
36
+ type EnqueueJobExecutionParams,
37
+ enqueueJobExecution,
38
+ isJobLeaseActive,
39
+ releaseJobExecution,
40
+ } from '#db/index.js';
41
+
42
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
43
+ const workflowsPath = resolve(packageRoot, 'dist/temporal/workflows/index.js');
44
+
45
+ const subscriber = subscriberFactory<WorkflowsEventMapDto>();
46
+
47
+ export const runnersModule: ShipfoxModule = {
48
+ name: 'runners',
49
+ database: {db, migrationsPath},
50
+ auth: [createRunnerRegistrationTokenAuthMethod(), createProvisionerTokenAuthMethod()],
51
+ routes,
52
+ metrics: registerRunnersServiceMetrics,
53
+ publishers: [{name: 'runners', table: runnersOutbox, db, eventSchemas: runnersEventSchemas}],
54
+ subscribers: [subscriber(WORKFLOWS_JOB_EXECUTION_TIMED_OUT, onWorkflowsJobExecutionTimedOut)],
55
+ workers: [
56
+ {
57
+ taskQueue: RUNNERS_MAINTENANCE_TASK_QUEUE,
58
+ workflowsPath,
59
+ activities: createRunnersMaintenanceActivities,
60
+ workflows: [{name: 'stuckJobDetector', id: 'stuck-job-detector', cronSchedule: '* * * * *'}],
61
+ },
62
+ ],
63
+ };
@@ -0,0 +1,13 @@
1
+ export {
2
+ jobExecutionClaimedCount,
3
+ jobExecutionEnqueuedCount,
4
+ jobExecutionLeaseExpiredCount,
5
+ provisionedRunnerAbsentTerminatedCount,
6
+ provisionedRunnerCountDivergenceCount,
7
+ provisionedRunnerReconcileCallCount,
8
+ provisionedRunnerTerminateIntentHonoredCount,
9
+ provisionedRunnerTerminateIntentIssuedCount,
10
+ recordRunnersRateLimitCheck,
11
+ recordRunnersRateLimitPruneFailure,
12
+ } from './instance.js';
13
+ export {registerRunnersServiceMetrics} from './service.js';
@@ -0,0 +1,115 @@
1
+ import {instanceMetrics} from '@shipfox/node-opentelemetry';
2
+
3
+ const meter = instanceMetrics.getMeter('runners');
4
+
5
+ export const jobExecutionEnqueuedCount = meter.createCounter<Record<string, never>>(
6
+ 'runners_job_execution_enqueued',
7
+ {
8
+ description: 'Job executions added to the pending queue',
9
+ },
10
+ );
11
+
12
+ export const jobExecutionClaimedCount = meter.createCounter<{outcome: 'claimed' | 'empty'}>(
13
+ 'runners_job_execution_claimed',
14
+ {description: 'Job execution claim attempts by outcome'},
15
+ );
16
+
17
+ export const jobExecutionLeaseExpiredCount = meter.createCounter<Record<string, never>>(
18
+ 'runners_job_execution_lease_expired',
19
+ {description: 'Job execution leases reaped after passing the heartbeat threshold'},
20
+ );
21
+
22
+ export const provisionedRunnerReportCount = meter.createCounter<{
23
+ state: 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'terminated';
24
+ }>('runners_provisioned_runner_reported', {
25
+ description: 'Provisioned runner lifecycle reports accepted by state',
26
+ });
27
+
28
+ export const provisionedRunnerReapedCount = meter.createCounter<Record<string, never>>(
29
+ 'runners_provisioned_runner_reaped',
30
+ {
31
+ description: 'Stale provisioned runners marked failed by backend maintenance',
32
+ },
33
+ );
34
+
35
+ export const provisionedRunnerCountDivergenceCount = meter.createCounter<{
36
+ template_key?: string;
37
+ state: 'starting' | 'running';
38
+ direction: 'backend-higher' | 'advertised-higher';
39
+ }>('runners_provisioned_runner_count_divergence', {
40
+ description:
41
+ 'Absolute difference between provisioner-advertised and backend-observed provisioned runner counts',
42
+ });
43
+
44
+ export const provisionedRunnerReconcileCallCount = meter.createCounter<Record<string, never>>(
45
+ 'runners_provisioned_runner_reconcile_called',
46
+ {description: 'Provisioned runner reconcile calls completed successfully'},
47
+ );
48
+
49
+ export const provisionedRunnerAbsentTerminatedCount = meter.createCounter<Record<string, never>>(
50
+ 'runners_provisioned_runner_absent_terminated',
51
+ {
52
+ description:
53
+ 'Owned provisioned runners marked terminated because they were absent from reconcile',
54
+ },
55
+ );
56
+
57
+ export const provisionedRunnerTerminateIntentIssuedCount = meter.createCounter<{
58
+ surface: 'poll-demand' | 'reconcile';
59
+ reason: 'job-cancelled' | 'terminal-state';
60
+ }>('runners_provisioned_runner_terminate_intent_issued', {
61
+ description: 'Provisioned runner terminate intents returned to provisioners',
62
+ });
63
+
64
+ export const provisionedRunnerTerminateIntentHonoredCount = meter.createCounter<{
65
+ reason: 'job-cancelled';
66
+ }>('runners_provisioned_runner_terminate_intent_honored', {
67
+ description: 'Provisioned runner terminate intents honored by first transition to terminated',
68
+ });
69
+
70
+ export const reservationReleasedCount = meter.createCounter<Record<string, never>>(
71
+ 'runners_reservation_released',
72
+ {description: 'Reservation units released from terminal provisioned runner reports'},
73
+ );
74
+
75
+ export type RunnersRateLimitAction = 'provisioner-mint' | 'ephemeral-register';
76
+ export type RunnersRateLimitScope = 'provisioner' | 'ephemeral-token';
77
+ export type RunnersRateLimitOutcome = 'allowed' | 'blocked' | 'unavailable';
78
+
79
+ const rateLimitCheckCount = meter.createCounter<{
80
+ action: RunnersRateLimitAction;
81
+ scope: RunnersRateLimitScope;
82
+ outcome: RunnersRateLimitOutcome;
83
+ }>('runners_rate_limit_checks', {
84
+ description: 'Runners rate limit checks by action, scope, and outcome',
85
+ });
86
+
87
+ const rateLimitPruneFailureCount = meter.createCounter('runners_rate_limit_prune_failures', {
88
+ description: 'Runners rate limit prune failures',
89
+ });
90
+
91
+ function recordMetric(record: () => void): void {
92
+ try {
93
+ record();
94
+ } catch {
95
+ // Metrics must not affect runner or provisioner request outcomes.
96
+ }
97
+ }
98
+
99
+ export function recordRunnersRateLimitCheck(params: {
100
+ action: RunnersRateLimitAction;
101
+ scope: RunnersRateLimitScope;
102
+ outcome: RunnersRateLimitOutcome;
103
+ }): void {
104
+ recordMetric(() =>
105
+ rateLimitCheckCount.add(1, {
106
+ action: params.action,
107
+ scope: params.scope,
108
+ outcome: params.outcome,
109
+ }),
110
+ );
111
+ }
112
+
113
+ export function recordRunnersRateLimitPruneFailure(): void {
114
+ recordMetric(() => rateLimitPruneFailureCount.add(1));
115
+ }
@@ -0,0 +1,22 @@
1
+ import {getServiceMetricsProvider} from '@shipfox/node-opentelemetry';
2
+ import {getJobExecutionQueueDepth} from '#db/job-executions.js';
3
+
4
+ export function registerRunnersServiceMetrics(): void {
5
+ const meter = getServiceMetricsProvider().getMeter('runners');
6
+
7
+ const pendingJobExecutions = meter.createObservableGauge('runners_pending_job_executions', {
8
+ description: 'Job executions currently waiting in the queue to be claimed',
9
+ });
10
+ const runningJobExecutions = meter.createObservableGauge('runners_running_job_executions', {
11
+ description: 'Job executions currently claimed by a runner and in progress',
12
+ });
13
+
14
+ meter.addBatchObservableCallback(
15
+ async (observer) => {
16
+ const depth = await getJobExecutionQueueDepth();
17
+ observer.observe(pendingJobExecutions, depth.pendingJobExecutions);
18
+ observer.observe(runningJobExecutions, depth.runningJobExecutions);
19
+ },
20
+ [pendingJobExecutions, runningJobExecutions],
21
+ );
22
+ }
@@ -0,0 +1,6 @@
1
+ export {createProvisionerTokenAuthMethod} from './provisioner-token-auth.js';
2
+ export {
3
+ createRunnerRegistrationTokenAuthMethod,
4
+ getRunnerContext,
5
+ type RunnerRegistrationContext,
6
+ } from './runner-registration-token-auth.js';
@@ -0,0 +1,76 @@
1
+ import {
2
+ AUTH_PROVISIONER_TOKEN,
3
+ type ProvisionerContext,
4
+ setProvisionerContext,
5
+ } from '@shipfox/api-auth-context';
6
+ import {type AuthMethod, ClientError, extractBearerToken} from '@shipfox/node-fastify';
7
+ import {logger} from '@shipfox/node-opentelemetry';
8
+ import {extractDisplayPrefix, getTokenType, hashOpaqueToken} from '@shipfox/node-tokens';
9
+ import {config} from '#config.js';
10
+ import {resolveProvisionerTokenByHash, touchProvisionerLastSeen} from '#db/provisioner-tokens.js';
11
+
12
+ type AuthFailureReason = 'missing' | 'type' | 'not-found' | 'revoked' | 'expired';
13
+
14
+ export function createProvisionerTokenAuthMethod(): AuthMethod {
15
+ return {
16
+ name: AUTH_PROVISIONER_TOKEN,
17
+ authenticate: async (request) => {
18
+ const rawToken = extractBearerToken(request.headers.authorization);
19
+ if (!rawToken) {
20
+ logAuthFailure({reason: 'missing'});
21
+ throw new ClientError('Missing or invalid Authorization header', 'unauthorized', {
22
+ status: 401,
23
+ });
24
+ }
25
+
26
+ if (getTokenType(rawToken) !== 'provisionerToken') {
27
+ logAuthFailure({rawToken, reason: 'type'});
28
+ throw new ClientError('Invalid provisioner token', 'unauthorized', {status: 401});
29
+ }
30
+
31
+ const provisionerToken = await resolveProvisionerTokenByHash(hashOpaqueToken(rawToken));
32
+ if (!provisionerToken) {
33
+ logAuthFailure({rawToken, reason: 'not-found'});
34
+ throw new ClientError('Invalid provisioner token', 'unauthorized', {status: 401});
35
+ }
36
+
37
+ if (provisionerToken.revokedAt) {
38
+ logAuthFailure({rawToken, reason: 'revoked'});
39
+ throw new ClientError('Provisioner token has been revoked', 'provisioner-token-revoked', {
40
+ status: 401,
41
+ });
42
+ }
43
+
44
+ if (provisionerToken.expiresAt && provisionerToken.expiresAt <= new Date()) {
45
+ logAuthFailure({rawToken, reason: 'expired'});
46
+ throw new ClientError('Provisioner token has expired', 'provisioner-token-expired', {
47
+ status: 401,
48
+ });
49
+ }
50
+
51
+ const context: ProvisionerContext = {
52
+ provisionerTokenId: provisionerToken.id,
53
+ workspaceId: provisionerToken.workspaceId,
54
+ };
55
+
56
+ await touchProvisionerLastSeen({
57
+ tokenId: provisionerToken.id,
58
+ throttleSeconds: config.PROVISIONER_LAST_SEEN_THROTTLE_SECONDS,
59
+ }).catch((error: unknown) => {
60
+ logger().warn({error, provisionerTokenId: provisionerToken.id}, 'last-seen touch failed');
61
+ });
62
+
63
+ setProvisionerContext(request, context);
64
+ },
65
+ };
66
+ }
67
+
68
+ function logAuthFailure(params: {rawToken?: string | undefined; reason: AuthFailureReason}): void {
69
+ logger().warn(
70
+ {
71
+ prefix: params.rawToken ? extractDisplayPrefix(params.rawToken) : undefined,
72
+ reason: params.reason,
73
+ },
74
+ 'provisioner token auth failed',
75
+ );
76
+ }
@@ -0,0 +1,105 @@
1
+ import {AUTH_RUNNER_REGISTRATION_TOKEN} from '@shipfox/api-auth-context';
2
+ import {type AuthMethod, ClientError, extractBearerToken} from '@shipfox/node-fastify';
3
+ import {getTokenType, hashOpaqueToken} from '@shipfox/node-tokens';
4
+ import type {FastifyRequest} from 'fastify';
5
+ import {resolveEphemeralRegistrationTokenByHash} from '#db/ephemeral-registration-tokens.js';
6
+ import {resolveManualRegistrationTokenByHash} from '#db/manual-registration-tokens.js';
7
+
8
+ const RUNNER_CONTEXT_KEY = 'runner';
9
+
10
+ export type RunnerRegistrationContext =
11
+ | {
12
+ kind: 'manual';
13
+ registrationTokenId: string;
14
+ workspaceId: string;
15
+ }
16
+ | {
17
+ kind: 'ephemeral';
18
+ ephemeralTokenId: string;
19
+ workspaceId: string;
20
+ provisionerId: string;
21
+ reservationId: string | null;
22
+ provisionedRunnerId: string;
23
+ };
24
+
25
+ export function getRunnerContext(request: FastifyRequest): RunnerRegistrationContext {
26
+ const context = (request as unknown as Record<string, unknown>)[RUNNER_CONTEXT_KEY] as
27
+ | RunnerRegistrationContext
28
+ | undefined;
29
+ if (!context) {
30
+ throw new Error('Runner context is not available on this request');
31
+ }
32
+ return context;
33
+ }
34
+
35
+ export function createRunnerRegistrationTokenAuthMethod(): AuthMethod {
36
+ return {
37
+ name: AUTH_RUNNER_REGISTRATION_TOKEN,
38
+ authenticate: async (request) => {
39
+ const rawToken = extractBearerToken(request.headers.authorization);
40
+ if (!rawToken) {
41
+ throw new ClientError('Missing or invalid Authorization header', 'unauthorized', {
42
+ status: 401,
43
+ });
44
+ }
45
+
46
+ const tokenType = getTokenType(rawToken);
47
+
48
+ if (tokenType === 'ephemeralRegistrationToken') {
49
+ const ephemeralToken = await resolveEphemeralRegistrationTokenByHash(
50
+ hashOpaqueToken(rawToken),
51
+ );
52
+ if (!ephemeralToken) {
53
+ throw new ClientError('Invalid runner registration token', 'unauthorized', {
54
+ status: 401,
55
+ });
56
+ }
57
+ if (ephemeralToken.expiresAt < new Date()) {
58
+ throw new ClientError(
59
+ 'Ephemeral registration token has expired',
60
+ 'registration-token-expired',
61
+ {status: 401},
62
+ );
63
+ }
64
+
65
+ (request as unknown as Record<string, unknown>)[RUNNER_CONTEXT_KEY] = {
66
+ kind: 'ephemeral',
67
+ ephemeralTokenId: ephemeralToken.id,
68
+ workspaceId: ephemeralToken.workspaceId,
69
+ provisionerId: ephemeralToken.provisionerId,
70
+ reservationId: ephemeralToken.reservationId,
71
+ provisionedRunnerId: ephemeralToken.provisionedRunnerId,
72
+ } satisfies RunnerRegistrationContext;
73
+ return;
74
+ }
75
+
76
+ if (tokenType !== 'manualRegistrationToken') {
77
+ throw new ClientError('Invalid runner registration token', 'unauthorized', {status: 401});
78
+ }
79
+
80
+ const token = await resolveManualRegistrationTokenByHash(hashOpaqueToken(rawToken));
81
+ if (!token) {
82
+ throw new ClientError('Invalid runner registration token', 'unauthorized', {status: 401});
83
+ }
84
+
85
+ if (token.expiresAt && token.expiresAt < new Date()) {
86
+ throw new ClientError('Registration token has expired', 'registration-token-expired', {
87
+ status: 401,
88
+ });
89
+ }
90
+ if (token.revokedAt) {
91
+ throw new ClientError(
92
+ 'Manual registration token has been revoked',
93
+ 'manual-registration-token-revoked',
94
+ {status: 401},
95
+ );
96
+ }
97
+
98
+ (request as unknown as Record<string, unknown>)[RUNNER_CONTEXT_KEY] = {
99
+ kind: 'manual',
100
+ registrationTokenId: token.id,
101
+ workspaceId: token.workspaceId,
102
+ } satisfies RunnerRegistrationContext;
103
+ },
104
+ };
105
+ }
@@ -0,0 +1,15 @@
1
+ import type {ActiveProvisionerToken} from '#core/entities/provisioner-token.js';
2
+
3
+ export function toActiveProvisionerDto(token: ActiveProvisionerToken): {
4
+ id: string;
5
+ name: string | null;
6
+ prefix: string;
7
+ last_seen_at: string;
8
+ } {
9
+ return {
10
+ id: token.id,
11
+ name: token.name,
12
+ prefix: token.prefix,
13
+ last_seen_at: token.lastSeenAt.toISOString(),
14
+ };
15
+ }
@@ -0,0 +1,13 @@
1
+ export {toActiveProvisionerDto} from './active-provisioner.js';
2
+ export {toManualRegistrationTokenDto} from './manual-registration-token.js';
3
+ export {
4
+ toMintRegistrationTokensProvisionedRunners,
5
+ toMintRegistrationTokensResponseDto,
6
+ } from './mint-registration-tokens.js';
7
+ export {toPollDemandResponseDto} from './poll-demand.js';
8
+ export {
9
+ toActiveRunnersResponseDto,
10
+ toReconcileProvisionedRunnersResponseDto,
11
+ toReportProvisionedRunnersResponseDto,
12
+ } from './provisioned-runners.js';
13
+ export {toProvisionerTokenDto} from './provisioner-token.js';
@@ -0,0 +1,23 @@
1
+ import type {ManualRegistrationToken} from '#core/entities/manual-registration-token.js';
2
+
3
+ export function toManualRegistrationTokenDto(token: ManualRegistrationToken): {
4
+ id: string;
5
+ workspace_id: string;
6
+ prefix: string;
7
+ name: string | null;
8
+ expires_at: string | null;
9
+ revoked_at: string | null;
10
+ created_at: string;
11
+ updated_at: string;
12
+ } {
13
+ return {
14
+ id: token.id,
15
+ workspace_id: token.workspaceId,
16
+ prefix: token.prefix,
17
+ name: token.name,
18
+ expires_at: token.expiresAt?.toISOString() ?? null,
19
+ revoked_at: token.revokedAt?.toISOString() ?? null,
20
+ created_at: token.createdAt.toISOString(),
21
+ updated_at: token.updatedAt.toISOString(),
22
+ };
23
+ }
@@ -0,0 +1,35 @@
1
+ import type {
2
+ MintedRegistrationTokenDto,
3
+ MintRegistrationTokensBatchResponseDto,
4
+ MintRegistrationTokensProvisionedRunnerDto,
5
+ } from '@shipfox/api-runners-dto';
6
+ import type {
7
+ MintEphemeralRegistrationTokensBatchProvisionedRunner,
8
+ MintEphemeralRegistrationTokensBatchResult,
9
+ } from '#core/ephemeral-registration-tokens.js';
10
+
11
+ export function toMintRegistrationTokensProvisionedRunners(
12
+ provisionedRunners: MintRegistrationTokensProvisionedRunnerDto[],
13
+ ): MintEphemeralRegistrationTokensBatchProvisionedRunner[] {
14
+ return provisionedRunners.map((provisionedRunner) => ({
15
+ provisionedRunnerId: provisionedRunner.provisioned_runner_id,
16
+ }));
17
+ }
18
+
19
+ export function toMintRegistrationTokensResponseDto(
20
+ result: MintEphemeralRegistrationTokensBatchResult[],
21
+ ): MintRegistrationTokensBatchResponseDto {
22
+ return {
23
+ tokens: result.map(toMintedRegistrationTokenDto),
24
+ };
25
+ }
26
+
27
+ function toMintedRegistrationTokenDto(
28
+ result: MintEphemeralRegistrationTokensBatchResult,
29
+ ): MintedRegistrationTokenDto {
30
+ return {
31
+ provisioned_runner_id: result.provisionedRunnerId,
32
+ registration_token: result.rawToken,
33
+ expires_at: result.token.expiresAt.toISOString(),
34
+ };
35
+ }
@@ -0,0 +1,20 @@
1
+ import type {PollDemandResponseDto} from '@shipfox/api-runners-dto';
2
+ import type {PollDemandResult} from '#core/demand.js';
3
+
4
+ export function toPollDemandResponseDto(result: PollDemandResult): PollDemandResponseDto {
5
+ return {
6
+ stats: result.stats.map((stat) => ({
7
+ labels: stat.labels,
8
+ queued: stat.queued,
9
+ reserved: stat.reserved,
10
+ oldest_queued_at: stat.oldestQueuedAt.toISOString(),
11
+ })),
12
+ reservations: result.reservations.map((reservation) => ({
13
+ reservation_id: reservation.reservationId,
14
+ labels: reservation.labels,
15
+ count: reservation.count,
16
+ expires_at: reservation.expiresAt.toISOString(),
17
+ })),
18
+ terminate_provisioned_runner_ids: result.terminateProvisionedRunnerIds,
19
+ };
20
+ }
@@ -0,0 +1,88 @@
1
+ import type {
2
+ ActiveRunner,
3
+ ReconcileProvisionedRunnersResult,
4
+ ReportProvisionedRunnersResult,
5
+ } from '#core/provisioned-runners.js';
6
+
7
+ export function toReportProvisionedRunnersResponseDto(result: ReportProvisionedRunnersResult): {
8
+ accepted: number;
9
+ reservations_released: number;
10
+ } {
11
+ return {
12
+ accepted: result.accepted,
13
+ reservations_released: result.reservationsReleased,
14
+ };
15
+ }
16
+
17
+ export function toReconcileProvisionedRunnersResponseDto(
18
+ result: ReconcileProvisionedRunnersResult,
19
+ ): {
20
+ runners: Array<{
21
+ provisioned_runner_id: string;
22
+ state: ReconcileProvisionedRunnersResult['runners'][number]['state'];
23
+ reservation_id: string | null;
24
+ runner_session_id: string | null;
25
+ bound_job: {
26
+ job_id: string;
27
+ workflow_run_attempt_id: string;
28
+ last_heartbeat_at: string;
29
+ cancellation_requested_at: string | null;
30
+ } | null;
31
+ desired_intent: ReconcileProvisionedRunnersResult['runners'][number]['desiredIntent'];
32
+ }>;
33
+ terminated_absent_provisioned_runner_ids: string[];
34
+ } {
35
+ return {
36
+ runners: result.runners.map((runner) => ({
37
+ provisioned_runner_id: runner.provisionedRunnerId,
38
+ state: runner.state,
39
+ reservation_id: runner.reservationId,
40
+ runner_session_id: runner.runnerSessionId,
41
+ bound_job: runner.boundJobExecution
42
+ ? {
43
+ job_id: runner.boundJobExecution.jobId,
44
+ workflow_run_attempt_id: runner.boundJobExecution.workflowRunAttemptId,
45
+ last_heartbeat_at: runner.boundJobExecution.lastHeartbeatAt.toISOString(),
46
+ cancellation_requested_at:
47
+ runner.boundJobExecution.cancellationRequestedAt?.toISOString() ?? null,
48
+ }
49
+ : null,
50
+ desired_intent: runner.desiredIntent,
51
+ })),
52
+ terminated_absent_provisioned_runner_ids: result.terminatedAbsentProvisionedRunnerIds,
53
+ };
54
+ }
55
+
56
+ export function toActiveRunnersResponseDto(runners: ActiveRunner[]): {
57
+ runners: Array<{
58
+ runner_session_id: string | null;
59
+ provisioned_runner_id: string | null;
60
+ provisioner_id: string | null;
61
+ state: ActiveRunner['state'];
62
+ labels: string[];
63
+ template_key: string | null;
64
+ provider_kind: string | null;
65
+ job_id: string | null;
66
+ workflow_run_attempt_id: string | null;
67
+ project_id: string | null;
68
+ reported_at: string | null;
69
+ last_heartbeat_at: string | null;
70
+ }>;
71
+ } {
72
+ return {
73
+ runners: runners.map((runner) => ({
74
+ runner_session_id: runner.runnerSessionId,
75
+ provisioned_runner_id: runner.provisionedRunnerId,
76
+ provisioner_id: runner.provisionerId,
77
+ state: runner.state,
78
+ labels: runner.labels,
79
+ template_key: runner.templateKey,
80
+ provider_kind: runner.providerKind,
81
+ job_id: runner.jobId,
82
+ workflow_run_attempt_id: runner.workflowRunAttemptId,
83
+ project_id: runner.projectId,
84
+ reported_at: runner.reportedAt?.toISOString() ?? null,
85
+ last_heartbeat_at: runner.lastHeartbeatAt?.toISOString() ?? null,
86
+ })),
87
+ };
88
+ }