@shipfox/api-runners 12.3.0 → 12.4.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 (153) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +36 -0
  3. package/dist/config.d.ts +1 -0
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +9 -2
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/demand.d.ts +4 -2
  8. package/dist/core/demand.d.ts.map +1 -1
  9. package/dist/core/demand.js +22 -6
  10. package/dist/core/demand.js.map +1 -1
  11. package/dist/core/entities/reservation.d.ts +1 -0
  12. package/dist/core/entities/reservation.d.ts.map +1 -1
  13. package/dist/core/entities/reservation.js.map +1 -1
  14. package/dist/core/entities/runner-instance.d.ts +3 -0
  15. package/dist/core/entities/runner-instance.d.ts.map +1 -1
  16. package/dist/core/entities/runner-instance.js.map +1 -1
  17. package/dist/core/errors.d.ts +5 -1
  18. package/dist/core/errors.d.ts.map +1 -1
  19. package/dist/core/errors.js +9 -2
  20. package/dist/core/errors.js.map +1 -1
  21. package/dist/core/runner-activation.d.ts +4 -1
  22. package/dist/core/runner-activation.d.ts.map +1 -1
  23. package/dist/core/runner-activation.js +32 -3
  24. package/dist/core/runner-activation.js.map +1 -1
  25. package/dist/core/runner-control-sessions.d.ts +8 -4
  26. package/dist/core/runner-control-sessions.d.ts.map +1 -1
  27. package/dist/core/runner-control-sessions.js +116 -20
  28. package/dist/core/runner-control-sessions.js.map +1 -1
  29. package/dist/core/runner-instances.d.ts +1 -0
  30. package/dist/core/runner-instances.d.ts.map +1 -1
  31. package/dist/core/runner-instances.js +1 -0
  32. package/dist/core/runner-instances.js.map +1 -1
  33. package/dist/db/db.d.ts +68 -0
  34. package/dist/db/db.d.ts.map +1 -1
  35. package/dist/db/ephemeral-registration-tokens.d.ts.map +1 -1
  36. package/dist/db/ephemeral-registration-tokens.js +1 -1
  37. package/dist/db/ephemeral-registration-tokens.js.map +1 -1
  38. package/dist/db/job-executions.d.ts.map +1 -1
  39. package/dist/db/job-executions.js +35 -4
  40. package/dist/db/job-executions.js.map +1 -1
  41. package/dist/db/reservation-locks.d.ts +6 -0
  42. package/dist/db/reservation-locks.d.ts.map +1 -0
  43. package/dist/db/reservation-locks.js +15 -0
  44. package/dist/db/reservation-locks.js.map +1 -0
  45. package/dist/db/reservations.d.ts +17 -3
  46. package/dist/db/reservations.d.ts.map +1 -1
  47. package/dist/db/reservations.js +246 -51
  48. package/dist/db/reservations.js.map +1 -1
  49. package/dist/db/runner-assignments.d.ts +25 -1
  50. package/dist/db/runner-assignments.d.ts.map +1 -1
  51. package/dist/db/runner-assignments.js +160 -18
  52. package/dist/db/runner-assignments.js.map +1 -1
  53. package/dist/db/runner-instances.d.ts +11 -2
  54. package/dist/db/runner-instances.d.ts.map +1 -1
  55. package/dist/db/runner-instances.js +167 -24
  56. package/dist/db/runner-instances.js.map +1 -1
  57. package/dist/db/runner-sessions.d.ts.map +1 -1
  58. package/dist/db/runner-sessions.js +14 -1
  59. package/dist/db/runner-sessions.js.map +1 -1
  60. package/dist/db/runner-states.d.ts +2 -0
  61. package/dist/db/runner-states.d.ts.map +1 -0
  62. package/dist/db/runner-states.js +7 -0
  63. package/dist/db/runner-states.js.map +1 -0
  64. package/dist/db/schema/reservations.d.ts +18 -0
  65. package/dist/db/schema/reservations.d.ts.map +1 -1
  66. package/dist/db/schema/reservations.js +7 -1
  67. package/dist/db/schema/reservations.js.map +1 -1
  68. package/dist/db/schema/runner-instances.d.ts +18 -0
  69. package/dist/db/schema/runner-instances.d.ts.map +1 -1
  70. package/dist/db/schema/runner-instances.js +8 -0
  71. package/dist/db/schema/runner-instances.js.map +1 -1
  72. package/dist/metrics/index.d.ts +2 -2
  73. package/dist/metrics/index.d.ts.map +1 -1
  74. package/dist/metrics/index.js +1 -1
  75. package/dist/metrics/index.js.map +1 -1
  76. package/dist/metrics/instance.d.ts +72 -2
  77. package/dist/metrics/instance.d.ts.map +1 -1
  78. package/dist/metrics/instance.js +161 -1
  79. package/dist/metrics/instance.js.map +1 -1
  80. package/dist/metrics/service.d.ts.map +1 -1
  81. package/dist/metrics/service.js +25 -4
  82. package/dist/metrics/service.js.map +1 -1
  83. package/dist/presentation/dto/poll-demand.d.ts.map +1 -1
  84. package/dist/presentation/dto/poll-demand.js +3 -0
  85. package/dist/presentation/dto/poll-demand.js.map +1 -1
  86. package/dist/presentation/dto/runner-instances.d.ts +4 -1
  87. package/dist/presentation/dto/runner-instances.d.ts.map +1 -1
  88. package/dist/presentation/dto/runner-instances.js +4 -1
  89. package/dist/presentation/dto/runner-instances.js.map +1 -1
  90. package/dist/presentation/routes/assign-runner-instances.d.ts.map +1 -1
  91. package/dist/presentation/routes/assign-runner-instances.js +10 -4
  92. package/dist/presentation/routes/assign-runner-instances.js.map +1 -1
  93. package/dist/presentation/routes/poll-demand.d.ts.map +1 -1
  94. package/dist/presentation/routes/poll-demand.js +1 -0
  95. package/dist/presentation/routes/poll-demand.js.map +1 -1
  96. package/dist/presentation/routes/reconcile-runner-instances.d.ts.map +1 -1
  97. package/dist/presentation/routes/reconcile-runner-instances.js +4 -2
  98. package/dist/presentation/routes/reconcile-runner-instances.js.map +1 -1
  99. package/dist/presentation/routes/runner-enrollment.d.ts +4 -1
  100. package/dist/presentation/routes/runner-enrollment.d.ts.map +1 -1
  101. package/dist/presentation/routes/runner-enrollment.js +11 -6
  102. package/dist/presentation/routes/runner-enrollment.js.map +1 -1
  103. package/dist/tsconfig.test.tsbuildinfo +1 -1
  104. package/drizzle/0005_previous_lenny_balinger.sql +5 -0
  105. package/drizzle/meta/0005_snapshot.json +2220 -0
  106. package/drizzle/meta/_journal.json +7 -0
  107. package/package.json +6 -6
  108. package/src/config.test.ts +22 -0
  109. package/src/config.ts +15 -2
  110. package/src/core/demand.test.ts +192 -3
  111. package/src/core/demand.ts +44 -4
  112. package/src/core/entities/reservation.ts +1 -0
  113. package/src/core/entities/runner-instance.ts +4 -0
  114. package/src/core/errors.ts +30 -1
  115. package/src/core/runner-activation.ts +48 -2
  116. package/src/core/runner-control-sessions.test.ts +135 -3
  117. package/src/core/runner-control-sessions.ts +141 -19
  118. package/src/core/runner-instances.test.ts +19 -0
  119. package/src/core/runner-instances.ts +2 -0
  120. package/src/core/runner-sessions.test.ts +121 -1
  121. package/src/db/ephemeral-registration-tokens.test.ts +20 -1
  122. package/src/db/ephemeral-registration-tokens.ts +1 -0
  123. package/src/db/job-executions.test.ts +98 -12
  124. package/src/db/job-executions.ts +52 -4
  125. package/src/db/reservation-locks.ts +22 -0
  126. package/src/db/reservations.test.ts +733 -22
  127. package/src/db/reservations.ts +408 -144
  128. package/src/db/runner-assignments.test.ts +105 -1
  129. package/src/db/runner-assignments.ts +245 -25
  130. package/src/db/runner-instances.test.ts +357 -2
  131. package/src/db/runner-instances.ts +289 -55
  132. package/src/db/runner-sessions.ts +19 -1
  133. package/src/db/runner-states.ts +7 -0
  134. package/src/db/schema/reservations.ts +5 -1
  135. package/src/db/schema/runner-instances.ts +9 -0
  136. package/src/metrics/index.ts +24 -1
  137. package/src/metrics/instance.test.ts +118 -0
  138. package/src/metrics/instance.ts +247 -3
  139. package/src/metrics/service.test.ts +46 -1
  140. package/src/metrics/service.ts +55 -8
  141. package/src/presentation/dto/poll-demand.ts +3 -0
  142. package/src/presentation/dto/runner-instances.ts +8 -1
  143. package/src/presentation/routes/assign-runner-instances.ts +14 -2
  144. package/src/presentation/routes/late-runner-enrollment.test.ts +219 -2
  145. package/src/presentation/routes/poll-demand.test.ts +56 -0
  146. package/src/presentation/routes/poll-demand.ts +1 -0
  147. package/src/presentation/routes/reconcile-runner-instances.test.ts +34 -2
  148. package/src/presentation/routes/reconcile-runner-instances.ts +7 -1
  149. package/src/presentation/routes/runner-enrollment.test.ts +316 -3
  150. package/src/presentation/routes/runner-enrollment.ts +8 -5
  151. package/test/factories/reservation.ts +2 -0
  152. package/test/factories/runner-instance.ts +6 -0
  153. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/errors.ts"],"sourcesContent":["export class RunningJobExecutionNotFoundError extends Error {\n constructor(jobExecutionId: string) {\n super(`Running job execution not found: ${jobExecutionId}`);\n this.name = 'RunningJobExecutionNotFoundError';\n }\n}\n\nexport class ManualRegistrationTokenNotFoundError extends Error {\n constructor(tokenId: string) {\n super(`Manual registration token not found: ${tokenId}`);\n this.name = 'ManualRegistrationTokenNotFoundError';\n }\n}\n\nexport class ProvisionerTokenNotFoundError extends Error {\n constructor(tokenId: string) {\n super(`Provisioner token not found: ${tokenId}`);\n this.name = 'ProvisionerTokenNotFoundError';\n }\n}\n\nexport class ProvisionerAdminIdempotencyKeyReuseError extends Error {\n constructor() {\n super('Idempotency-Key was already used for a different provisioner token command');\n this.name = 'ProvisionerAdminIdempotencyKeyReuseError';\n }\n}\n\nexport class ProvisionerAdminIdempotencyReplayUnavailableError extends Error {\n constructor() {\n super('Idempotency-Key replay cannot reproduce the original provisioner token');\n this.name = 'ProvisionerAdminIdempotencyReplayUnavailableError';\n }\n}\n\nexport class EmptyRunnerLabelsError extends Error {\n constructor() {\n super('Runner labels cannot be empty');\n this.name = 'EmptyRunnerLabelsError';\n }\n}\n\nexport class RunnerLabelsReservedError extends Error {\n constructor(public readonly labels: string[]) {\n super(\n `All supplied runner labels are reserved for installation-scope provisioners: ${labels.join(', ')}`,\n );\n this.name = 'RunnerLabelsReservedError';\n }\n}\n\nexport class EmptyRequiredLabelsError extends Error {\n constructor() {\n super('Required labels cannot be empty');\n this.name = 'EmptyRequiredLabelsError';\n }\n}\n\nexport class RegistrationTokenConsumedError extends Error {\n constructor(public readonly ephemeralTokenId: string) {\n super(`Ephemeral registration token has already been consumed: ${ephemeralTokenId}`);\n this.name = 'RegistrationTokenConsumedError';\n }\n}\n\nexport class RegistrationTokenExpiredError extends Error {\n constructor(public readonly ephemeralTokenId: string) {\n super(`Ephemeral registration token has expired: ${ephemeralTokenId}`);\n this.name = 'RegistrationTokenExpiredError';\n }\n}\n\nexport class RegistrationTokenWorkspaceMismatchError extends Error {\n constructor(\n public readonly ephemeralTokenId: string,\n public readonly workspaceId: string,\n ) {\n super(\n `Ephemeral registration token ${ephemeralTokenId} does not belong to workspace ${workspaceId}`,\n );\n this.name = 'RegistrationTokenWorkspaceMismatchError';\n }\n}\n\nexport class ActiveEphemeralRegistrationTokenExistsError extends Error {\n constructor(\n public readonly workspaceId: string,\n public readonly provisionerId: string,\n public readonly providerRunnerId: string,\n ) {\n super(\n `Active ephemeral registration token already exists for provisioned runner ${providerRunnerId} in workspace ${workspaceId}`,\n );\n this.name = 'ActiveEphemeralRegistrationTokenExistsError';\n }\n}\n\nexport class ReservationNotFoundError extends Error {\n constructor(public readonly reservationId: string) {\n super(`Reservation not found: ${reservationId}`);\n this.name = 'ReservationNotFoundError';\n }\n}\n\nexport class ReservationExpiredError extends Error {\n constructor(public readonly reservationId: string) {\n super(`Reservation has expired: ${reservationId}`);\n this.name = 'ReservationExpiredError';\n }\n}\n\nexport class RunnerInstanceNotAssignableError extends Error {\n constructor(public readonly runnerInstanceId: string) {\n super(`Runner instance cannot be assigned: ${runnerInstanceId}`);\n this.name = 'RunnerInstanceNotAssignableError';\n }\n}\nexport class RunnerInstanceAlreadyAssignedError extends Error {\n constructor(public readonly runnerInstanceId: string) {\n super(`Runner instance is already assigned: ${runnerInstanceId}`);\n this.name = 'RunnerInstanceAlreadyAssignedError';\n }\n}\nexport class ReservationAlreadyAssignedError extends Error {\n constructor(public readonly reservationId: string) {\n super(`Reservation is already assigned: ${reservationId}`);\n this.name = 'ReservationAlreadyAssignedError';\n }\n}\n\nexport class RegistrationTokenBatchTooLargeError extends Error {\n constructor(\n public readonly requested: number,\n public readonly max: number,\n ) {\n super(\n `Registration token batch requested ${requested} provisioned runners, exceeding max ${max}`,\n );\n this.name = 'RegistrationTokenBatchTooLargeError';\n }\n}\n\nexport class RegistrationTokenBatchExceedsReservationError extends Error {\n constructor(\n public readonly requested: number,\n public readonly reservationCount: number,\n public readonly alreadyMinted: number = 0,\n ) {\n super(\n `Registration token batch requested ${requested} provisioned runners with ${alreadyMinted} already minted, exceeding reservation count ${reservationCount}`,\n );\n this.name = 'RegistrationTokenBatchExceedsReservationError';\n }\n}\n\nexport class ActiveEphemeralRegistrationTokensExistError extends Error {\n constructor(public readonly providerRunnerIds: string[]) {\n super(\n `Active ephemeral registration tokens already exist for provisioned runners: ${providerRunnerIds.join(', ')}`,\n );\n this.name = 'ActiveEphemeralRegistrationTokensExistError';\n }\n}\n\nexport class RunnerSessionExhaustedError extends Error {\n constructor(public readonly runnerSessionId: string) {\n super(`Runner session claim limit exhausted: ${runnerSessionId}`);\n this.name = 'RunnerSessionExhaustedError';\n }\n}\n"],"names":["RunningJobExecutionNotFoundError","Error","jobExecutionId","name","ManualRegistrationTokenNotFoundError","tokenId","ProvisionerTokenNotFoundError","ProvisionerAdminIdempotencyKeyReuseError","ProvisionerAdminIdempotencyReplayUnavailableError","EmptyRunnerLabelsError","RunnerLabelsReservedError","labels","join","EmptyRequiredLabelsError","RegistrationTokenConsumedError","ephemeralTokenId","RegistrationTokenExpiredError","RegistrationTokenWorkspaceMismatchError","workspaceId","ActiveEphemeralRegistrationTokenExistsError","provisionerId","providerRunnerId","ReservationNotFoundError","reservationId","ReservationExpiredError","RunnerInstanceNotAssignableError","runnerInstanceId","RunnerInstanceAlreadyAssignedError","ReservationAlreadyAssignedError","RegistrationTokenBatchTooLargeError","requested","max","RegistrationTokenBatchExceedsReservationError","reservationCount","alreadyMinted","ActiveEphemeralRegistrationTokensExistError","providerRunnerIds","RunnerSessionExhaustedError","runnerSessionId"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/errors.ts"],"sourcesContent":["export class RunningJobExecutionNotFoundError extends Error {\n constructor(jobExecutionId: string) {\n super(`Running job execution not found: ${jobExecutionId}`);\n this.name = 'RunningJobExecutionNotFoundError';\n }\n}\n\nexport class ManualRegistrationTokenNotFoundError extends Error {\n constructor(tokenId: string) {\n super(`Manual registration token not found: ${tokenId}`);\n this.name = 'ManualRegistrationTokenNotFoundError';\n }\n}\n\nexport class ProvisionerTokenNotFoundError extends Error {\n constructor(tokenId: string) {\n super(`Provisioner token not found: ${tokenId}`);\n this.name = 'ProvisionerTokenNotFoundError';\n }\n}\n\nexport class ProvisionerAdminIdempotencyKeyReuseError extends Error {\n constructor() {\n super('Idempotency-Key was already used for a different provisioner token command');\n this.name = 'ProvisionerAdminIdempotencyKeyReuseError';\n }\n}\n\nexport class ProvisionerAdminIdempotencyReplayUnavailableError extends Error {\n constructor() {\n super('Idempotency-Key replay cannot reproduce the original provisioner token');\n this.name = 'ProvisionerAdminIdempotencyReplayUnavailableError';\n }\n}\n\nexport class EmptyRunnerLabelsError extends Error {\n constructor() {\n super('Runner labels cannot be empty');\n this.name = 'EmptyRunnerLabelsError';\n }\n}\n\nexport class RunnerLabelsReservedError extends Error {\n constructor(public readonly labels: string[]) {\n super(\n `All supplied runner labels are reserved for installation-scope provisioners: ${labels.join(', ')}`,\n );\n this.name = 'RunnerLabelsReservedError';\n }\n}\n\nexport class EmptyRequiredLabelsError extends Error {\n constructor() {\n super('Required labels cannot be empty');\n this.name = 'EmptyRequiredLabelsError';\n }\n}\n\nexport class RegistrationTokenConsumedError extends Error {\n constructor(public readonly ephemeralTokenId: string) {\n super(`Ephemeral registration token has already been consumed: ${ephemeralTokenId}`);\n this.name = 'RegistrationTokenConsumedError';\n }\n}\n\nexport class RegistrationTokenExpiredError extends Error {\n constructor(public readonly ephemeralTokenId: string) {\n super(`Ephemeral registration token has expired: ${ephemeralTokenId}`);\n this.name = 'RegistrationTokenExpiredError';\n }\n}\n\nexport class RegistrationTokenWorkspaceMismatchError extends Error {\n constructor(\n public readonly ephemeralTokenId: string,\n public readonly workspaceId: string,\n ) {\n super(\n `Ephemeral registration token ${ephemeralTokenId} does not belong to workspace ${workspaceId}`,\n );\n this.name = 'RegistrationTokenWorkspaceMismatchError';\n }\n}\n\nexport class ActiveEphemeralRegistrationTokenExistsError extends Error {\n constructor(\n public readonly workspaceId: string,\n public readonly provisionerId: string,\n public readonly providerRunnerId: string,\n ) {\n super(\n `Active ephemeral registration token already exists for provisioned runner ${providerRunnerId} in workspace ${workspaceId}`,\n );\n this.name = 'ActiveEphemeralRegistrationTokenExistsError';\n }\n}\n\nexport class ReservationNotFoundError extends Error {\n constructor(public readonly reservationId: string) {\n super(`Reservation not found: ${reservationId}`);\n this.name = 'ReservationNotFoundError';\n }\n}\n\nexport class ReservationExpiredError extends Error {\n constructor(public readonly reservationId: string) {\n super(`Reservation has expired: ${reservationId}`);\n this.name = 'ReservationExpiredError';\n }\n}\n\nexport type RunnerInstanceNotAssignableReason =\n | 'runner-not-found'\n | 'runner-not-running'\n | 'provider-identity-missing'\n | 'control-session-not-active'\n | 'labels-mismatch'\n | 'capacity-exhausted';\n\nexport type RunnerAssignmentRejectionReason =\n | 'reservation-not-found'\n | 'reservation-expired'\n | 'already-assigned'\n | RunnerInstanceNotAssignableReason;\n\nexport class RunnerInstanceNotAssignableError extends Error {\n constructor(\n public readonly runnerInstanceId: string,\n public readonly reason: RunnerInstanceNotAssignableReason,\n ) {\n super(`Runner instance cannot be assigned: ${runnerInstanceId}`);\n this.name = 'RunnerInstanceNotAssignableError';\n }\n}\n\nexport class RunnerInstanceAlreadyAssignedError extends Error {\n constructor(public readonly runnerInstanceId: string) {\n super(`Runner instance is already assigned: ${runnerInstanceId}`);\n this.name = 'RunnerInstanceAlreadyAssignedError';\n }\n}\n\nexport function getRunnerAssignmentRejectionReason(\n error: unknown,\n): RunnerAssignmentRejectionReason | null {\n if (error instanceof ReservationNotFoundError) return 'reservation-not-found';\n if (error instanceof ReservationExpiredError) return 'reservation-expired';\n if (error instanceof RunnerInstanceAlreadyAssignedError) return 'already-assigned';\n if (error instanceof RunnerInstanceNotAssignableError) return error.reason;\n return null;\n}\n\nexport class ReservationAlreadyAssignedError extends Error {\n constructor(public readonly reservationId: string) {\n super(`Reservation is already assigned: ${reservationId}`);\n this.name = 'ReservationAlreadyAssignedError';\n }\n}\n\nexport class RegistrationTokenBatchTooLargeError extends Error {\n constructor(\n public readonly requested: number,\n public readonly max: number,\n ) {\n super(\n `Registration token batch requested ${requested} provisioned runners, exceeding max ${max}`,\n );\n this.name = 'RegistrationTokenBatchTooLargeError';\n }\n}\n\nexport class RegistrationTokenBatchExceedsReservationError extends Error {\n constructor(\n public readonly requested: number,\n public readonly reservationCount: number,\n public readonly alreadyMinted: number = 0,\n ) {\n super(\n `Registration token batch requested ${requested} provisioned runners with ${alreadyMinted} already minted, exceeding reservation count ${reservationCount}`,\n );\n this.name = 'RegistrationTokenBatchExceedsReservationError';\n }\n}\n\nexport class ActiveEphemeralRegistrationTokensExistError extends Error {\n constructor(public readonly providerRunnerIds: string[]) {\n super(\n `Active ephemeral registration tokens already exist for provisioned runners: ${providerRunnerIds.join(', ')}`,\n );\n this.name = 'ActiveEphemeralRegistrationTokensExistError';\n }\n}\n\nexport class RunnerSessionExhaustedError extends Error {\n constructor(public readonly runnerSessionId: string) {\n super(`Runner session claim limit exhausted: ${runnerSessionId}`);\n this.name = 'RunnerSessionExhaustedError';\n }\n}\n"],"names":["RunningJobExecutionNotFoundError","Error","jobExecutionId","name","ManualRegistrationTokenNotFoundError","tokenId","ProvisionerTokenNotFoundError","ProvisionerAdminIdempotencyKeyReuseError","ProvisionerAdminIdempotencyReplayUnavailableError","EmptyRunnerLabelsError","RunnerLabelsReservedError","labels","join","EmptyRequiredLabelsError","RegistrationTokenConsumedError","ephemeralTokenId","RegistrationTokenExpiredError","RegistrationTokenWorkspaceMismatchError","workspaceId","ActiveEphemeralRegistrationTokenExistsError","provisionerId","providerRunnerId","ReservationNotFoundError","reservationId","ReservationExpiredError","RunnerInstanceNotAssignableError","runnerInstanceId","reason","RunnerInstanceAlreadyAssignedError","getRunnerAssignmentRejectionReason","error","ReservationAlreadyAssignedError","RegistrationTokenBatchTooLargeError","requested","max","RegistrationTokenBatchExceedsReservationError","reservationCount","alreadyMinted","ActiveEphemeralRegistrationTokensExistError","providerRunnerIds","RunnerSessionExhaustedError","runnerSessionId"],"mappings":"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"}
