| 1 | import crypto from 'crypto' |
| 2 | import { JobCoordinator, sessionLockKey } from '../agent-runtime' |
| 3 | |
| 4 | export const withImageFulfillmentRetryLock = async <T>( |
| 5 | coordinator: JobCoordinator, |
| 6 | sessionId: string, |
| 7 | sourceJobId: string, |
| 8 | operation: () => Promise<T> |
| 9 | ): Promise<T> => { |
| 10 | const reservation = await coordinator.reserve({ |
| 11 | jobId: `image-retry:${sourceJobId}:${crypto.randomUUID()}`, |
| 12 | domain: 'image', |
| 13 | owner: { kind: 'image-fulfillment', id: `retry:${sourceJobId}` }, |
| 14 | claims: { write: [sessionLockKey(sessionId)] }, |
| 15 | wait: 'fail' |
| 16 | }) |
| 17 | if (reservation.status === 'busy') { |
| 18 | throw new Error('The session is busy with another generation or edit. Please try again later.') |
| 19 | } |
| 20 | |
| 21 | try { |
| 22 | return await operation() |
| 23 | } finally { |
| 24 | reservation.lease.release() |
| 25 | } |
| 26 | } |
| 27 |