cancelling actions works now

This commit is contained in:
2026-03-10 19:53:20 -05:00
parent 5b45b17fa6
commit 71ea3f34ca
30 changed files with 482 additions and 1071 deletions

View File

@@ -32,7 +32,7 @@ export type ApiResponse_ExecutionResponse = {
*/
enforcement?: number | null;
/**
* Executor ID (worker/executor that ran this)
* Identity ID that initiated this execution
*/
executor?: number | null;
/**
@@ -43,6 +43,10 @@ export type ApiResponse_ExecutionResponse = {
* Parent execution ID (for nested/child executions)
*/
parent?: number | null;
/**
* Worker ID currently assigned to this execution
*/
worker?: number | null;
/**
* Execution result/output
*/

View File

@@ -28,7 +28,7 @@ export type ExecutionResponse = {
*/
enforcement?: number | null;
/**
* Executor ID (worker/executor that ran this)
* Identity ID that initiated this execution
*/
executor?: number | null;
/**
@@ -39,6 +39,10 @@ export type ExecutionResponse = {
* Parent execution ID (for nested/child executions)
*/
parent?: number | null;
/**
* Worker ID currently assigned to this execution
*/
worker?: number | null;
/**
* Execution result/output
*/

View File

@@ -224,7 +224,7 @@ export class ExecutionsService {
*/
enforcement?: number | null;
/**
* Executor ID (worker/executor that ran this)
* Identity ID that initiated this execution
*/
executor?: number | null;
/**
@@ -235,6 +235,10 @@ export class ExecutionsService {
* Parent execution ID (for nested/child executions)
*/
parent?: number | null;
/**
* Worker ID currently assigned to this execution
*/
worker?: number | null;
/**
* Execution result/output
*/

View File

@@ -337,11 +337,16 @@ function TextFileDetail({
interface ProgressDetailProps {
artifactId: number;
isRunning?: boolean;
onClose: () => void;
}
function ProgressDetail({ artifactId, onClose }: ProgressDetailProps) {
const { data: artifactData, isLoading } = useArtifact(artifactId);
function ProgressDetail({
artifactId,
isRunning = false,
onClose,
}: ProgressDetailProps) {
const { data: artifactData, isLoading } = useArtifact(artifactId, isRunning);
const artifact = artifactData?.data;
const progressEntries = useMemo(() => {
@@ -707,6 +712,7 @@ export default function ExecutionArtifactsPanel({
<div className="px-3">
<ProgressDetail
artifactId={artifact.id}
isRunning={isRunning}
onClose={() => setExpandedProgressId(null)}
/>
</div>

View File

@@ -69,9 +69,10 @@ const ExecutionPreviewPanel = memo(function ExecutionPreviewPanel({
execution?.status === "running" ||
execution?.status === "scheduling" ||
execution?.status === "scheduled" ||
execution?.status === "requested";
execution?.status === "requested" ||
execution?.status === "canceling";
const isCancellable = isRunning || execution?.status === "canceling";
const isCancellable = isRunning;
const startedAt = execution?.started_at
? new Date(execution.started_at)
@@ -241,13 +242,23 @@ const ExecutionPreviewPanel = memo(function ExecutionPreviewPanel({
{execution.executor && (
<div>
<dt className="text-xs font-medium text-gray-500 uppercase tracking-wide">
Executor
Initiated By
</dt>
<dd className="mt-0.5 text-sm text-gray-900 font-mono">
#{execution.executor}
</dd>
</div>
)}
{execution.worker && (
<div>
<dt className="text-xs font-medium text-gray-500 uppercase tracking-wide">
Worker
</dt>
<dd className="mt-0.5 text-sm text-gray-900 font-mono">
#{execution.worker}
</dd>
</div>
)}
{execution.workflow_task && (
<div>
<dt className="text-xs font-medium text-gray-500 uppercase tracking-wide">

View File

@@ -147,15 +147,18 @@ export function useExecutionArtifacts(
return response;
},
enabled: !!executionId,
staleTime: isRunning ? 3000 : 10000,
refetchInterval: isRunning ? 3000 : 10000,
staleTime: isRunning ? 3000 : 30000,
refetchInterval: isRunning ? 3000 : false,
});
}
/**
* Fetch a single artifact by ID (includes data field for progress artifacts).
*
* @param isRunning - When true, polls every 3s for live updates. When false,
* uses a longer stale time and disables automatic polling.
*/
export function useArtifact(id: number | undefined) {
export function useArtifact(id: number | undefined, isRunning = false) {
return useQuery({
queryKey: ["artifacts", id],
queryFn: async () => {
@@ -169,8 +172,8 @@ export function useArtifact(id: number | undefined) {
return response;
},
enabled: !!id,
staleTime: 3000,
refetchInterval: 3000,
staleTime: isRunning ? 3000 : 30000,
refetchInterval: isRunning ? 3000 : false,
});
}

View File

@@ -205,6 +205,11 @@ export function useExecutionStream(options: UseExecutionStreamOptions = {}) {
},
);
queryClient.invalidateQueries({
queryKey: ["history", "execution", executionNotification.entity_id],
exact: false,
});
// Update execution list queries by modifying existing data.
// We need to iterate manually to access query keys for filtering.
const queries = queryClient

View File

@@ -22,6 +22,16 @@ interface ExecutionsQueryParams {
topLevelOnly?: boolean;
}
function isExecutionActive(status: string | undefined): boolean {
return (
status === "requested" ||
status === "scheduling" ||
status === "scheduled" ||
status === "running" ||
status === "canceling"
);
}
export function useExecutions(params?: ExecutionsQueryParams) {
// Check if any filters are applied
const hasFilters =
@@ -67,7 +77,9 @@ export function useExecution(id: number) {
return response;
},
enabled: !!id,
staleTime: 30000, // 30 seconds - SSE handles real-time updates
staleTime: 30000,
refetchInterval: (query) =>
isExecutionActive(query.state.data?.data?.status) ? 3000 : false,
});
}
@@ -180,11 +192,7 @@ export function useChildExecutions(parentId: number | undefined) {
const data = query.state.data;
if (!data) return false;
const hasActive = data.data.some(
(e) =>
e.status === "requested" ||
e.status === "scheduling" ||
e.status === "scheduled" ||
e.status === "running",
(e) => isExecutionActive(e.status),
);
return hasActive ? 5000 : false;
},

View File

@@ -199,10 +199,10 @@ export default function ExecutionDetailPage() {
execution.status === ExecutionStatus.RUNNING ||
execution.status === ExecutionStatus.SCHEDULING ||
execution.status === ExecutionStatus.SCHEDULED ||
execution.status === ExecutionStatus.REQUESTED;
execution.status === ExecutionStatus.REQUESTED ||
execution.status === ExecutionStatus.CANCELING;
const isCancellable =
isRunning || execution.status === ExecutionStatus.CANCELING;
const isCancellable = isRunning;
return (
<div className="p-6 max-w-7xl mx-auto">
@@ -392,13 +392,23 @@ export default function ExecutionDetailPage() {
{execution.executor && (
<div>
<dt className="text-sm font-medium text-gray-500">
Executor ID
Initiated By
</dt>
<dd className="mt-1 text-sm text-gray-900">
{execution.executor}
</dd>
</div>
)}
{execution.worker && (
<div>
<dt className="text-sm font-medium text-gray-500">
Worker ID
</dt>
<dd className="mt-1 text-sm text-gray-900">
{execution.worker}
</dd>
</div>
)}
</dl>
{/* Inline progress bar (visible when execution has progress artifacts) */}