@@ -1,19 +1,22 @@
1
1
  import type { Tx } from '#db/db.js';
2
+ import { type RunnerActivationTokenNotIssuedSurface } from '#metrics/instance.js';
2
3
  export declare function issueRunnerActivationToken(params: {
3
4
  runnerInstanceId: string;
4
5
  provisionerId: string;
5
6
  ttlSeconds: number;
7
+ surface: RunnerActivationTokenNotIssuedSurface;
6
8
  }): Promise<string | null>;
7
9
  export declare function issueRunnerActivationTokenTx(tx: Tx, params: {
8
10
  runnerInstanceId: string;
9
11
  provisionerId: string;
10
12
  ttlSeconds: number;
13
+ surface: RunnerActivationTokenNotIssuedSurface;
11
14
  }): Promise<string | null>;
12
15
  export declare function getRunnerAssignment(params: {
13
16
  runnerInstanceId: string;
14
17
  provisionerId: string;
15
18
  }): Promise<{
16
- workspaceId: string | null;
19
+ workspaceId: string;
17
20
  runnerSessionId: string | null;
18
21
  } | null>;
19
22
  //# sourceMappingURL=runner-activation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner-activation.d.ts","sourceRoot":"","sources":["../../src/core/runner-activation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,WAAW,CAAC;AAKlC,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEzB;AAED,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,EAAE,EACN,MAAM,EAAE;IACN,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB,GACA,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuCxB;AAED,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;CACvB;;;UAeA"}
