// Cancels running workflow tasks. The ownership check lives here rather than in SKILL.md so a // task id from another Workstation is refused even if the model is told to use it. import { resolveApiUrl } from './tela-api.ts' type Task = { id: string name: string reference: number | null status: string promptApplicationId: string | null workflowRunId: string | null } const apiUrl = resolveApiUrl(process.argv[2]) const dataToken = process.env.DATA_TOKEN if (!dataToken) { console.error('DATA_TOKEN is required') process.exit(1) } const [promptApplicationId, rawTaskIds] = process.argv.slice(3) const taskIds = rawTaskIds ? JSON.parse(rawTaskIds) as string[] : [] if (!promptApplicationId || !Array.isArray(taskIds) || taskIds.length === 0) { console.error('Usage: bun run ./.claude/skills/workstation/cancel.ts \'["", ...]\'') process.exit(1) } const headers = { 'x-data-token': dataToken, 'Content-Type': 'application/json', } for (const taskId of taskIds) { const taskResponse = await fetch(`${apiUrl}/task/${encodeURIComponent(taskId)}`, { headers }) if (!taskResponse.ok) { process.stdout.write(`${taskId}: not cancelled, task lookup failed with status ${taskResponse.status}\n`) continue } const task = await taskResponse.json() as Task const label = `#${task.reference ?? '?'} ${task.name}` if (task.promptApplicationId !== promptApplicationId) { process.stdout.write(`${label}: not cancelled, it belongs to another Workstation\n`) continue } if (task.status !== 'running') { process.stdout.write(`${label}: not cancelled, status is ${task.status}\n`) continue } if (!task.workflowRunId) { process.stdout.write(`${label}: not cancelled, only workflow tasks can be cancelled\n`) continue } const cancelResponse = await fetch(`${apiUrl}/workflow/runs/${task.workflowRunId}/cancel`, { method: 'POST', headers }) process.stdout.write(cancelResponse.ok ? `${label}: cancelled\n` : `${label}: not cancelled, status ${cancelResponse.status}: ${await cancelResponse.text()}\n`) }