import { CLICKUP_TASK_SCHEMA } from "@automate.ax/integration-contracts/clickup" import * as z from "zod" import { defineAction } from "../../../automation/actions" import { CLICKUP_ACCOUNT, CLICKUP_ACCOUNT_REQUIREMENT, CLICKUP_ID_SCHEMA, CLICKUP_TIMESTAMP_SCHEMA, CLICKUP_VOID_RESPONSE_SCHEMA, compactClickUpInput, getClickUpApi, } from "./lib" const TASK_PAGE_SCHEMA = z.object({ lastPage: z.boolean().optional(), tasks: CLICKUP_TASK_SCHEMA.array(), }) const CUSTOM_FIELD_INPUT_SCHEMA = z.object({ id: CLICKUP_ID_SCHEMA, value: z.json(), }) const TASK_WRITE_FIELDS = { archived: z.boolean().optional(), assignees: z.number().int().array().optional(), customFields: CUSTOM_FIELD_INPUT_SCHEMA.array().optional(), description: z.string().optional(), dueDate: CLICKUP_TIMESTAMP_SCHEMA.optional(), dueDateTime: z.boolean().optional(), groupAssignees: CLICKUP_ID_SCHEMA.array().optional(), markdownContent: z.string().optional(), name: z.string().trim().min(1), notifyAll: z.boolean().optional(), points: z.number().optional(), priority: z.number().int().min(1).max(4).nullable().optional(), startDate: CLICKUP_TIMESTAMP_SCHEMA.optional(), startDateTime: z.boolean().optional(), status: z.string().optional(), tags: z.string().array().optional(), timeEstimate: z.number().int().nonnegative().optional(), } as const const TASK_CREATE_SCHEMA = z.object({ ...TASK_WRITE_FIELDS, checkRequiredCustomFields: z.boolean().optional(), customItemId: z.number().int().nonnegative().optional(), listId: CLICKUP_ID_SCHEMA, linksTo: CLICKUP_ID_SCHEMA.nullable().optional(), parent: CLICKUP_ID_SCHEMA.nullable().optional(), }) const TASK_UPDATE_SCHEMA = z .object({ archived: z.boolean().optional(), assignees: z .object({ add: z.number().int().array().prefault([]), rem: z.number().int().array().prefault([]), }) .optional(), customItemId: z.number().int().nonnegative().nullable().optional(), description: z.string().optional(), dueDate: CLICKUP_TIMESTAMP_SCHEMA.optional(), dueDateTime: z.boolean().optional(), groupAssignees: z .object({ add: CLICKUP_ID_SCHEMA.array(), rem: CLICKUP_ID_SCHEMA.array(), }) .partial() .optional(), markdownContent: z.string().optional(), name: z.string().trim().min(1).optional(), parent: CLICKUP_ID_SCHEMA.optional(), points: z.number().optional(), priority: z.number().int().min(1).max(4).nullable().optional(), startDate: CLICKUP_TIMESTAMP_SCHEMA.optional(), startDateTime: z.boolean().optional(), status: z.string().optional(), taskId: CLICKUP_ID_SCHEMA, timeEstimate: z.number().int().nonnegative().optional(), watchers: z .object({ add: z.number().int().array().prefault([]), rem: z.number().int().array().prefault([]), }) .optional(), }) .refine(({ taskId: _taskId, ...body }) => Object.keys(body).length > 0, { message: "Provide at least one task field to update.", }) const TASK_LIST_FILTERS = { assignees: z.string().array().optional(), dueDateGt: CLICKUP_TIMESTAMP_SCHEMA.optional(), dueDateLt: CLICKUP_TIMESTAMP_SCHEMA.optional(), includeClosed: z.boolean().prefault(false), includeMarkdownDescription: z.boolean().prefault(false), page: z.number().int().nonnegative().prefault(0), parent: CLICKUP_ID_SCHEMA.optional(), reverse: z.boolean().optional(), statuses: z.string().array().optional(), subtasks: z.boolean().prefault(false), tags: z.string().array().optional(), } as const /** Lists tasks in a ClickUp List. */ export const listClickUpTasks = defineAction("List ClickUp tasks") .describe("Lists tasks in a ClickUp List with zero-based pagination.") .account(CLICKUP_ACCOUNT, CLICKUP_ACCOUNT_REQUIREMENT) .input( z.object({ ...TASK_LIST_FILTERS, archived: z.boolean().prefault(false), listId: CLICKUP_ID_SCHEMA, orderBy: z .enum(["created", "dueDate", "id", "updated"]) .prefault("created"), }), ) .output(TASK_PAGE_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { listId, ...query } = input return getClickUpApi(account).request(`list/${listId}/task`, { query: { ...query, orderBy: query.orderBy === "dueDate" ? "due_date" : query.orderBy, }, responseSchema: TASK_PAGE_SCHEMA, }) }) /** Lists filtered tasks across a ClickUp Workspace. */ export const listClickUpWorkspaceTasks = defineAction( "List ClickUp Workspace tasks", ) .describe("Lists up to 100 filtered tasks per page across a Workspace.") .account(CLICKUP_ACCOUNT, CLICKUP_ACCOUNT_REQUIREMENT) .input( z.object({ ...TASK_LIST_FILTERS, customItems: z.number().int().nonnegative().array().optional(), listIds: CLICKUP_ID_SCHEMA.array().optional(), orderBy: z .enum(["created", "dueDate", "id", "updated"]) .prefault("created"), spaceIds: CLICKUP_ID_SCHEMA.array().optional(), workspaceId: CLICKUP_ID_SCHEMA, }), ) .output(TASK_PAGE_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { workspaceId, ...query } = input return getClickUpApi(account).request(`team/${workspaceId}/task`, { query: { ...query, orderBy: query.orderBy === "dueDate" ? "due_date" : query.orderBy, }, responseSchema: TASK_PAGE_SCHEMA, }) }) /** Gets one ClickUp task. */ export const getClickUpTask = defineAction("Get ClickUp task") .describe("Gets one ClickUp task, optionally including its subtasks.") .account(CLICKUP_ACCOUNT, CLICKUP_ACCOUNT_REQUIREMENT) .input( z.object({ includeMarkdownDescription: z.boolean().prefault(false), includeSubtasks: z.boolean().prefault(false), taskId: CLICKUP_ID_SCHEMA, }), ) .output(CLICKUP_TASK_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => getClickUpApi(account).request(`task/${input.taskId}`, { query: { includeMarkdownDescription: input.includeMarkdownDescription, includeSubtasks: input.includeSubtasks, }, responseSchema: CLICKUP_TASK_SCHEMA, }), ) /** Creates a ClickUp task. */ export const createClickUpTask = defineAction("Create ClickUp task") .describe("Creates a task in a ClickUp List.") .account(CLICKUP_ACCOUNT, CLICKUP_ACCOUNT_REQUIREMENT) .input(TASK_CREATE_SCHEMA) .output(CLICKUP_TASK_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => createTask(account, input)) /** Creates a ClickUp subtask. */ export const createClickUpSubtask = defineAction("Create ClickUp subtask") .describe("Creates a subtask in the same ClickUp List as its parent.") .account(CLICKUP_ACCOUNT, CLICKUP_ACCOUNT_REQUIREMENT) .input( TASK_CREATE_SCHEMA.omit({ parent: true }).extend({ parentTaskId: CLICKUP_ID_SCHEMA, }), ) .output(CLICKUP_TASK_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { parentTaskId, ...task } = input return createTask(account, { ...task, parent: parentTaskId }) }) /** Updates a ClickUp task. */ export const updateClickUpTask = defineAction("Update ClickUp task") .describe("Updates task fields, assignees, dates, status, or priority.") .account(CLICKUP_ACCOUNT, CLICKUP_ACCOUNT_REQUIREMENT) .input(TASK_UPDATE_SCHEMA) .output(CLICKUP_TASK_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { taskId, ...body } = input return getClickUpApi(account).request(`task/${taskId}`, { body: compactClickUpInput(body), method: "PUT", responseSchema: CLICKUP_TASK_SCHEMA, }) }) /** Deletes a ClickUp task. */ export const deleteClickUpTask = defineAction("Delete ClickUp task") .describe("Deletes a ClickUp task.") .account(CLICKUP_ACCOUNT, CLICKUP_ACCOUNT_REQUIREMENT) .input(z.object({ taskId: CLICKUP_ID_SCHEMA })) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getClickUpApi(account).request(`task/${input.taskId}`, { method: "DELETE", responseSchema: CLICKUP_VOID_RESPONSE_SCHEMA, }), ) /** * Creates a task or subtask with the shared ClickUp request shape. * * @param account - Resolved ClickUp account. * @param input - Validated task creation fields. */ async function createTask( account: Parameters[0], input: z.output, ) { const { listId, ...body } = input return getClickUpApi(account).request(`list/${listId}/task`, { body: compactClickUpInput(body), method: "POST", responseSchema: CLICKUP_TASK_SCHEMA, }) }