1
+ {"version":3,"file":"runner-activation.d.ts","sourceRoot":"","sources":["../../src/core/runner-activation.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,WAAW,CAAC;AAKlC,OAAO,EAEL,KAAK,qCAAqC,EAE3C,MAAM,sBAAsB,CAAC;AAE9B,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,qCAAqC,CAAC;CAChD,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEzB;AAED,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,EAAE,EACN,MAAM,EAAE;IACN,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,qCAAqC,CAAC;CAChD,GACA,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2CxB;AAED,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;CACvB;;;UAkBA"}
@@ -1,8 +1,10 @@
1
+ import { logger } from '@shipfox/node-opentelemetry';
1
2
  import { extractDisplayPrefix, generateOpaqueToken, hashOpaqueToken } from '@shipfox/node-tokens';
2
3
  import { and, eq, isNull, sql } from 'drizzle-orm';
3
4
  import { db } from '#db/db.js';
4
5
  import { runnerActivationTokens } from '#db/schema/runner-activation-tokens.js';
5
6
  import { providerRunners } from '#db/schema/runner-instances.js';
7
+ import { recordRunnerActivationTokenNotIssued } from '#metrics/instance.js';
6
8
  export async function issueRunnerActivationToken(params) {
7
9
  return await db().transaction(async (tx)=>issueRunnerActivationTokenTx(tx, params));
8
10
  }
@@ -13,7 +15,11 @@ export async function issueRunnerActivationTokenTx(tx, params) {
13
15
  runnerSessionId: providerRunners.runnerSessionId,
14
16
  state: providerRunners.state
15
17
  }).from(providerRunners).where(and(eq(providerRunners.id, params.runnerInstanceId), eq(providerRunners.provisionerId, params.provisionerId))).limit(1).for('update');
16
- if (!runner?.workspaceId || runner.runnerSessionId || runner.state !== 'running') return null;
18
+ const notIssuedReason = getRunnerActivationTokenNotIssuedReason(runner);
19
+ if (notIssuedReason) {
20
+ recordRunnerActivationTokenNotIssuedWithLog(notIssuedReason, params);
21
+ return null;
22
+ }
17
23
  await tx.update(runnerActivationTokens).set({
18
24
  revokedAt: sql`now()`
19
25
  }).where(and(eq(runnerActivationTokens.runnerInstanceId, params.runnerInstanceId), isNull(runnerActivationTokens.consumedAt), isNull(runnerActivationTokens.revokedAt)));
