chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { isApiClientError } from '@/lib/api/client/errors'
|
||||
import { requestJson } from '@/lib/api/client/request'
|
||||
import {
|
||||
getPauseContextDetailContract,
|
||||
resumeWorkflowExecutionContract,
|
||||
} from '@/lib/api/contracts/workflows'
|
||||
import type { ResumeStatus } from '@/executor/types'
|
||||
|
||||
export const RESUME_EXECUTION_DETAIL_STALE_TIME = 30 * 1000
|
||||
|
||||
export const resumeKeys = {
|
||||
all: ['resume-execution'] as const,
|
||||
executions: () => [...resumeKeys.all, 'execution'] as const,
|
||||
execution: (workflowId?: string, executionId?: string) =>
|
||||
[...resumeKeys.executions(), workflowId ?? '', executionId ?? ''] as const,
|
||||
contexts: () => [...resumeKeys.all, 'context'] as const,
|
||||
context: (workflowId?: string, executionId?: string, contextId?: string) =>
|
||||
[...resumeKeys.contexts(), workflowId ?? '', executionId ?? '', contextId ?? ''] as const,
|
||||
}
|
||||
|
||||
export interface ResumeLinks {
|
||||
apiUrl: string
|
||||
uiUrl: string
|
||||
contextId: string
|
||||
executionId: string
|
||||
workflowId: string
|
||||
}
|
||||
|
||||
export interface ResumeQueueEntrySummary {
|
||||
id: string
|
||||
contextId: string
|
||||
status: string
|
||||
queuedAt: string | null
|
||||
claimedAt: string | null
|
||||
completedAt: string | null
|
||||
failureReason: string | null
|
||||
newExecutionId: string
|
||||
resumeInput: any
|
||||
}
|
||||
|
||||
export interface PausePointWithQueue {
|
||||
contextId: string
|
||||
triggerBlockId?: string
|
||||
blockId?: string
|
||||
response: any
|
||||
registeredAt: string
|
||||
resumeStatus: ResumeStatus
|
||||
snapshotReady: boolean
|
||||
resumeLinks?: ResumeLinks
|
||||
queuePosition?: number | null
|
||||
latestResumeEntry?: ResumeQueueEntrySummary | null
|
||||
parallelScope?: any
|
||||
loopScope?: any
|
||||
pauseKind?: 'human' | 'time'
|
||||
resumeAt?: string
|
||||
}
|
||||
|
||||
export interface PausedExecutionSummary {
|
||||
id: string
|
||||
workflowId: string
|
||||
executionId: string
|
||||
status: string
|
||||
totalPauseCount: number
|
||||
resumedCount: number
|
||||
pausedAt: string | null
|
||||
updatedAt: string | null
|
||||
expiresAt: string | null
|
||||
metadata: Record<string, any> | null
|
||||
triggerIds: string[]
|
||||
pausePoints: PausePointWithQueue[]
|
||||
}
|
||||
|
||||
export interface PausedExecutionDetail extends PausedExecutionSummary {
|
||||
executionSnapshot: any
|
||||
queue: ResumeQueueEntrySummary[]
|
||||
}
|
||||
|
||||
export interface PauseContextDetail {
|
||||
execution: PausedExecutionSummary
|
||||
pausePoint: PausePointWithQueue
|
||||
queue: ResumeQueueEntrySummary[]
|
||||
activeResumeEntry?: ResumeQueueEntrySummary | null
|
||||
}
|
||||
|
||||
export interface ResumeContextResult {
|
||||
ok: boolean
|
||||
payload: {
|
||||
status?: string
|
||||
queuePosition?: number | null
|
||||
error?: string
|
||||
message?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
|
||||
interface ResumeContextVariables {
|
||||
workflowId: string
|
||||
executionId: string
|
||||
contextId: string
|
||||
input?: unknown
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the paused execution detail (all pause points for an execution). The
|
||||
* contract models pause points loosely (`z.record`); the resume UI works against
|
||||
* the richer `PausedExecutionDetail` interface, hence the bridging cast.
|
||||
*/
|
||||
export function useResumeExecutionDetail(
|
||||
workflowId: string,
|
||||
executionId: string,
|
||||
initialData?: PausedExecutionDetail
|
||||
) {
|
||||
return useQuery({
|
||||
queryKey: resumeKeys.execution(workflowId, executionId),
|
||||
queryFn: async ({ signal }): Promise<PausedExecutionDetail> => {
|
||||
const raw = await requestJson(resumeWorkflowExecutionContract, {
|
||||
params: { workflowId, executionId },
|
||||
signal,
|
||||
})
|
||||
// double-cast-allowed: contract models pause points as z.record; the resume UI uses the richer PausedExecutionDetail interface
|
||||
return raw as unknown as PausedExecutionDetail
|
||||
},
|
||||
enabled: Boolean(workflowId && executionId),
|
||||
staleTime: RESUME_EXECUTION_DETAIL_STALE_TIME,
|
||||
initialData,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the detail for a single pause context. Returns `null` when the context
|
||||
* no longer exists (404). `staleTime: 0` because pause state is live.
|
||||
*/
|
||||
export function usePauseContextDetail(workflowId: string, executionId: string, contextId?: string) {
|
||||
return useQuery({
|
||||
queryKey: resumeKeys.context(workflowId, executionId, contextId),
|
||||
queryFn: async ({ signal }): Promise<PauseContextDetail | null> => {
|
||||
try {
|
||||
const raw = await requestJson(getPauseContextDetailContract, {
|
||||
params: { workflowId, executionId, contextId: contextId as string },
|
||||
signal,
|
||||
})
|
||||
// double-cast-allowed: contract models the pause point as z.record; the resume UI uses the richer PauseContextDetail interface
|
||||
return raw as unknown as PauseContextDetail
|
||||
} catch (error) {
|
||||
if (isApiClientError(error) && error.status === 404) return null
|
||||
throw error
|
||||
}
|
||||
},
|
||||
enabled: Boolean(workflowId && executionId && contextId),
|
||||
staleTime: 0,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits a resume for a pause context and invalidates the execution + context
|
||||
* queries so the cache reconciles with the server. The POST stays a raw fetch:
|
||||
* the route reads the body with a tolerant JSON parse and the contract has no
|
||||
* body schema, so it cannot go through `requestJson`.
|
||||
*/
|
||||
export function useResumeContext() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
workflowId,
|
||||
executionId,
|
||||
contextId,
|
||||
input,
|
||||
}: ResumeContextVariables): Promise<ResumeContextResult> => {
|
||||
// boundary-raw-fetch: resume-context POST contract has no body schema (route uses tolerant raw JSON parse for resume input forwarded to PauseResumeManager)
|
||||
const response = await fetch(`/api/resume/${workflowId}/${executionId}/${contextId}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(input ? { input } : {}),
|
||||
})
|
||||
const payload = await response.json().catch(() => ({}))
|
||||
return { ok: response.ok, payload }
|
||||
},
|
||||
onSettled: (_data, _error, variables) =>
|
||||
Promise.all([
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: resumeKeys.execution(variables.workflowId, variables.executionId),
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: resumeKeys.context(
|
||||
variables.workflowId,
|
||||
variables.executionId,
|
||||
variables.contextId
|
||||
),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user