@@ -29,9 +35,32 @@ export async function issueRunnerActivationTokenTx(tx, params) {
29
35
  export async function getRunnerAssignment(params) {
30
36
  const [runner] = await db().select({
31
37
  workspaceId: providerRunners.workspaceId,
32
- runnerSessionId: providerRunners.runnerSessionId
38
+ runnerSessionId: providerRunners.runnerSessionId,
39
+ state: providerRunners.state
33
40
  }).from(providerRunners).where(and(eq(providerRunners.id, params.runnerInstanceId), eq(providerRunners.provisionerId, params.provisionerId))).limit(1);
34
- return runner?.workspaceId && !runner.runnerSessionId ? runner : null;
41
+ return runner?.workspaceId && !runner.runnerSessionId && runner.state === 'running' ? {
42
+ workspaceId: runner.workspaceId,
43
+ runnerSessionId: runner.runnerSessionId
44
+ } : null;
45
+ }
46
+ function getRunnerActivationTokenNotIssuedReason(runner) {
47
+ if (!runner) return 'runner-not-found';
48
+ if (!runner.workspaceId) return 'missing-workspace';
49
+ if (runner.runnerSessionId) return 'existing-session';
50
+ if (runner.state !== 'running') return 'not-running';
51
+ return null;
52
+ }
53
+ function recordRunnerActivationTokenNotIssuedWithLog(reason, params) {
54
+ recordRunnerActivationTokenNotIssued({
55
+ reason,
56
+ surface: params.surface
57
+ });
58
+ logger().debug({
59
+ runnerInstanceId: params.runnerInstanceId,
60
+ provisionerId: params.provisionerId,
61
+ reason,
62
+ surface: params.surface
63
+ }, 'Runner activation token was not issued');
35
64
  }
36
65
 
37
66
  //# sourceMappingURL=runner-activation.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/runner-activation.ts"],"sourcesContent":["import {extractDisplayPrefix, generateOpaqueToken, hashOpaqueToken} from '@shipfox/node-tokens';\nimport {and, eq, isNull, sql} from 'drizzle-orm';\nimport type {Tx} from '#db/db.js';\nimport {db} from '#db/db.js';\nimport {runnerActivationTokens} from '#db/schema/runner-activation-tokens.js';\nimport {providerRunners} from '#db/schema/runner-instances.js';\n\nexport async function issueRunnerActivationToken(params: {\n runnerInstanceId: string;\n provisionerId: string;\n ttlSeconds: number;\n}): Promise<string | null> {\n return await db().transaction(async (tx) => issueRunnerActivationTokenTx(tx, params));\n}\n\nexport async function issueRunnerActivationTokenTx(\n tx: Tx,\n params: {\n runnerInstanceId: string;\n provisionerId: string;\n ttlSeconds: number;\n },\n): Promise<string | null> {\n await tx.execute(\n sql`select pg_advisory_xact_lock(hashtext(${`runners_activation:${params.runnerInstanceId}`}))`,\n );\n const [runner] = await tx\n .select({\n workspaceId: providerRunners.workspaceId,\n runnerSessionId: providerRunners.runnerSessionId,\n state: providerRunners.state,\n })\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n ),\n )\n .limit(1)\n .for('update');\n if (!runner?.workspaceId || runner.runnerSessionId || runner.state !== 'running') return null;\n\n await tx\n .update(runnerActivationTokens)\n .set({revokedAt: sql`now()`})\n .where(\n and(\n eq(runnerActivationTokens.runnerInstanceId, params.runnerInstanceId),\n isNull(runnerActivationTokens.consumedAt),\n isNull(runnerActivationTokens.revokedAt),\n ),\n );\n const rawToken = generateOpaqueToken('runnerActivationToken');\n await tx.insert(runnerActivationTokens).values({\n runnerInstanceId: params.runnerInstanceId,\n hashedToken: hashOpaqueToken(rawToken),\n prefix: extractDisplayPrefix(rawToken),\n expiresAt: new Date(Date.now() + params.ttlSeconds * 1000),\n });\n return rawToken;\n}\n\nexport async function getRunnerAssignment(params: {\n runnerInstanceId: string;\n provisionerId: string;\n}) {\n const [runner] = await db()\n .select({\n workspaceId: providerRunners.workspaceId,\n runnerSessionId: providerRunners.runnerSessionId,\n })\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n ),\n )\n .limit(1);\n return runner?.workspaceId && !runner.runnerSessionId ? runner : null;\n}\n"],"names":["extractDisplayPrefix","generateOpaqueToken","hashOpaqueToken","and","eq","isNull","sql","db","runnerActivationTokens","providerRunners","issueRunnerActivationToken","params","transaction","tx","issueRunnerActivationTokenTx","execute","runnerInstanceId","runner","select","workspaceId","runnerSessionId","state","from","where","id","provisionerId","limit","for","update","set","revokedAt","consumedAt","rawToken","insert","values","hashedToken","prefix","expiresAt","Date","now","ttlSeconds","getRunnerAssignment"],"mappings":"AAAA,SAAQA,oBAAoB,EAAEC,mBAAmB,EAAEC,eAAe,QAAO,uBAAuB;AAChG,SAAQC,GAAG,EAAEC,EAAE,EAAEC,MAAM,EAAEC,GAAG,QAAO,cAAc;AAEjD,SAAQC,EAAE,QAAO,YAAY;AAC7B,SAAQC,sBAAsB,QAAO,yCAAyC;AAC9E,SAAQC,eAAe,QAAO,iCAAiC;AAE/D,OAAO,eAAeC,2BAA2BC,MAIhD;IACC,OAAO,MAAMJ,KAAKK,WAAW,CAAC,OAAOC,KAAOC,6BAA6BD,IAAIF;AAC/E;AAEA,OAAO,eAAeG,6BACpBD,EAAM,EACNF,MAIC;IAED,MAAME,GAAGE,OAAO,CACdT,GAAG,CAAC,sCAAsC,EAAE,CAAC,mBAAmB,EAAEK,OAAOK,gBAAgB,EAAE,CAAC,EAAE,CAAC;IAEjG,MAAM,CAACC,OAAO,GAAG,MAAMJ,GACpBK,MAAM,CAAC;QACNC,aAAaV,gBAAgBU,WAAW;QACxCC,iBAAiBX,gBAAgBW,eAAe;QAChDC,OAAOZ,gBAAgBY,KAAK;IAC9B,GACCC,IAAI,CAACb,iBACLc,KAAK,CACJpB,IACEC,GAAGK,gBAAgBe,EAAE,EAAEb,OAAOK,gBAAgB,GAC9CZ,GAAGK,gBAAgBgB,aAAa,EAAEd,OAAOc,aAAa,IAGzDC,KAAK,CAAC,GACNC,GAAG,CAAC;IACP,IAAI,CAACV,QAAQE,eAAeF,OAAOG,eAAe,IAAIH,OAAOI,KAAK,KAAK,WAAW,OAAO;IAEzF,MAAMR,GACHe,MAAM,CAACpB,wBACPqB,GAAG,CAAC;QAACC,WAAWxB,GAAG,CAAC,KAAK,CAAC;IAAA,GAC1BiB,KAAK,CACJpB,IACEC,GAAGI,uBAAuBQ,gBAAgB,EAAEL,OAAOK,gBAAgB,GACnEX,OAAOG,uBAAuBuB,UAAU,GACxC1B,OAAOG,uBAAuBsB,SAAS;IAG7C,MAAME,WAAW/B,oBAAoB;IACrC,MAAMY,GAAGoB,MAAM,CAACzB,wBAAwB0B,MAAM,CAAC;QAC7ClB,kBAAkBL,OAAOK,gBAAgB;QACzCmB,aAAajC,gBAAgB8B;QAC7BI,QAAQpC,qBAAqBgC;QAC7BK,WAAW,IAAIC,KAAKA,KAAKC,GAAG,KAAK5B,OAAO6B,UAAU,GAAG;IACvD;IACA,OAAOR;AACT;AAEA,OAAO,eAAeS,oBAAoB9B,MAGzC;IACC,MAAM,CAACM,OAAO,GAAG,MAAMV,KACpBW,MAAM,CAAC;QACNC,aAAaV,gBAAgBU,WAAW;QACxCC,iBAAiBX,gBAAgBW,eAAe;IAClD,GACCE,IAAI,CAACb,iBACLc,KAAK,CACJpB,IACEC,GAAGK,gBAAgBe,EAAE,EAAEb,OAAOK,gBAAgB,GAC9CZ,GAAGK,gBAAgBgB,aAAa,EAAEd,OAAOc,aAAa,IAGzDC,KAAK,CAAC;IACT,OAAOT,QAAQE,eAAe,CAACF,OAAOG,eAAe,GAAGH,SAAS;AACnE"}
1
+ {"version":3,"sources":["../../src/core/runner-activation.ts"],"sourcesContent":["import {logger} from '@shipfox/node-opentelemetry';\nimport {extractDisplayPrefix, generateOpaqueToken, hashOpaqueToken} from '@shipfox/node-tokens';\nimport {and, eq, isNull, sql} from 'drizzle-orm';\nimport type {Tx} from '#db/db.js';\nimport {db} from '#db/db.js';\nimport {runnerActivationTokens} from '#db/schema/runner-activation-tokens.js';\nimport type {RunnerInstanceDb} from '#db/schema/runner-instances.js';\nimport {providerRunners} from '#db/schema/runner-instances.js';\nimport {\n type RunnerActivationTokenNotIssuedReason,\n type RunnerActivationTokenNotIssuedSurface,\n recordRunnerActivationTokenNotIssued,\n} from '#metrics/instance.js';\n\nexport async function issueRunnerActivationToken(params: {\n runnerInstanceId: string;\n provisionerId: string;\n ttlSeconds: number;\n surface: RunnerActivationTokenNotIssuedSurface;\n}): Promise<string | null> {\n return await db().transaction(async (tx) => issueRunnerActivationTokenTx(tx, params));\n}\n\nexport async function issueRunnerActivationTokenTx(\n tx: Tx,\n params: {\n runnerInstanceId: string;\n provisionerId: string;\n ttlSeconds: number;\n surface: RunnerActivationTokenNotIssuedSurface;\n },\n): Promise<string | null> {\n await tx.execute(\n sql`select pg_advisory_xact_lock(hashtext(${`runners_activation:${params.runnerInstanceId}`}))`,\n );\n const [runner] = await tx\n .select({\n workspaceId: providerRunners.workspaceId,\n runnerSessionId: providerRunners.runnerSessionId,\n state: providerRunners.state,\n })\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n ),\n )\n .limit(1)\n .for('update');\n const notIssuedReason = getRunnerActivationTokenNotIssuedReason(runner);\n if (notIssuedReason) {\n recordRunnerActivationTokenNotIssuedWithLog(notIssuedReason, params);\n return null;\n }\n\n await tx\n .update(runnerActivationTokens)\n .set({revokedAt: sql`now()`})\n .where(\n and(\n eq(runnerActivationTokens.runnerInstanceId, params.runnerInstanceId),\n isNull(runnerActivationTokens.consumedAt),\n isNull(runnerActivationTokens.revokedAt),\n ),\n );\n const rawToken = generateOpaqueToken('runnerActivationToken');\n await tx.insert(runnerActivationTokens).values({\n runnerInstanceId: params.runnerInstanceId,\n hashedToken: hashOpaqueToken(rawToken),\n prefix: extractDisplayPrefix(rawToken),\n expiresAt: new Date(Date.now() + params.ttlSeconds * 1000),\n });\n return rawToken;\n}\n\nexport async function getRunnerAssignment(params: {\n runnerInstanceId: string;\n provisionerId: string;\n}) {\n const [runner] = await db()\n .select({\n workspaceId: providerRunners.workspaceId,\n runnerSessionId: providerRunners.runnerSessionId,\n state: providerRunners.state,\n })\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n ),\n )\n .limit(1);\n return runner?.workspaceId && !runner.runnerSessionId && runner.state === 'running'\n ? {workspaceId: runner.workspaceId, runnerSessionId: runner.runnerSessionId}\n : null;\n}\n\nfunction getRunnerActivationTokenNotIssuedReason(\n runner: Pick<RunnerInstanceDb, 'workspaceId' | 'runnerSessionId' | 'state'> | undefined,\n): RunnerActivationTokenNotIssuedReason | null {\n if (!runner) return 'runner-not-found';\n if (!runner.workspaceId) return 'missing-workspace';\n if (runner.runnerSessionId) return 'existing-session';\n if (runner.state !== 'running') return 'not-running';\n return null;\n}\n\nfunction recordRunnerActivationTokenNotIssuedWithLog(\n reason: RunnerActivationTokenNotIssuedReason,\n params: {\n runnerInstanceId: string;\n provisionerId: string;\n surface: RunnerActivationTokenNotIssuedSurface;\n },\n): void {\n recordRunnerActivationTokenNotIssued({reason, surface: params.surface});\n logger().debug(\n {\n runnerInstanceId: params.runnerInstanceId,\n provisionerId: params.provisionerId,\n reason,\n surface: params.surface,\n },\n 'Runner activation token was not issued',\n );\n}\n"],"names":["logger","extractDisplayPrefix","generateOpaqueToken","hashOpaqueToken","and","eq","isNull","sql","db","runnerActivationTokens","providerRunners","recordRunnerActivationTokenNotIssued","issueRunnerActivationToken","params","transaction","tx","issueRunnerActivationTokenTx","execute","runnerInstanceId","runner","select","workspaceId","runnerSessionId","state","from","where","id","provisionerId","limit","for","notIssuedReason","getRunnerActivationTokenNotIssuedReason","recordRunnerActivationTokenNotIssuedWithLog","update","set","revokedAt","consumedAt","rawToken","insert","values","hashedToken","prefix","expiresAt","Date","now","ttlSeconds","getRunnerAssignment","reason","surface","debug"],"mappings":"AAAA,SAAQA,MAAM,QAAO,8BAA8B;AACnD,SAAQC,oBAAoB,EAAEC,mBAAmB,EAAEC,eAAe,QAAO,uBAAuB;AAChG,SAAQC,GAAG,EAAEC,EAAE,EAAEC,MAAM,EAAEC,GAAG,QAAO,cAAc;AAEjD,SAAQC,EAAE,QAAO,YAAY;AAC7B,SAAQC,sBAAsB,QAAO,yCAAyC;AAE9E,SAAQC,eAAe,QAAO,iCAAiC;AAC/D,SAGEC,oCAAoC,QAC/B,uBAAuB;AAE9B,OAAO,eAAeC,2BAA2BC,MAKhD;IACC,OAAO,MAAML,KAAKM,WAAW,CAAC,OAAOC,KAAOC,6BAA6BD,IAAIF;AAC/E;AAEA,OAAO,eAAeG,6BACpBD,EAAM,EACNF,MAKC;IAED,MAAME,GAAGE,OAAO,CACdV,GAAG,CAAC,sCAAsC,EAAE,CAAC,mBAAmB,EAAEM,OAAOK,gBAAgB,EAAE,CAAC,EAAE,CAAC;IAEjG,MAAM,CAACC,OAAO,GAAG,MAAMJ,GACpBK,MAAM,CAAC;QACNC,aAAaX,gBAAgBW,WAAW;QACxCC,iBAAiBZ,gBAAgBY,eAAe;QAChDC,OAAOb,gBAAgBa,KAAK;IAC9B,GACCC,IAAI,CAACd,iBACLe,KAAK,CACJrB,IACEC,GAAGK,gBAAgBgB,EAAE,EAAEb,OAAOK,gBAAgB,GAC9Cb,GAAGK,gBAAgBiB,aAAa,EAAEd,OAAOc,aAAa,IAGzDC,KAAK,CAAC,GACNC,GAAG,CAAC;IACP,MAAMC,kBAAkBC,wCAAwCZ;IAChE,IAAIW,iBAAiB;QACnBE,4CAA4CF,iBAAiBjB;QAC7D,OAAO;IACT;IAEA,MAAME,GACHkB,MAAM,CAACxB,wBACPyB,GAAG,CAAC;QAACC,WAAW5B,GAAG,CAAC,KAAK,CAAC;IAAA,GAC1BkB,KAAK,CACJrB,IACEC,GAAGI,uBAAuBS,gBAAgB,EAAEL,OAAOK,gBAAgB,GACnEZ,OAAOG,uBAAuB2B,UAAU,GACxC9B,OAAOG,uBAAuB0B,SAAS;IAG7C,MAAME,WAAWnC,oBAAoB;IACrC,MAAMa,GAAGuB,MAAM,CAAC7B,wBAAwB8B,MAAM,CAAC;QAC7CrB,kBAAkBL,OAAOK,gBAAgB;QACzCsB,aAAarC,gBAAgBkC;QAC7BI,QAAQxC,qBAAqBoC;QAC7BK,WAAW,IAAIC,KAAKA,KAAKC,GAAG,KAAK/B,OAAOgC,UAAU,GAAG;IACvD;IACA,OAAOR;AACT;AAEA,OAAO,eAAeS,oBAAoBjC,MAGzC;IACC,MAAM,CAACM,OAAO,GAAG,MAAMX,KACpBY,MAAM,CAAC;QACNC,aAAaX,gBAAgBW,WAAW;QACxCC,iBAAiBZ,gBAAgBY,eAAe;QAChDC,OAAOb,gBAAgBa,KAAK;IAC9B,GACCC,IAAI,CAACd,iBACLe,KAAK,CACJrB,IACEC,GAAGK,gBAAgBgB,EAAE,EAAEb,OAAOK,gBAAgB,GAC9Cb,GAAGK,gBAAgBiB,aAAa,EAAEd,OAAOc,aAAa,IAGzDC,KAAK,CAAC;IACT,OAAOT,QAAQE,eAAe,CAACF,OAAOG,eAAe,IAAIH,OAAOI,KAAK,KAAK,YACtE;QAACF,aAAaF,OAAOE,WAAW;QAAEC,iBAAiBH,OAAOG,eAAe;IAAA,IACzE;AACN;AAEA,SAASS,wCACPZ,MAAuF;IAEvF,IAAI,CAACA,QAAQ,OAAO;IACpB,IAAI,CAACA,OAAOE,WAAW,EAAE,OAAO;IAChC,IAAIF,OAAOG,eAAe,EAAE,OAAO;IACnC,IAAIH,OAAOI,KAAK,KAAK,WAAW,OAAO;IACvC,OAAO;AACT;AAEA,SAASS,4CACPe,MAA4C,EAC5ClC,MAIC;IAEDF,qCAAqC;QAACoC;QAAQC,SAASnC,OAAOmC,OAAO;IAAA;IACrEhD,SAASiD,KAAK,CACZ;QACE/B,kBAAkBL,OAAOK,gBAAgB;QACzCS,eAAed,OAAOc,aAAa;QACnCoB;QACAC,SAASnC,OAAOmC,OAAO;IACzB,GACA;AAEJ"}
@@ -13,10 +13,14 @@ export declare function createRunnerInstancesWithBootstrapTokens(params: {
13
13
  reservationId?: string | null;
14
14
  }>;
15
15
  ttlSeconds: number;
16
- }): Promise<Array<{
17
- runnerInstanceId: string;
18
- bootstrapToken: string;
19
- }>>;
16
+ }): Promise<{
17
+ runnerInstances: Array<{
18
+ runnerInstanceId: string;
19
+ bootstrapToken: string;
20
+ requestIndex: number;
21
+ }>;
22
+ reservationUnavailable: boolean;
23
+ }>;
20
24
  export declare function exchangeRunnerBootstrapToken(params: {
21
25
  rawToken: string;
22
26
  ttlSeconds: number;
@@ -1 +1 @@
1
- {"version":3,"file":"runner-control-sessions.d.ts","sourceRoot":"","sources":["../../src/core/runner-control-sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,0BAA0B,CAAC;AAyBxE,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,cAGC;CACF;AAED,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,cAGC;CACF;AAED,wBAAsB,wCAAwC,CAAC,MAAM,EAAE;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,KAAK,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC,CAAC;IACH,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,KAAK,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAC,CAAC,CAAC,CAgCrE;AAED,wBAAsB,4BAA4B,CAAC,MAAM,EAAE;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,IAAI,CAAA;CAAC,CAAC,CA8BpF;AAED,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,MAAM;;;;eAmBjE;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2GzB;AAYD,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,GAAG,OAAO,CAAC,OAAO,CAAC,CAcnB;AAED,wBAAsB,yBAAyB,CAAC,gBAAgB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,iBAW9F"}
1
+ {"version":3,"file":"runner-control-sessions.d.ts","sourceRoot":"","sources":["../../src/core/runner-control-sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,0BAA0B,CAAC;AAiCxE,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,cAGC;CACF;AAED,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,cAGC;CACF;AAED,wBAAsB,wCAAwC,CAAC,MAAM,EAAE;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,KAAK,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC,CAAC;IACH,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC;IACV,eAAe,EAAE,KAAK,CAAC;QACrB,gBAAgB,EAAE,MAAM,CAAC;QACzB,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,sBAAsB,EAAE,OAAO,CAAC;CACjC,CAAC,CAyED;AAED,wBAAsB,4BAA4B,CAAC,MAAM,EAAE;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,IAAI,CAAA;CAAC,CAAC,CA+DpF;AAED,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,MAAM;;;;eAmBjE;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4IzB;AAYD,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,GAAG,OAAO,CAAC,OAAO,CAAC,CAcnB;AAED,wBAAsB,yBAAyB,CAAC,gBAAgB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,iBAW9F"}
@@ -2,15 +2,15 @@ import { logger } from '@shipfox/node-opentelemetry';
2
2
  import { extractDisplayPrefix, generateOpaqueToken, hashOpaqueToken } from '@shipfox/node-tokens';
3
3
  import { and, eq, gt, isNull, notInArray, or, sql } from 'drizzle-orm';
4
4
  import { config } from '#config.js';
5
- import { ReservationExpiredError, ReservationNotFoundError, RunnerInstanceAlreadyAssignedError, RunnerInstanceNotAssignableError } from '#core/errors.js';
5
+ import { getRunnerAssignmentRejectionReason, ReservationExpiredError, ReservationNotFoundError, RunnerInstanceAlreadyAssignedError, RunnerInstanceNotAssignableError } from '#core/errors.js';
6
6
  import { sanitizeRunnerLabels } from '#core/runner-labels.js';
7
7
  import { db } from '#db/db.js';
8
- import { assignRunnerInstancesTx } from '#db/runner-assignments.js';
9
- import { terminalStates } from '#db/runner-instances.js';
8
+ import { assignRunnerInstancesTx, validateRunnerReservationCapacityTx } from '#db/runner-assignments.js';
9
+ import { terminalStates } from '#db/runner-states.js';
10
10
  import { provisionerTokens } from '#db/schema/provisioner-tokens.js';
11
11
  import { runnerBootstrapTokens, runnerControlSessions } from '#db/schema/runner-control-sessions.js';
12
12
  import { providerRunners } from '#db/schema/runner-instances.js';
13
- import { recordRunnerReservationPromotionFailure } from '#metrics/index.js';
13
+ import { recordProviderRunnerAssignmentRejected, recordProviderRunnerControlSessionToAssignment, recordProviderRunnerCreatedToControlSession, recordRunnerReservationCapacityFailure, recordRunnerReservationPromotionFailure } from '#metrics/index.js';
14
14
  import { issueRunnerActivationTokenTx } from './runner-activation.js';
15
15
  export class RunnerBootstrapTokenInvalidError extends Error {
16
16
  constructor(){
@@ -27,21 +27,63 @@ export class RunnerControlSessionInvalidError extends Error {
27
27
  export async function createRunnerInstancesWithBootstrapTokens(params) {
28
28
  const expiresAt = new Date(Date.now() + params.ttlSeconds * 1000);
29
29
  return await db().transaction(async (tx)=>{
30
- const instances = await tx.insert(providerRunners).values(params.runnerInstances.map((runner)=>({
30
+ const reservationValidation = await validateRunnerReservationCapacityTx(tx, {
31
+ provisionerId: params.provisionerId,
32
+ requests: params.runnerInstances.reduce((requests, runner)=>{
33
+ if (!runner.reservationId) return requests;
34
+ const request = requests.find((candidate)=>candidate.reservationId === runner.reservationId);
35
+ if (request) request.count += 1;
36
+ else requests.push({
37
+ reservationId: runner.reservationId,
38
+ count: 1
39
+ });
40
+ return requests;
41
+ }, [])
42
+ });
43
+ for (const { reason, count } of reservationValidation.unavailableByReservation.values())recordRunnerReservationCapacityFailure(reason, count);
44
+ const remainingAcceptedByReservation = new Map(reservationValidation.acceptedByReservation);
45
+ const runnerInstances = params.runnerInstances.flatMap((runner, requestIndex)=>{
46
+ if (!runner.reservationId) return [
47
+ {
48
+ ...runner,
49
+ requestIndex
50
+ }
51
+ ];
52
+ const remaining = remainingAcceptedByReservation.get(runner.reservationId) ?? 0;
53
+ if (remaining === 0) return [];
54
+ remainingAcceptedByReservation.set(runner.reservationId, remaining - 1);
55
+ return [
56
+ {
57
+ ...runner,
58
+ requestIndex
59
+ }
60
+ ];
61
+ });
62
+ if (runnerInstances.length === 0) return {
63
+ runnerInstances: [],
64
+ reservationUnavailable: reservationValidation.unavailableByReservation.size > 0
65
+ };
66
+ const instances = await tx.insert(providerRunners).values(runnerInstances.map((runner)=>({
31
67
  provisionerId: params.provisionerId,
32
68
  providerKind: params.providerKind ?? null,
33
69
  templateKey: runner.templateKey ?? null,
34
70
  intendedReservationId: runner.reservationId ?? null,
71
+ launchKind: runner.reservationId ? 'demand' : 'warm',
35
72
  state: 'starting',
36
73
  labels: [],
37
74
  reportedAt: new Date()
38
75
  }))).returning({
39
76
  id: providerRunners.id
40
77
  });
41
- const results = instances.map((instance)=>({
78
+ const results = instances.map((instance, index)=>{
79
+ const runner = runnerInstances[index];
80
+ if (!runner) throw new Error('Runner instance insert returned an unexpected row count');
81
+ return {
42
82
  runnerInstanceId: instance.id,
43
- bootstrapToken: generateOpaqueToken('runnerBootstrapToken')
44
- }));
83
+ bootstrapToken: generateOpaqueToken('runnerBootstrapToken'),
84
+ requestIndex: runner.requestIndex
85
+ };
86
+ });
45
87
  await tx.insert(runnerBootstrapTokens).values(results.map((result)=>({
46
88
  runnerInstanceId: result.runnerInstanceId,
47
89
  provisionerId: params.provisionerId,
@@ -49,17 +91,26 @@ export async function createRunnerInstancesWithBootstrapTokens(params) {
49
91
  prefix: extractDisplayPrefix(result.bootstrapToken),
50
92
  expiresAt
51
93
  })));
52
- return results;
94
+ return {
95
+ runnerInstances: results,
96
+ reservationUnavailable: reservationValidation.unavailableByReservation.size > 0
97
+ };
53
98
  });
54
99
  }
55
100
  export async function exchangeRunnerBootstrapToken(params) {
56
101
  const controlSessionToken = generateOpaqueToken('runnerControlSession');
57
102
  const expiresAt = new Date(Date.now() + params.ttlSeconds * 1000);
58
- return await db().transaction(async (tx)=>{
103
+ const result = await db().transaction(async (tx)=>{
59
104
  const [bootstrap] = await tx.update(runnerBootstrapTokens).set({
60
105
  consumedAt: sql`now()`
61
106
  }).where(and(eq(runnerBootstrapTokens.hashedToken, hashOpaqueToken(params.rawToken)), isNull(runnerBootstrapTokens.consumedAt), isNull(runnerBootstrapTokens.revokedAt), gt(runnerBootstrapTokens.expiresAt, sql`now()`))).returning();
62
107
  if (!bootstrap) throw new RunnerBootstrapTokenInvalidError();
108
+ const [runner] = await tx.select({
109
+ runnerInstanceId: providerRunners.id,
110
+ createdAt: providerRunners.createdAt,
111
+ provider: providerRunners.providerKind,
112
+ launchKind: providerRunners.launchKind
113
+ }).from(providerRunners).where(and(eq(providerRunners.id, bootstrap.runnerInstanceId), eq(providerRunners.provisionerId, bootstrap.provisionerId))).limit(1);
63
114
  const [session] = await tx.insert(runnerControlSessions).values({
64
115
  runnerInstanceId: bootstrap.runnerInstanceId,
65
116
  provisionerId: bootstrap.provisionerId,
@@ -67,15 +118,28 @@ export async function exchangeRunnerBootstrapToken(params) {
67
118
  prefix: extractDisplayPrefix(controlSessionToken),
68
119
  expiresAt
69
120
  }).returning({
70
- id: runnerControlSessions.id
121
+ createdAt: runnerControlSessions.createdAt
71
122
  });
72
123
  if (!session) throw new Error('Runner control session insert returned no row');
73
124
  return {
125
+ runner,
126
+ session,
74
127
  runnerInstanceId: bootstrap.runnerInstanceId,
75
128
  controlSessionToken,
76
129
  expiresAt
77
130
  };
78
131
  });
132
+ if (result.runner) recordProviderRunnerCreatedToControlSession({
133
+ durationMilliseconds: result.session.createdAt.getTime() - result.runner.createdAt.getTime(),
134
+ provider: result.runner.provider,
135
+ launchKind: result.runner.launchKind,
136
+ runnerInstanceId: result.runner.runnerInstanceId
137
+ });
138
+ return {
139
+ runnerInstanceId: result.runnerInstanceId,
140
+ controlSessionToken: result.controlSessionToken,
141
+ expiresAt: result.expiresAt
142
+ };
79
143
  }
80
144
  export async function resolveRunnerControlSession(rawToken) {
81
145
  const [session] = await db().select({
@@ -86,7 +150,7 @@ export async function resolveRunnerControlSession(rawToken) {
86
150
  return session;
87
151
  }
88
152
  export async function enrollRunnerControlSession(params) {
89
- return await db().transaction(async (tx)=>{
153
+ const result = await db().transaction(async (tx)=>{
90
154
  const [current] = await tx.select({
91
155
  intendedReservationId: providerRunners.intendedReservationId,
92
156
  provisionerScope: provisionerTokens.scope
@@ -125,23 +189,41 @@ export async function enrollRunnerControlSession(params) {
125
189
  if (!updated) throw new RunnerControlSessionInvalidError();
126
190
  await updateRunnerControlSessionLastSeen(tx, params.runnerInstanceId, params.provisionerId);
127
191
  const intendedReservationId = updated.intendedReservationId;
128
- if (!intendedReservationId) return null;
192
+ if (!intendedReservationId) return {
193
+ activationToken: null,
194
+ controlSessionToAssignment: [],
195
+ assignmentRejectedReason: null,
196
+ promotionFailureReason: null
197
+ };
129
198
  try {
130
- return await tx.transaction(async (promotionTx)=>{
131
- await assignRunnerInstancesTx(promotionTx, {
199
+ const promotion = await tx.transaction(async (promotionTx)=>{
200
+ const assignment = await assignRunnerInstancesTx(promotionTx, {
132
201
  provisionerId: params.provisionerId,
133
202
  reservationId: intendedReservationId,
134
203
  runnerInstanceIds: [
135
204
  params.runnerInstanceId
136
- ]
205
+ ],
206
+ surface: 'enrollment'
137
207
  });
138
- return await issueRunnerActivationTokenTx(promotionTx, {
208
+ const activationToken = await issueRunnerActivationTokenTx(promotionTx, {
139
209
  runnerInstanceId: params.runnerInstanceId,
140
210
  provisionerId: params.provisionerId,
141
- ttlSeconds: config.RUNNER_ACTIVATION_TOKEN_TTL_SECONDS
211
+ ttlSeconds: config.RUNNER_ACTIVATION_TOKEN_TTL_SECONDS,
212
+ surface: 'enrollment'
142
213
  });
214
+ return {
215
+ activationToken,
216
+ controlSessionToAssignment: assignment.controlSessionToAssignment
217
+ };
143
218
  });
219
+ return {
220
+ activationToken: promotion.activationToken,
221
+ controlSessionToAssignment: promotion.controlSessionToAssignment,
222
+ assignmentRejectedReason: null,
223
+ promotionFailureReason: null
224
+ };
144
225
  } catch (error) {
226
+ const assignmentRejectionReason = getRunnerAssignmentRejectionReason(error);
145
227
  const expectedFailureReason = getRunnerReservationPromotionFailureReason(error);
146
228
  const details = {
147
229
  err: error,
@@ -150,14 +232,28 @@ export async function enrollRunnerControlSession(params) {
150
232
  reservationId: intendedReservationId
151
233
  };
152
234
  if (expectedFailureReason) {
153
- recordRunnerReservationPromotionFailure(expectedFailureReason);
154
235
  logger().debug(details, 'Runner reservation could not be promoted during enrollment');
155
236
  } else {
156
237
  logger().error(details, 'Unexpected failure promoting runner reservation during enrollment');
157
238
  }
158
- return null;
239
+ return {
240
+ activationToken: null,
241
+ controlSessionToAssignment: [],
242
+ assignmentRejectedReason: assignmentRejectionReason,
243
+ promotionFailureReason: expectedFailureReason
244
+ };
159
245
  }
160
246
  });
247
+ for (const observation of result.controlSessionToAssignment)recordProviderRunnerControlSessionToAssignment(observation);
248
+ // These counters intentionally overlap: assignment_rejected records the assignment-facing
249
+ // reason, while reservation_promotion_failure records the enrollment recovery outcome. They
250
+ // answer different questions and must not be summed.
251
+ if (result.assignmentRejectedReason) recordProviderRunnerAssignmentRejected({
252
+ reason: result.assignmentRejectedReason,
253
+ surface: 'enrollment'
254
+ });
255
+ if (result.promotionFailureReason) recordRunnerReservationPromotionFailure(result.promotionFailureReason);
256
+ return result.activationToken;
161
257
  }
162
258
  function getRunnerReservationPromotionFailureReason(error) {
163
259
  if (error instanceof ReservationExpiredError) return 'reservation-expired';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/runner-control-sessions.ts"],"sourcesContent":["import type {RunnerToolCapabilitiesDto} from '@shipfox/api-runners-dto';\nimport type {NodePgDatabase} from '@shipfox/node-drizzle';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {extractDisplayPrefix, generateOpaqueToken, hashOpaqueToken} from '@shipfox/node-tokens';\nimport {and, eq, gt, isNull, notInArray, or, sql} from 'drizzle-orm';\nimport {config} from '#config.js';\nimport {\n ReservationExpiredError,\n ReservationNotFoundError,\n RunnerInstanceAlreadyAssignedError,\n RunnerInstanceNotAssignableError,\n} from '#core/errors.js';\nimport {sanitizeRunnerLabels} from '#core/runner-labels.js';\nimport {db, type schema, type Tx} from '#db/db.js';\nimport {assignRunnerInstancesTx} from '#db/runner-assignments.js';\nimport {terminalStates} from '#db/runner-instances.js';\nimport {provisionerTokens} from '#db/schema/provisioner-tokens.js';\nimport {runnerBootstrapTokens, runnerControlSessions} from '#db/schema/runner-control-sessions.js';\nimport {providerRunners} from '#db/schema/runner-instances.js';\nimport {\n type RunnerReservationPromotionFailureReason,\n recordRunnerReservationPromotionFailure,\n} from '#metrics/index.js';\nimport {issueRunnerActivationTokenTx} from './runner-activation.js';\n\nexport class RunnerBootstrapTokenInvalidError extends Error {\n constructor() {\n super('Runner bootstrap token is invalid, expired, or has already been used');\n this.name = 'RunnerBootstrapTokenInvalidError';\n }\n}\n\nexport class RunnerControlSessionInvalidError extends Error {\n constructor() {\n super('Runner control session is invalid, expired, or closed');\n this.name = 'RunnerControlSessionInvalidError';\n }\n}\n\nexport async function createRunnerInstancesWithBootstrapTokens(params: {\n provisionerId: string;\n providerKind?: string | null;\n runnerInstances: Array<{\n templateKey?: string | null;\n reservationId?: string | null;\n }>;\n ttlSeconds: number;\n}): Promise<Array<{runnerInstanceId: string; bootstrapToken: string}>> {\n const expiresAt = new Date(Date.now() + params.ttlSeconds * 1000);\n return await db().transaction(async (tx) => {\n const instances = await tx\n .insert(providerRunners)\n .values(\n params.runnerInstances.map((runner) => ({\n provisionerId: params.provisionerId,\n providerKind: params.providerKind ?? null,\n templateKey: runner.templateKey ?? null,\n intendedReservationId: runner.reservationId ?? null,\n state: 'starting' as const,\n labels: [],\n reportedAt: new Date(),\n })),\n )\n .returning({id: providerRunners.id});\n const results = instances.map((instance) => ({\n runnerInstanceId: instance.id,\n bootstrapToken: generateOpaqueToken('runnerBootstrapToken'),\n }));\n await tx.insert(runnerBootstrapTokens).values(\n results.map((result) => ({\n runnerInstanceId: result.runnerInstanceId,\n provisionerId: params.provisionerId,\n hashedToken: hashOpaqueToken(result.bootstrapToken),\n prefix: extractDisplayPrefix(result.bootstrapToken),\n expiresAt,\n })),\n );\n return results;\n });\n}\n\nexport async function exchangeRunnerBootstrapToken(params: {\n rawToken: string;\n ttlSeconds: number;\n}): Promise<{runnerInstanceId: string; controlSessionToken: string; expiresAt: Date}> {\n const controlSessionToken = generateOpaqueToken('runnerControlSession');\n const expiresAt = new Date(Date.now() + params.ttlSeconds * 1000);\n return await db().transaction(async (tx) => {\n const [bootstrap] = await tx\n .update(runnerBootstrapTokens)\n .set({consumedAt: sql`now()`})\n .where(\n and(\n eq(runnerBootstrapTokens.hashedToken, hashOpaqueToken(params.rawToken)),\n isNull(runnerBootstrapTokens.consumedAt),\n isNull(runnerBootstrapTokens.revokedAt),\n gt(runnerBootstrapTokens.expiresAt, sql`now()`),\n ),\n )\n .returning();\n if (!bootstrap) throw new RunnerBootstrapTokenInvalidError();\n const [session] = await tx\n .insert(runnerControlSessions)\n .values({\n runnerInstanceId: bootstrap.runnerInstanceId,\n provisionerId: bootstrap.provisionerId,\n hashedToken: hashOpaqueToken(controlSessionToken),\n prefix: extractDisplayPrefix(controlSessionToken),\n expiresAt,\n })\n .returning({id: runnerControlSessions.id});\n if (!session) throw new Error('Runner control session insert returned no row');\n return {runnerInstanceId: bootstrap.runnerInstanceId, controlSessionToken, expiresAt};\n });\n}\n\nexport async function resolveRunnerControlSession(rawToken: string) {\n const [session] = await db()\n .select({\n id: runnerControlSessions.id,\n runnerInstanceId: runnerControlSessions.runnerInstanceId,\n provisionerId: runnerControlSessions.provisionerId,\n })\n .from(runnerControlSessions)\n .leftJoin(provisionerTokens, eq(provisionerTokens.id, runnerControlSessions.provisionerId))\n .where(\n and(\n eq(runnerControlSessions.hashedToken, hashOpaqueToken(rawToken)),\n isNull(runnerControlSessions.closedAt),\n or(isNull(provisionerTokens.id), isNull(provisionerTokens.revokedAt)),\n gt(runnerControlSessions.expiresAt, sql`now()`),\n ),\n )\n .limit(1);\n return session;\n}\n\nexport async function enrollRunnerControlSession(params: {\n runnerInstanceId: string;\n provisionerId: string;\n labels: string[];\n capabilities?: RunnerToolCapabilitiesDto | null;\n providerKind: string;\n protocolVersion: string;\n}): Promise<string | null> {\n return await db().transaction(async (tx) => {\n const [current] = await tx\n .select({\n intendedReservationId: providerRunners.intendedReservationId,\n provisionerScope: provisionerTokens.scope,\n })\n .from(providerRunners)\n .innerJoin(provisionerTokens, eq(provisionerTokens.id, providerRunners.provisionerId))\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n ),\n );\n if (!current) throw new RunnerControlSessionInvalidError();\n if (current.intendedReservationId)\n await tx.execute(\n sql`select pg_advisory_xact_lock(hashtext(${`runners_assignment:${params.provisionerId}:${current.intendedReservationId}`}))`,\n );\n\n // Provider reports may populate reservationId before the assignment commits. assignedAt is\n // written by the assignment transaction, so keep the guard on that write boundary.\n const [metadataUpdated] = await tx\n .update(providerRunners)\n .set({\n labels: sanitizeRunnerLabels(params.labels, {\n scope: current.provisionerScope,\n source: 'runner control enrollment',\n }),\n providerKind: params.providerKind,\n protocolVersion: params.protocolVersion,\n capabilities: params.capabilities ?? null,\n })\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n isNull(providerRunners.assignedAt),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n )\n .returning({id: providerRunners.id});\n if (!metadataUpdated)\n logger().debug(\n {runnerInstanceId: params.runnerInstanceId, provisionerId: params.provisionerId},\n 'Runner enrollment metadata update skipped for assigned or terminal runner',\n );\n\n const [updated] = await tx\n .update(providerRunners)\n .set({\n state: 'running',\n reportedAt: sql`now()`,\n updatedAt: sql`now()`,\n })\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n )\n .returning({\n id: providerRunners.id,\n intendedReservationId: providerRunners.intendedReservationId,\n });\n if (!updated) throw new RunnerControlSessionInvalidError();\n\n await updateRunnerControlSessionLastSeen(tx, params.runnerInstanceId, params.provisionerId);\n\n const intendedReservationId = updated.intendedReservationId;\n if (!intendedReservationId) return null;\n\n try {\n return await tx.transaction(async (promotionTx) => {\n await assignRunnerInstancesTx(promotionTx, {\n provisionerId: params.provisionerId,\n reservationId: intendedReservationId,\n runnerInstanceIds: [params.runnerInstanceId],\n });\n return await issueRunnerActivationTokenTx(promotionTx, {\n runnerInstanceId: params.runnerInstanceId,\n provisionerId: params.provisionerId,\n ttlSeconds: config.RUNNER_ACTIVATION_TOKEN_TTL_SECONDS,\n });\n });\n } catch (error) {\n const expectedFailureReason = getRunnerReservationPromotionFailureReason(error);\n const details = {\n err: error,\n runnerInstanceId: params.runnerInstanceId,\n provisionerId: params.provisionerId,\n reservationId: intendedReservationId,\n };\n if (expectedFailureReason) {\n recordRunnerReservationPromotionFailure(expectedFailureReason);\n logger().debug(details, 'Runner reservation could not be promoted during enrollment');\n } else {\n logger().error(\n details,\n 'Unexpected failure promoting runner reservation during enrollment',\n );\n }\n return null;\n }\n });\n}\n\nfunction getRunnerReservationPromotionFailureReason(\n error: unknown,\n): RunnerReservationPromotionFailureReason | null {\n if (error instanceof ReservationExpiredError) return 'reservation-expired';\n if (error instanceof ReservationNotFoundError) return 'reservation-not-found';\n if (error instanceof RunnerInstanceAlreadyAssignedError) return 'already-assigned';\n if (error instanceof RunnerInstanceNotAssignableError) return 'not-assignable';\n return null;\n}\n\nexport async function attachRunnerControlProviderId(params: {\n runnerInstanceId: string;\n provisionerId: string;\n providerRunnerId: string;\n}): Promise<boolean> {\n const rows = await db()\n .update(providerRunners)\n .set({providerRunnerId: params.providerRunnerId, updatedAt: sql`now()`})\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n isNull(providerRunners.providerRunnerId),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n )\n .returning({id: providerRunners.id});\n return rows.length === 1;\n}\n\nexport async function touchRunnerControlSession(runnerInstanceId: string, provisionerId: string) {\n await updateRunnerControlSessionLastSeen(db(), runnerInstanceId, provisionerId);\n await db()\n .update(providerRunners)\n .set({reportedAt: sql`now()`, updatedAt: sql`now()`})\n .where(\n and(\n eq(providerRunners.id, runnerInstanceId),\n eq(providerRunners.provisionerId, provisionerId),\n ),\n );\n}\n\nasync function updateRunnerControlSessionLastSeen(\n tx: Tx | NodePgDatabase<typeof schema>,\n runnerInstanceId: string,\n provisionerId: string,\n) {\n await tx\n .update(runnerControlSessions)\n .set({lastSeenAt: sql`now()`})\n .where(\n and(\n eq(runnerControlSessions.runnerInstanceId, runnerInstanceId),\n eq(runnerControlSessions.provisionerId, provisionerId),\n isNull(runnerControlSessions.closedAt),\n ),\n );\n}\n"],"names":["logger","extractDisplayPrefix","generateOpaqueToken","hashOpaqueToken","and","eq","gt","isNull","notInArray","or","sql","config","ReservationExpiredError","ReservationNotFoundError","RunnerInstanceAlreadyAssignedError","RunnerInstanceNotAssignableError","sanitizeRunnerLabels","db","assignRunnerInstancesTx","terminalStates","provisionerTokens","runnerBootstrapTokens","runnerControlSessions","providerRunners","recordRunnerReservationPromotionFailure","issueRunnerActivationTokenTx","RunnerBootstrapTokenInvalidError","Error","name","RunnerControlSessionInvalidError","createRunnerInstancesWithBootstrapTokens","params","expiresAt","Date","now","ttlSeconds","transaction","tx","instances","insert","values","runnerInstances","map","runner","provisionerId","providerKind","templateKey","intendedReservationId","reservationId","state","labels","reportedAt","returning","id","results","instance","runnerInstanceId","bootstrapToken","result","hashedToken","prefix","exchangeRunnerBootstrapToken","controlSessionToken","bootstrap","update","set","consumedAt","where","rawToken","revokedAt","session","resolveRunnerControlSession","select","from","leftJoin","closedAt","limit","enrollRunnerControlSession","current","provisionerScope","scope","innerJoin","execute","metadataUpdated","source","protocolVersion","capabilities","assignedAt","debug","updated","updatedAt","updateRunnerControlSessionLastSeen","promotionTx","runnerInstanceIds","RUNNER_ACTIVATION_TOKEN_TTL_SECONDS","error","expectedFailureReason","getRunnerReservationPromotionFailureReason","details","err","attachRunnerControlProviderId","rows","providerRunnerId","length","touchRunnerControlSession","lastSeenAt"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/runner-control-sessions.ts"],"sourcesContent":["import type {RunnerToolCapabilitiesDto} from '@shipfox/api-runners-dto';\nimport type {NodePgDatabase} from '@shipfox/node-drizzle';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {extractDisplayPrefix, generateOpaqueToken, hashOpaqueToken} from '@shipfox/node-tokens';\nimport {and, eq, gt, isNull, notInArray, or, sql} from 'drizzle-orm';\nimport {config} from '#config.js';\nimport {\n getRunnerAssignmentRejectionReason,\n ReservationExpiredError,\n ReservationNotFoundError,\n RunnerInstanceAlreadyAssignedError,\n RunnerInstanceNotAssignableError,\n} from '#core/errors.js';\nimport {sanitizeRunnerLabels} from '#core/runner-labels.js';\nimport {db, type schema, type Tx} from '#db/db.js';\nimport {\n assignRunnerInstancesTx,\n validateRunnerReservationCapacityTx,\n} from '#db/runner-assignments.js';\nimport {terminalStates} from '#db/runner-states.js';\nimport {provisionerTokens} from '#db/schema/provisioner-tokens.js';\nimport {runnerBootstrapTokens, runnerControlSessions} from '#db/schema/runner-control-sessions.js';\nimport {providerRunners} from '#db/schema/runner-instances.js';\nimport {\n type RunnerReservationPromotionFailureReason,\n recordProviderRunnerAssignmentRejected,\n recordProviderRunnerControlSessionToAssignment,\n recordProviderRunnerCreatedToControlSession,\n recordRunnerReservationCapacityFailure,\n recordRunnerReservationPromotionFailure,\n} from '#metrics/index.js';\nimport {issueRunnerActivationTokenTx} from './runner-activation.js';\n\nexport class RunnerBootstrapTokenInvalidError extends Error {\n constructor() {\n super('Runner bootstrap token is invalid, expired, or has already been used');\n this.name = 'RunnerBootstrapTokenInvalidError';\n }\n}\n\nexport class RunnerControlSessionInvalidError extends Error {\n constructor() {\n super('Runner control session is invalid, expired, or closed');\n this.name = 'RunnerControlSessionInvalidError';\n }\n}\n\nexport async function createRunnerInstancesWithBootstrapTokens(params: {\n provisionerId: string;\n providerKind?: string | null;\n runnerInstances: Array<{\n templateKey?: string | null;\n reservationId?: string | null;\n }>;\n ttlSeconds: number;\n}): Promise<{\n runnerInstances: Array<{\n runnerInstanceId: string;\n bootstrapToken: string;\n requestIndex: number;\n }>;\n reservationUnavailable: boolean;\n}> {\n const expiresAt = new Date(Date.now() + params.ttlSeconds * 1000);\n return await db().transaction(async (tx) => {\n const reservationValidation = await validateRunnerReservationCapacityTx(tx, {\n provisionerId: params.provisionerId,\n requests: params.runnerInstances.reduce(\n (requests, runner) => {\n if (!runner.reservationId) return requests;\n const request = requests.find(\n (candidate) => candidate.reservationId === runner.reservationId,\n );\n if (request) request.count += 1;\n else requests.push({reservationId: runner.reservationId, count: 1});\n return requests;\n },\n [] as Array<{reservationId: string; count: number}>,\n ),\n });\n for (const {reason, count} of reservationValidation.unavailableByReservation.values())\n recordRunnerReservationCapacityFailure(reason, count);\n const remainingAcceptedByReservation = new Map(reservationValidation.acceptedByReservation);\n const runnerInstances = params.runnerInstances.flatMap((runner, requestIndex) => {\n if (!runner.reservationId) return [{...runner, requestIndex}];\n const remaining = remainingAcceptedByReservation.get(runner.reservationId) ?? 0;\n if (remaining === 0) return [];\n remainingAcceptedByReservation.set(runner.reservationId, remaining - 1);\n return [{...runner, requestIndex}];\n });\n\n if (runnerInstances.length === 0)\n return {\n runnerInstances: [],\n reservationUnavailable: reservationValidation.unavailableByReservation.size > 0,\n };\n\n const instances = await tx\n .insert(providerRunners)\n .values(\n runnerInstances.map((runner) => ({\n provisionerId: params.provisionerId,\n providerKind: params.providerKind ?? null,\n templateKey: runner.templateKey ?? null,\n intendedReservationId: runner.reservationId ?? null,\n launchKind: runner.reservationId ? ('demand' as const) : ('warm' as const),\n state: 'starting' as const,\n labels: [],\n reportedAt: new Date(),\n })),\n )\n .returning({id: providerRunners.id});\n const results = instances.map((instance, index) => {\n const runner = runnerInstances[index];\n if (!runner) throw new Error('Runner instance insert returned an unexpected row count');\n return {\n runnerInstanceId: instance.id,\n bootstrapToken: generateOpaqueToken('runnerBootstrapToken'),\n requestIndex: runner.requestIndex,\n };\n });\n await tx.insert(runnerBootstrapTokens).values(\n results.map((result) => ({\n runnerInstanceId: result.runnerInstanceId,\n provisionerId: params.provisionerId,\n hashedToken: hashOpaqueToken(result.bootstrapToken),\n prefix: extractDisplayPrefix(result.bootstrapToken),\n expiresAt,\n })),\n );\n return {\n runnerInstances: results,\n reservationUnavailable: reservationValidation.unavailableByReservation.size > 0,\n };\n });\n}\n\nexport async function exchangeRunnerBootstrapToken(params: {\n rawToken: string;\n ttlSeconds: number;\n}): Promise<{runnerInstanceId: string; controlSessionToken: string; expiresAt: Date}> {\n const controlSessionToken = generateOpaqueToken('runnerControlSession');\n const expiresAt = new Date(Date.now() + params.ttlSeconds * 1000);\n const result = await db().transaction(async (tx) => {\n const [bootstrap] = await tx\n .update(runnerBootstrapTokens)\n .set({consumedAt: sql`now()`})\n .where(\n and(\n eq(runnerBootstrapTokens.hashedToken, hashOpaqueToken(params.rawToken)),\n isNull(runnerBootstrapTokens.consumedAt),\n isNull(runnerBootstrapTokens.revokedAt),\n gt(runnerBootstrapTokens.expiresAt, sql`now()`),\n ),\n )\n .returning();\n if (!bootstrap) throw new RunnerBootstrapTokenInvalidError();\n const [runner] = await tx\n .select({\n runnerInstanceId: providerRunners.id,\n createdAt: providerRunners.createdAt,\n provider: providerRunners.providerKind,\n launchKind: providerRunners.launchKind,\n })\n .from(providerRunners)\n .where(\n and(\n eq(providerRunners.id, bootstrap.runnerInstanceId),\n eq(providerRunners.provisionerId, bootstrap.provisionerId),\n ),\n )\n .limit(1);\n const [session] = await tx\n .insert(runnerControlSessions)\n .values({\n runnerInstanceId: bootstrap.runnerInstanceId,\n provisionerId: bootstrap.provisionerId,\n hashedToken: hashOpaqueToken(controlSessionToken),\n prefix: extractDisplayPrefix(controlSessionToken),\n expiresAt,\n })\n .returning({createdAt: runnerControlSessions.createdAt});\n if (!session) throw new Error('Runner control session insert returned no row');\n return {\n runner,\n session,\n runnerInstanceId: bootstrap.runnerInstanceId,\n controlSessionToken,\n expiresAt,\n };\n });\n if (result.runner)\n recordProviderRunnerCreatedToControlSession({\n durationMilliseconds: result.session.createdAt.getTime() - result.runner.createdAt.getTime(),\n provider: result.runner.provider,\n launchKind: result.runner.launchKind,\n runnerInstanceId: result.runner.runnerInstanceId,\n });\n return {\n runnerInstanceId: result.runnerInstanceId,\n controlSessionToken: result.controlSessionToken,\n expiresAt: result.expiresAt,\n };\n}\n\nexport async function resolveRunnerControlSession(rawToken: string) {\n const [session] = await db()\n .select({\n id: runnerControlSessions.id,\n runnerInstanceId: runnerControlSessions.runnerInstanceId,\n provisionerId: runnerControlSessions.provisionerId,\n })\n .from(runnerControlSessions)\n .leftJoin(provisionerTokens, eq(provisionerTokens.id, runnerControlSessions.provisionerId))\n .where(\n and(\n eq(runnerControlSessions.hashedToken, hashOpaqueToken(rawToken)),\n isNull(runnerControlSessions.closedAt),\n or(isNull(provisionerTokens.id), isNull(provisionerTokens.revokedAt)),\n gt(runnerControlSessions.expiresAt, sql`now()`),\n ),\n )\n .limit(1);\n return session;\n}\n\nexport async function enrollRunnerControlSession(params: {\n runnerInstanceId: string;\n provisionerId: string;\n labels: string[];\n capabilities?: RunnerToolCapabilitiesDto | null;\n providerKind: string;\n protocolVersion: string;\n}): Promise<string | null> {\n const result = await db().transaction(async (tx) => {\n const [current] = await tx\n .select({\n intendedReservationId: providerRunners.intendedReservationId,\n provisionerScope: provisionerTokens.scope,\n })\n .from(providerRunners)\n .innerJoin(provisionerTokens, eq(provisionerTokens.id, providerRunners.provisionerId))\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n ),\n );\n if (!current) throw new RunnerControlSessionInvalidError();\n if (current.intendedReservationId)\n await tx.execute(\n sql`select pg_advisory_xact_lock(hashtext(${`runners_assignment:${params.provisionerId}:${current.intendedReservationId}`}))`,\n );\n\n // Provider reports may populate reservationId before the assignment commits. assignedAt is\n // written by the assignment transaction, so keep the guard on that write boundary.\n const [metadataUpdated] = await tx\n .update(providerRunners)\n .set({\n labels: sanitizeRunnerLabels(params.labels, {\n scope: current.provisionerScope,\n source: 'runner control enrollment',\n }),\n providerKind: params.providerKind,\n protocolVersion: params.protocolVersion,\n capabilities: params.capabilities ?? null,\n })\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n isNull(providerRunners.assignedAt),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n )\n .returning({id: providerRunners.id});\n if (!metadataUpdated)\n logger().debug(\n {runnerInstanceId: params.runnerInstanceId, provisionerId: params.provisionerId},\n 'Runner enrollment metadata update skipped for assigned or terminal runner',\n );\n\n const [updated] = await tx\n .update(providerRunners)\n .set({\n state: 'running',\n reportedAt: sql`now()`,\n updatedAt: sql`now()`,\n })\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n )\n .returning({\n id: providerRunners.id,\n intendedReservationId: providerRunners.intendedReservationId,\n });\n if (!updated) throw new RunnerControlSessionInvalidError();\n\n await updateRunnerControlSessionLastSeen(tx, params.runnerInstanceId, params.provisionerId);\n\n const intendedReservationId = updated.intendedReservationId;\n if (!intendedReservationId)\n return {\n activationToken: null,\n controlSessionToAssignment: [],\n assignmentRejectedReason: null,\n promotionFailureReason: null,\n };\n\n try {\n const promotion = await tx.transaction(async (promotionTx) => {\n const assignment = await assignRunnerInstancesTx(promotionTx, {\n provisionerId: params.provisionerId,\n reservationId: intendedReservationId,\n runnerInstanceIds: [params.runnerInstanceId],\n surface: 'enrollment',\n });\n const activationToken = await issueRunnerActivationTokenTx(promotionTx, {\n runnerInstanceId: params.runnerInstanceId,\n provisionerId: params.provisionerId,\n ttlSeconds: config.RUNNER_ACTIVATION_TOKEN_TTL_SECONDS,\n surface: 'enrollment',\n });\n return {activationToken, controlSessionToAssignment: assignment.controlSessionToAssignment};\n });\n return {\n activationToken: promotion.activationToken,\n controlSessionToAssignment: promotion.controlSessionToAssignment,\n assignmentRejectedReason: null,\n promotionFailureReason: null,\n };\n } catch (error) {\n const assignmentRejectionReason = getRunnerAssignmentRejectionReason(error);\n const expectedFailureReason = getRunnerReservationPromotionFailureReason(error);\n const details = {\n err: error,\n runnerInstanceId: params.runnerInstanceId,\n provisionerId: params.provisionerId,\n reservationId: intendedReservationId,\n };\n if (expectedFailureReason) {\n logger().debug(details, 'Runner reservation could not be promoted during enrollment');\n } else {\n logger().error(\n details,\n 'Unexpected failure promoting runner reservation during enrollment',\n );\n }\n return {\n activationToken: null,\n controlSessionToAssignment: [],\n assignmentRejectedReason: assignmentRejectionReason,\n promotionFailureReason: expectedFailureReason,\n };\n }\n });\n for (const observation of result.controlSessionToAssignment)\n recordProviderRunnerControlSessionToAssignment(observation);\n // These counters intentionally overlap: assignment_rejected records the assignment-facing\n // reason, while reservation_promotion_failure records the enrollment recovery outcome. They\n // answer different questions and must not be summed.\n if (result.assignmentRejectedReason)\n recordProviderRunnerAssignmentRejected({\n reason: result.assignmentRejectedReason,\n surface: 'enrollment',\n });\n if (result.promotionFailureReason)\n recordRunnerReservationPromotionFailure(result.promotionFailureReason);\n return result.activationToken;\n}\n\nfunction getRunnerReservationPromotionFailureReason(\n error: unknown,\n): RunnerReservationPromotionFailureReason | null {\n if (error instanceof ReservationExpiredError) return 'reservation-expired';\n if (error instanceof ReservationNotFoundError) return 'reservation-not-found';\n if (error instanceof RunnerInstanceAlreadyAssignedError) return 'already-assigned';\n if (error instanceof RunnerInstanceNotAssignableError) return 'not-assignable';\n return null;\n}\n\nexport async function attachRunnerControlProviderId(params: {\n runnerInstanceId: string;\n provisionerId: string;\n providerRunnerId: string;\n}): Promise<boolean> {\n const rows = await db()\n .update(providerRunners)\n .set({providerRunnerId: params.providerRunnerId, updatedAt: sql`now()`})\n .where(\n and(\n eq(providerRunners.id, params.runnerInstanceId),\n eq(providerRunners.provisionerId, params.provisionerId),\n isNull(providerRunners.providerRunnerId),\n notInArray(providerRunners.state, [...terminalStates]),\n ),\n )\n .returning({id: providerRunners.id});\n return rows.length === 1;\n}\n\nexport async function touchRunnerControlSession(runnerInstanceId: string, provisionerId: string) {\n await updateRunnerControlSessionLastSeen(db(), runnerInstanceId, provisionerId);\n await db()\n .update(providerRunners)\n .set({reportedAt: sql`now()`, updatedAt: sql`now()`})\n .where(\n and(\n eq(providerRunners.id, runnerInstanceId),\n eq(providerRunners.provisionerId, provisionerId),\n ),\n );\n}\n\nasync function updateRunnerControlSessionLastSeen(\n tx: Tx | NodePgDatabase<typeof schema>,\n runnerInstanceId: string,\n provisionerId: string,\n) {\n await tx\n .update(runnerControlSessions)\n .set({lastSeenAt: sql`now()`})\n .where(\n and(\n eq(runnerControlSessions.runnerInstanceId, runnerInstanceId),\n eq(runnerControlSessions.provisionerId, provisionerId),\n isNull(runnerControlSessions.closedAt),\n ),\n );\n}\n"],"names":["logger","extractDisplayPrefix","generateOpaqueToken","hashOpaqueToken","and","eq","gt","isNull","notInArray","or","sql","config","getRunnerAssignmentRejectionReason","ReservationExpiredError","ReservationNotFoundError","RunnerInstanceAlreadyAssignedError","RunnerInstanceNotAssignableError","sanitizeRunnerLabels","db","assignRunnerInstancesTx","validateRunnerReservationCapacityTx","terminalStates","provisionerTokens","runnerBootstrapTokens","runnerControlSessions","providerRunners","recordProviderRunnerAssignmentRejected","recordProviderRunnerControlSessionToAssignment","recordProviderRunnerCreatedToControlSession","recordRunnerReservationCapacityFailure","recordRunnerReservationPromotionFailure","issueRunnerActivationTokenTx","RunnerBootstrapTokenInvalidError","Error","name","RunnerControlSessionInvalidError","createRunnerInstancesWithBootstrapTokens","params","expiresAt","Date","now","ttlSeconds","transaction","tx","reservationValidation","provisionerId","requests","runnerInstances","reduce","runner","reservationId","request","find","candidate","count","push","reason","unavailableByReservation","values","remainingAcceptedByReservation","Map","acceptedByReservation","flatMap","requestIndex","remaining","get","set","length","reservationUnavailable","size","instances","insert","map","providerKind","templateKey","intendedReservationId","launchKind","state","labels","reportedAt","returning","id","results","instance","index","runnerInstanceId","bootstrapToken","result","hashedToken","prefix","exchangeRunnerBootstrapToken","controlSessionToken","bootstrap","update","consumedAt","where","rawToken","revokedAt","select","createdAt","provider","from","limit","session","durationMilliseconds","getTime","resolveRunnerControlSession","leftJoin","closedAt","enrollRunnerControlSession","current","provisionerScope","scope","innerJoin","execute","metadataUpdated","source","protocolVersion","capabilities","assignedAt","debug","updated","updatedAt","updateRunnerControlSessionLastSeen","activationToken","controlSessionToAssignment","assignmentRejectedReason","promotionFailureReason","promotion","promotionTx","assignment","runnerInstanceIds","surface","RUNNER_ACTIVATION_TOKEN_TTL_SECONDS","error","assignmentRejectionReason","expectedFailureReason","getRunnerReservationPromotionFailureReason","details","err","observation","attachRunnerControlProviderId","rows","providerRunnerId","touchRunnerControlSession","lastSeenAt"],"mappings":"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"}
@@ -33,6 +33,7 @@ export interface ReconciledBoundJobExecution {
33
33
  export interface ReconciledRunnerInstance {
34
34
  providerRunnerId: string;
35
35
  state: RunnerInstanceState | null;
36
+ intendedReservationId: string | null;
36
37
  reservationId: string | null;
37
38
  runnerSessionId: string | null;
38
39
  boundJobExecution: ReconciledBoundJobExecution | null;
@@ -1 +1 @@
1
- {"version":3,"file":"runner-instances.d.ts","sourceRoot":"","sources":["../../src/core/runner-instances.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runner-instances.d.ts","sourceRoot":"","sources":["../../src/core/runner-instances.ts"],"names":[],"mappings":"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"}
@@ -57,6 +57,7 @@ export function reconcileRunnerInstancesFromDbResult(params) {
57
57
  return {
58
58
  providerRunnerId,
59
59
  state: row?.state ?? null,
60
+ intendedReservationId: row?.intendedReservationId ?? null,
60
61
  reservationId: row?.reservationId ?? null,
61
62
  runnerSessionId: row?.runnerSessionId ?? null,
62
63
  boundJobExecution: boundJobExecution ? toReconciledBoundJobExecution(boundJobExecution) : null,