diff --git a/apps/sim/blocks/blocks/trello.ts b/apps/sim/blocks/blocks/trello.ts index 371ab9dcaa9..a9226ef3380 100644 --- a/apps/sim/blocks/blocks/trello.ts +++ b/apps/sim/blocks/blocks/trello.ts @@ -55,10 +55,10 @@ function parseStringArray(value: unknown): string[] | undefined { export const TrelloBlock: BlockConfig = { type: 'trello', name: 'Trello', - description: 'Manage Trello lists, cards, and activity', + description: 'Manage Trello lists, cards, checklists, and activity', authMode: AuthMode.OAuth, longDescription: - 'Integrate with Trello to list board lists, list cards, create cards, update cards, review activity, and add comments.', + 'Integrate with Trello to list, search, create, update, and delete cards and lists, manage checklists and checklist items, assign labels and members, review activity, and add comments.', docsLink: 'https://docs.sim.ai/integrations/trello', category: 'tools', integrationType: IntegrationType.Productivity, @@ -72,17 +72,25 @@ export const TrelloBlock: BlockConfig = { options: [ { label: 'Get Lists', id: 'trello_list_lists' }, { label: 'List Cards', id: 'trello_list_cards' }, + { label: 'Search', id: 'trello_search' }, { label: 'Create Card', id: 'trello_create_card' }, { label: 'Get Card', id: 'trello_get_card' }, { label: 'Update Card', id: 'trello_update_card' }, + { label: 'Delete Card', id: 'trello_delete_card' }, { label: 'Get Actions', id: 'trello_get_actions' }, { label: 'Add Comment', id: 'trello_add_comment' }, { label: 'Add Checklist', id: 'trello_add_checklist' }, + { label: 'Add Checklist Item', id: 'trello_add_checklist_item' }, + { label: 'Update Checklist Item', id: 'trello_update_checklist_item' }, { label: 'Add Label', id: 'trello_add_label' }, + { label: 'Remove Label', id: 'trello_remove_label' }, { label: 'Add Member', id: 'trello_add_member' }, + { label: 'Remove Member', id: 'trello_remove_member' }, + { label: 'List Members', id: 'trello_list_members' }, { label: 'Create Board', id: 'trello_create_board' }, { label: 'Get Board', id: 'trello_get_board' }, { label: 'Create List', id: 'trello_create_list' }, + { label: 'Update List', id: 'trello_update_list' }, ], value: () => 'trello_list_lists', }, @@ -124,11 +132,17 @@ export const TrelloBlock: BlockConfig = { 'trello_get_actions', 'trello_get_board', 'trello_create_list', + 'trello_list_members', ], }, required: { field: 'operation', - value: ['trello_list_lists', 'trello_get_board', 'trello_create_list'], + value: [ + 'trello_list_lists', + 'trello_get_board', + 'trello_create_list', + 'trello_list_members', + ], }, }, { @@ -147,11 +161,17 @@ export const TrelloBlock: BlockConfig = { 'trello_get_actions', 'trello_get_board', 'trello_create_list', + 'trello_list_members', ], }, required: { field: 'operation', - value: ['trello_list_lists', 'trello_get_board', 'trello_create_list'], + value: [ + 'trello_list_lists', + 'trello_get_board', + 'trello_create_list', + 'trello_list_members', + ], }, }, { @@ -161,11 +181,43 @@ export const TrelloBlock: BlockConfig = { placeholder: 'Enter Trello list ID', condition: { field: 'operation', - value: ['trello_list_cards', 'trello_create_card'], + value: ['trello_list_cards', 'trello_create_card', 'trello_update_list'], }, required: { field: 'operation', - value: 'trello_create_card', + value: ['trello_create_card', 'trello_update_list'], + }, + }, + { + id: 'listFilter', + title: 'List Filter', + type: 'dropdown', + options: [ + { label: 'Open (default)', id: '' }, + { label: 'Closed', id: 'closed' }, + { label: 'All', id: 'all' }, + ], + value: () => '', + mode: 'advanced', + condition: { + field: 'operation', + value: 'trello_list_lists', + }, + }, + { + id: 'cardFilter', + title: 'Card Filter', + type: 'dropdown', + options: [ + { label: 'Open (default)', id: '' }, + { label: 'Closed', id: 'closed' }, + { label: 'All', id: 'all' }, + ], + value: () => '', + mode: 'advanced', + condition: { + field: 'operation', + value: 'trello_list_cards', }, }, { @@ -177,23 +229,31 @@ export const TrelloBlock: BlockConfig = { field: 'operation', value: [ 'trello_update_card', + 'trello_delete_card', 'trello_get_actions', 'trello_add_comment', 'trello_get_card', 'trello_add_checklist', + 'trello_update_checklist_item', 'trello_add_label', + 'trello_remove_label', 'trello_add_member', + 'trello_remove_member', ], }, required: { field: 'operation', value: [ 'trello_update_card', + 'trello_delete_card', 'trello_add_comment', 'trello_get_card', 'trello_add_checklist', + 'trello_update_checklist_item', 'trello_add_label', + 'trello_remove_label', 'trello_add_member', + 'trello_remove_member', ], }, }, @@ -290,6 +350,23 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, placeholder: 'Describe the label IDs to include...', }, }, + { + id: 'memberIds', + title: 'Member IDs', + type: 'short-input', + placeholder: 'Comma-separated member IDs', + mode: 'advanced', + condition: { + field: 'operation', + value: 'trello_create_card', + }, + wandConfig: { + enabled: true, + prompt: + 'Generate a comma-separated list of Trello member IDs. Return ONLY the comma-separated values - no explanations, no extra text.', + placeholder: 'Describe the member IDs to assign...', + }, + }, { id: 'closed', title: 'Archive Status', @@ -311,7 +388,6 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, title: 'Move to List ID', type: 'short-input', placeholder: 'Enter Trello list ID', - mode: 'advanced', condition: { field: 'operation', value: 'trello_update_card', @@ -350,6 +426,52 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, value: 'trello_get_actions', }, }, + { + id: 'since', + title: 'Since', + type: 'short-input', + placeholder: 'ISO 8601 timestamp or action ID', + mode: 'advanced', + condition: { + field: 'operation', + value: 'trello_get_actions', + }, + wandConfig: { + enabled: true, + prompt: `Generate a date or timestamp based on the user's description. +The timestamp should be in ISO 8601 format: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ. +Examples: +- "yesterday" -> Calculate yesterday's date in YYYY-MM-DD format +- "1 week ago" -> Calculate the date 1 week ago in YYYY-MM-DD format + +Return ONLY the date/timestamp string - no explanations, no extra text.`, + placeholder: 'Describe the start of the range (e.g. "1 week ago")...', + generationType: 'timestamp', + }, + }, + { + id: 'before', + title: 'Before', + type: 'short-input', + placeholder: 'ISO 8601 timestamp or action ID', + mode: 'advanced', + condition: { + field: 'operation', + value: 'trello_get_actions', + }, + wandConfig: { + enabled: true, + prompt: `Generate a date or timestamp based on the user's description. +The timestamp should be in ISO 8601 format: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ. +Examples: +- "today" -> Calculate today's date in YYYY-MM-DD format +- "end of last month" -> Calculate the last day of the previous month + +Return ONLY the date/timestamp string - no explanations, no extra text.`, + placeholder: 'Describe the end of the range (e.g. "today")...', + generationType: 'timestamp', + }, + }, { id: 'text', title: 'Comment', @@ -415,10 +537,13 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, type: 'short-input', placeholder: 'Enter list name', condition: { + field: 'operation', + value: ['trello_create_list', 'trello_update_list'], + }, + required: { field: 'operation', value: 'trello_create_list', }, - required: true, }, { id: 'listPos', @@ -428,7 +553,34 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, mode: 'advanced', condition: { field: 'operation', - value: 'trello_create_list', + value: ['trello_create_list', 'trello_update_list'], + }, + }, + { + id: 'listClosed', + title: 'Archive Status', + type: 'dropdown', + options: [ + { label: 'Leave Unchanged', id: '' }, + { label: 'Archive List', id: 'true' }, + { label: 'Reopen List', id: 'false' }, + ], + value: () => '', + mode: 'advanced', + condition: { + field: 'operation', + value: 'trello_update_list', + }, + }, + { + id: 'moveListToBoardId', + title: 'Move to Board ID', + type: 'short-input', + placeholder: 'Enter Trello board ID', + mode: 'advanced', + condition: { + field: 'operation', + value: 'trello_update_list', }, }, { @@ -453,6 +605,91 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, value: 'trello_add_checklist', }, }, + { + id: 'checklistId', + title: 'Checklist ID', + type: 'short-input', + placeholder: 'Enter Trello checklist ID', + condition: { + field: 'operation', + value: 'trello_add_checklist_item', + }, + required: true, + }, + { + id: 'itemName', + title: 'Item Name', + type: 'short-input', + placeholder: 'Enter checklist item name', + condition: { + field: 'operation', + value: 'trello_add_checklist_item', + }, + required: true, + }, + { + id: 'itemPos', + title: 'Item Position', + type: 'short-input', + placeholder: 'top, bottom, or a positive float', + mode: 'advanced', + condition: { + field: 'operation', + value: 'trello_add_checklist_item', + }, + }, + { + id: 'itemChecked', + title: 'Start Checked', + type: 'dropdown', + options: [ + { label: 'Unchecked', id: '' }, + { label: 'Checked', id: 'true' }, + ], + value: () => '', + mode: 'advanced', + condition: { + field: 'operation', + value: 'trello_add_checklist_item', + }, + }, + { + id: 'checkItemId', + title: 'Checklist Item ID', + type: 'short-input', + placeholder: 'Enter checklist item ID', + condition: { + field: 'operation', + value: 'trello_update_checklist_item', + }, + required: true, + }, + { + id: 'checkItemState', + title: 'State', + type: 'dropdown', + options: [ + { label: 'Leave Unchanged', id: '' }, + { label: 'Complete', id: 'complete' }, + { label: 'Incomplete', id: 'incomplete' }, + ], + value: () => '', + condition: { + field: 'operation', + value: 'trello_update_checklist_item', + }, + }, + { + id: 'checkItemName', + title: 'New Item Name', + type: 'short-input', + placeholder: 'Enter new checklist item name', + mode: 'advanced', + condition: { + field: 'operation', + value: 'trello_update_checklist_item', + }, + }, { id: 'labelId', title: 'Label ID', @@ -460,7 +697,7 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, placeholder: 'Enter Trello label ID', condition: { field: 'operation', - value: 'trello_add_label', + value: ['trello_add_label', 'trello_remove_label'], }, required: true, }, @@ -471,26 +708,82 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, placeholder: 'Enter Trello member ID', condition: { field: 'operation', - value: 'trello_add_member', + value: ['trello_add_member', 'trello_remove_member'], }, required: true, }, + { + id: 'searchQuery', + title: 'Search Query', + type: 'long-input', + placeholder: 'Enter search text (supports Trello operators like board:, list:, due:)', + condition: { + field: 'operation', + value: 'trello_search', + }, + required: true, + }, + { + id: 'searchModelTypes', + title: 'Search Scope', + type: 'dropdown', + options: [ + { label: 'All', id: 'all' }, + { label: 'Cards Only', id: 'cards' }, + { label: 'Boards Only', id: 'boards' }, + ], + value: () => 'all', + condition: { + field: 'operation', + value: 'trello_search', + }, + }, + { + id: 'searchBoardIds', + title: 'Restrict to Board IDs', + type: 'short-input', + placeholder: 'Comma-separated board IDs', + mode: 'advanced', + condition: { + field: 'operation', + value: 'trello_search', + }, + }, + { + id: 'searchCardsLimit', + title: 'Card Result Limit', + type: 'short-input', + placeholder: 'Maximum number of cards to return (1-1000, default 10)', + mode: 'advanced', + condition: { + field: 'operation', + value: 'trello_search', + }, + }, ], tools: { access: [ 'trello_list_lists', 'trello_list_cards', + 'trello_search', 'trello_create_card', 'trello_update_card', + 'trello_delete_card', 'trello_get_actions', 'trello_add_comment', 'trello_create_board', 'trello_get_board', 'trello_create_list', + 'trello_update_list', 'trello_get_card', 'trello_add_checklist', + 'trello_add_checklist_item', + 'trello_update_checklist_item', 'trello_add_label', + 'trello_remove_label', 'trello_add_member', + 'trello_remove_member', + 'trello_list_members', ], config: { tool: (params) => getTrimmedString(params.operation) ?? 'trello_list_lists', @@ -511,6 +804,7 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, return { ...baseParams, boardId, + filter: getTrimmedString(params.listFilter), } } @@ -530,6 +824,23 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, ...baseParams, boardId, listId, + filter: getTrimmedString(params.cardFilter), + } + } + + case 'trello_search': { + const query = getTrimmedString(params.searchQuery) + + if (!query) { + throw new Error('Search query is required.') + } + + return { + ...baseParams, + query, + idBoards: parseStringArray(params.searchBoardIds), + modelTypes: getTrimmedString(params.searchModelTypes), + cardsLimit: parseOptionalNumberInput(params.searchCardsLimit, 'cardsLimit'), } } @@ -554,6 +865,7 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, due: getTrimmedString(params.due), dueComplete: parseOptionalBooleanInput(params.dueComplete), labelIds: parseStringArray(params.labelIds), + memberIds: parseStringArray(params.memberIds), } } @@ -576,6 +888,19 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, } } + case 'trello_delete_card': { + const cardId = getTrimmedString(params.cardId) + + if (!cardId) { + throw new Error('Card ID is required.') + } + + return { + ...baseParams, + cardId, + } + } + case 'trello_get_actions': { const boardId = getTrimmedString(params.boardId) const cardId = getTrimmedString(params.cardId) @@ -595,6 +920,8 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, filter: getTrimmedString(params.filter), limit: parseOptionalNumberInput(params.limit, 'limit'), page: parseOptionalNumberInput(params.page, 'page'), + since: getTrimmedString(params.since), + before: getTrimmedString(params.before), } } @@ -666,6 +993,23 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, } } + case 'trello_update_list': { + const listId = getTrimmedString(params.listId) + + if (!listId) { + throw new Error('List ID is required.') + } + + return { + ...baseParams, + listId, + name: getTrimmedString(params.listName), + closed: parseOptionalBooleanInput(params.listClosed), + idBoard: getTrimmedString(params.moveListToBoardId), + pos: getTrimmedString(params.listPos), + } + } + case 'trello_get_card': { const cardId = getTrimmedString(params.cardId) @@ -699,6 +1043,57 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, } } + case 'trello_add_checklist_item': { + const checklistId = getTrimmedString(params.checklistId) + const name = getTrimmedString(params.itemName) + + if (!checklistId) { + throw new Error('Checklist ID is required.') + } + + if (!name) { + throw new Error('Item name is required.') + } + + return { + ...baseParams, + checklistId, + name, + pos: getTrimmedString(params.itemPos), + checked: parseOptionalBooleanInput(params.itemChecked), + } + } + + case 'trello_update_checklist_item': { + const cardId = getTrimmedString(params.cardId) + const checkItemId = getTrimmedString(params.checkItemId) + + if (!cardId) { + throw new Error('Card ID is required.') + } + + if (!checkItemId) { + throw new Error('Checklist item ID is required.') + } + + const state = getTrimmedString(params.checkItemState) + const normalizedState = + state === 'complete' || state === 'incomplete' ? state : undefined + const name = getTrimmedString(params.checkItemName) + + if (!normalizedState && !name) { + throw new Error('Provide a State or a New Item Name to update.') + } + + return { + ...baseParams, + cardId, + checkItemId, + state: normalizedState, + name, + } + } + case 'trello_add_label': { const cardId = getTrimmedString(params.cardId) const labelId = getTrimmedString(params.labelId) @@ -718,6 +1113,25 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, } } + case 'trello_remove_label': { + const cardId = getTrimmedString(params.cardId) + const labelId = getTrimmedString(params.labelId) + + if (!cardId) { + throw new Error('Card ID is required.') + } + + if (!labelId) { + throw new Error('Label ID is required.') + } + + return { + ...baseParams, + cardId, + labelId, + } + } + case 'trello_add_member': { const cardId = getTrimmedString(params.cardId) const memberId = getTrimmedString(params.memberId) @@ -737,6 +1151,38 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, } } + case 'trello_remove_member': { + const cardId = getTrimmedString(params.cardId) + const memberId = getTrimmedString(params.memberId) + + if (!cardId) { + throw new Error('Card ID is required.') + } + + if (!memberId) { + throw new Error('Member ID is required.') + } + + return { + ...baseParams, + cardId, + memberId, + } + } + + case 'trello_list_members': { + const boardId = getTrimmedString(params.boardId) + + if (!boardId) { + throw new Error('Board ID is required.') + } + + return { + ...baseParams, + boardId, + } + } + default: return baseParams } @@ -758,11 +1204,17 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, type: 'json', description: 'Label IDs as an array or comma-separated string', }, + memberIds: { + type: 'json', + description: 'Member IDs as an array or comma-separated string, to assign on card creation', + }, closed: { type: 'boolean', description: 'Whether the card should be archived or reopened' }, idList: { type: 'string', description: 'List ID to move the card to' }, filter: { type: 'string', description: 'Trello action filter' }, limit: { type: 'number', description: 'Maximum number of board actions to return' }, page: { type: 'number', description: 'Page number for action results' }, + since: { type: 'string', description: 'Only return actions after this date or action ID' }, + before: { type: 'string', description: 'Only return actions before this date or action ID' }, text: { type: 'string', description: 'Comment text' }, boardName: { type: 'string', description: 'Board name' }, boardDesc: { type: 'string', description: 'Board description' }, @@ -776,13 +1228,34 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, }, listName: { type: 'string', description: 'List name' }, listPos: { type: 'string', description: 'List position (top, bottom, or positive float)' }, + listClosed: { type: 'boolean', description: 'Whether the list should be archived or reopened' }, + moveListToBoardId: { type: 'string', description: 'Board ID to move the list to' }, + listFilter: { type: 'string', description: 'Which lists to return: open, closed, or all' }, + cardFilter: { type: 'string', description: 'Which cards to return: open, closed, or all' }, checklistName: { type: 'string', description: 'Checklist name' }, checklistPos: { type: 'string', description: 'Checklist position (top, bottom, or positive float)', }, - labelId: { type: 'string', description: 'Label ID to attach to a card' }, - memberId: { type: 'string', description: 'Member ID to assign to a card' }, + checklistId: { type: 'string', description: 'Checklist ID to add an item to' }, + itemName: { type: 'string', description: 'Checklist item name' }, + itemPos: { + type: 'string', + description: 'Checklist item position (top, bottom, or positive float)', + }, + itemChecked: { type: 'boolean', description: 'Whether the checklist item starts checked' }, + checkItemId: { type: 'string', description: 'Checklist item ID to update' }, + checkItemState: { type: 'string', description: 'Checklist item state: complete or incomplete' }, + checkItemName: { type: 'string', description: 'New name for a checklist item' }, + labelId: { type: 'string', description: 'Label ID to attach to or remove from a card' }, + memberId: { type: 'string', description: 'Member ID to assign to or remove from a card' }, + searchQuery: { type: 'string', description: 'Trello search query text' }, + searchModelTypes: { type: 'string', description: 'Search scope: all, cards, or boards' }, + searchBoardIds: { + type: 'json', + description: 'Board IDs to restrict the search to, as an array or comma-separated string', + }, + searchCardsLimit: { type: 'number', description: 'Maximum number of cards to return' }, }, outputs: { lists: { @@ -811,6 +1284,10 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, type: 'json', description: 'Created checklist (id, name, idCard, idBoard, pos)', }, + item: { + type: 'json', + description: 'Created or updated checklist item (id, name, state, pos, idChecklist)', + }, labelIds: { type: 'json', description: 'Label IDs applied to a card after adding a label', @@ -819,6 +1296,14 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, type: 'json', description: 'Member IDs assigned to a card after adding a member', }, + members: { + type: 'json', + description: 'Board members (id, fullName, username)', + }, + boards: { + type: 'json', + description: 'Boards matching a search query (id, name, desc, url, closed, idOrganization)', + }, actions: { type: 'json', description: @@ -831,7 +1316,12 @@ Return ONLY the date/timestamp string - no explanations, no extra text.`, }, count: { type: 'number', - description: 'Number of returned lists, cards, or actions', + description: 'Number of returned lists, cards, boards, actions, or members', + }, + success: { + type: 'boolean', + description: + 'Whether a delete/remove operation succeeded (delete card, remove label, remove member)', }, error: { type: 'string', @@ -939,5 +1429,19 @@ export const TrelloBlockMeta = { content: '# Review Trello Card Activity\n\nInspect what has happened recently on a board or card to build a digest or audit.\n\n## Steps\n1. Use the Get Actions operation with either a Board ID or a Card ID (one or the other, not both).\n2. Set an Action Filter such as commentCard,updateCard,createCard to focus on the events you care about.\n3. Use Board Action Limit and Action Page to page through longer histories.\n\n## Output\nReturn the actions with their type, date, author, and text, summarized into a short activity recap.', }, + { + name: 'build-and-track-checklist', + description: + 'Add a checklist to a card, populate it with items, and check items off as work completes.', + content: + '# Build and Track a Trello Checklist\n\nGive a card a task list and keep it up to date as steps finish.\n\n## Steps\n1. Use Add Checklist on the target Card ID to create an empty checklist, and note the returned Checklist ID.\n2. Use Add Checklist Item once per task, providing the Checklist ID and Item Name.\n3. As each task completes, use Update Checklist Item with the Card ID and Checklist Item ID, setting State to Complete.\n\n## Output\nReturn the checklist and item IDs created, and confirm which items were marked complete.', + }, + { + name: 'find-and-clean-up-cards', + description: + 'Search Trello for cards matching a query, then delete or archive the ones that no longer belong.', + content: + '# Find and Clean Up Trello Cards\n\nLocate cards by keyword without already knowing their IDs, then remove the ones that should not remain.\n\n## Steps\n1. Use the Search operation with a Search Query (Trello operators like board:, list:, or due: are supported) and optionally restrict to specific Board IDs.\n2. Review the matching cards and decide which should be archived (Update Card with Archive Status) or permanently removed (Delete Card).\n3. Use Delete Card only for cards that should be gone for good — prefer archiving when the history should be kept.\n\n## Output\nReturn how many cards matched, and which were archived versus deleted.', + }, ], } as const satisfies BlockMeta diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 4adbd38fb46..7f0555b42a1 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -3859,6 +3859,7 @@ import { tinybirdTruncateDatasourceTool, } from '@/tools/tinybird' import { + trelloAddChecklistItemTool, trelloAddChecklistTool, trelloAddCommentTool, trelloAddLabelTool, @@ -3866,12 +3867,19 @@ import { trelloCreateBoardTool, trelloCreateCardTool, trelloCreateListTool, + trelloDeleteCardTool, trelloGetActionsTool, trelloGetBoardTool, trelloGetCardTool, trelloListCardsTool, trelloListListsTool, + trelloListMembersTool, + trelloRemoveLabelTool, + trelloRemoveMemberTool, + trelloSearchTool, trelloUpdateCardTool, + trelloUpdateChecklistItemTool, + trelloUpdateListTool, } from '@/tools/trello' import { triggerDevActivateScheduleTool, @@ -6511,15 +6519,23 @@ export const tools: Record = { trello_list_cards: trelloListCardsTool, trello_create_card: trelloCreateCardTool, trello_update_card: trelloUpdateCardTool, + trello_delete_card: trelloDeleteCardTool, trello_get_actions: trelloGetActionsTool, trello_add_comment: trelloAddCommentTool, trello_create_board: trelloCreateBoardTool, trello_get_board: trelloGetBoardTool, trello_create_list: trelloCreateListTool, + trello_update_list: trelloUpdateListTool, trello_get_card: trelloGetCardTool, trello_add_checklist: trelloAddChecklistTool, + trello_add_checklist_item: trelloAddChecklistItemTool, + trello_update_checklist_item: trelloUpdateChecklistItemTool, trello_add_label: trelloAddLabelTool, + trello_remove_label: trelloRemoveLabelTool, trello_add_member: trelloAddMemberTool, + trello_remove_member: trelloRemoveMemberTool, + trello_list_members: trelloListMembersTool, + trello_search: trelloSearchTool, trigger_dev_trigger_task: triggerDevTriggerTaskTool, trigger_dev_batch_trigger_task: triggerDevBatchTriggerTaskTool, trigger_dev_get_batch: triggerDevGetBatchTool, diff --git a/apps/sim/tools/trello/add_checklist_item.ts b/apps/sim/tools/trello/add_checklist_item.ts new file mode 100644 index 00000000000..085768bbc61 --- /dev/null +++ b/apps/sim/tools/trello/add_checklist_item.ts @@ -0,0 +1,148 @@ +import { getErrorMessage } from '@sim/utils/errors' +import { env } from '@/lib/core/config/env' +import { + extractTrelloErrorMessage, + mapTrelloChecklistItem, + TRELLO_API_BASE_URL, +} from '@/tools/trello/shared' +import type { + TrelloAddChecklistItemParams, + TrelloAddChecklistItemResponse, +} from '@/tools/trello/types' +import type { ToolConfig } from '@/tools/types' + +export const trelloAddChecklistItemTool: ToolConfig< + TrelloAddChecklistItemParams, + TrelloAddChecklistItemResponse +> = { + id: 'trello_add_checklist_item', + name: 'Trello Add Checklist Item', + description: 'Add an item to a Trello checklist', + version: '1.0.0', + + oauth: { + required: true, + provider: 'trello', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Trello OAuth access token', + }, + checklistId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Trello checklist ID to add the item to (24-character hex string)', + }, + name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Name of the checklist item', + }, + pos: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Position of the item (top, bottom, or positive float)', + }, + checked: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether the item should start checked off', + }, + }, + + request: { + url: (params) => { + if (!params.checklistId) { + throw new Error('Checklist ID is required') + } + if (!params.name) { + throw new Error('Checklist item name is required') + } + const apiKey = env.TRELLO_API_KEY + + if (!apiKey) { + throw new Error('TRELLO_API_KEY environment variable is not set') + } + + const url = new URL( + `${TRELLO_API_BASE_URL}/checklists/${params.checklistId.trim()}/checkItems` + ) + url.searchParams.set('key', apiKey) + url.searchParams.set('token', params.accessToken) + url.searchParams.set('name', params.name.trim()) + + if (params.pos) url.searchParams.set('pos', params.pos) + if (params.checked !== undefined) url.searchParams.set('checked', String(params.checked)) + + return url.toString() + }, + method: 'POST', + headers: () => ({ + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json().catch(() => null) + + if (!response.ok) { + const error = extractTrelloErrorMessage(response, data, 'Failed to add checklist item') + + return { + success: false, + output: { + error, + }, + error, + } + } + + try { + const item = mapTrelloChecklistItem(data) + + return { + success: true, + output: { + item, + }, + } + } catch (error) { + const message = getErrorMessage(error, 'Failed to parse created checklist item') + + return { + success: false, + output: { + error: message, + }, + error: message, + } + } + }, + + outputs: { + item: { + type: 'json', + description: 'Created checklist item (id, name, state, pos, idChecklist)', + optional: true, + properties: { + id: { type: 'string', description: 'Checklist item ID' }, + name: { type: 'string', description: 'Checklist item name' }, + state: { type: 'string', description: 'Item state (complete or incomplete)' }, + pos: { type: 'number', description: 'Item position on the checklist' }, + idChecklist: { + type: 'string', + description: 'Checklist ID containing the item', + optional: true, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/trello/create_card.ts b/apps/sim/tools/trello/create_card.ts index b71f128a433..26b9d78d7e0 100644 --- a/apps/sim/tools/trello/create_card.ts +++ b/apps/sim/tools/trello/create_card.ts @@ -72,6 +72,16 @@ export const trelloCreateCardTool: ToolConfig = { + id: 'trello_delete_card', + name: 'Trello Delete Card', + description: 'Permanently delete a Trello card', + version: '1.0.0', + + oauth: { + required: true, + provider: 'trello', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Trello OAuth access token', + }, + cardId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Trello card ID to permanently delete (24-character hex string)', + }, + }, + + request: { + url: (params) => { + if (!params.cardId) { + throw new Error('Card ID is required') + } + const apiKey = env.TRELLO_API_KEY + + if (!apiKey) { + throw new Error('TRELLO_API_KEY environment variable is not set') + } + + const url = new URL(`${TRELLO_API_BASE_URL}/cards/${params.cardId.trim()}`) + url.searchParams.set('key', apiKey) + url.searchParams.set('token', params.accessToken) + + return url.toString() + }, + method: 'DELETE', + headers: () => ({ + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json().catch(() => null) + + if (!response.ok) { + const error = extractTrelloErrorMessage(response, data, 'Failed to delete card') + + return { + success: false, + output: { + success: false, + error, + }, + error, + } + } + + return { + success: true, + output: { + success: true, + }, + } + }, + + outputs: { + success: { + type: 'boolean', + description: 'Whether the card was deleted', + }, + }, +} diff --git a/apps/sim/tools/trello/get_actions.ts b/apps/sim/tools/trello/get_actions.ts index 00857a5bede..edeab1a830c 100644 --- a/apps/sim/tools/trello/get_actions.ts +++ b/apps/sim/tools/trello/get_actions.ts @@ -56,6 +56,20 @@ export const trelloGetActionsTool: ToolConfig = + { + id: 'trello_list_members', + name: 'Trello List Members', + description: 'List members of a Trello board', + version: '1.0.0', + + oauth: { + required: true, + provider: 'trello', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Trello OAuth access token', + }, + boardId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Trello board ID (24-character hex string)', + }, + }, + + request: { + url: (params) => { + if (!params.boardId) { + throw new Error('Board ID is required') + } + const apiKey = env.TRELLO_API_KEY + + if (!apiKey) { + throw new Error('TRELLO_API_KEY environment variable is not set') + } + + const url = new URL(`${TRELLO_API_BASE_URL}/boards/${params.boardId.trim()}/members`) + url.searchParams.set('key', apiKey) + url.searchParams.set('token', params.accessToken) + + return url.toString() + }, + method: 'GET', + headers: () => ({ + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json().catch(() => null) + + if (!response.ok) { + const error = extractTrelloErrorMessage(response, data, 'Failed to list board members') + + return { + success: false, + output: { + members: [], + count: 0, + error, + }, + error, + } + } + + if (!Array.isArray(data)) { + const error = 'Trello returned an invalid member collection' + + return { + success: false, + output: { + members: [], + count: 0, + error, + }, + error, + } + } + + try { + const members = data + .map((item) => mapTrelloMember(item)) + .filter((member): member is NonNullable => member !== null) + + return { + success: true, + output: { + members, + count: members.length, + }, + } + } catch (error) { + const message = getErrorMessage(error, 'Failed to parse board members') + + return { + success: false, + output: { + members: [], + count: 0, + error: message, + }, + error: message, + } + } + }, + + outputs: { + members: { + type: 'array', + description: 'Members on the selected board', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Member ID' }, + fullName: { type: 'string', description: 'Member full name', optional: true }, + username: { type: 'string', description: 'Member username', optional: true }, + }, + }, + }, + count: { type: 'number', description: 'Number of members returned' }, + }, + } diff --git a/apps/sim/tools/trello/remove_label.ts b/apps/sim/tools/trello/remove_label.ts new file mode 100644 index 00000000000..27be742794d --- /dev/null +++ b/apps/sim/tools/trello/remove_label.ts @@ -0,0 +1,97 @@ +import { env } from '@/lib/core/config/env' +import { extractTrelloErrorMessage, TRELLO_API_BASE_URL } from '@/tools/trello/shared' +import type { TrelloRemoveLabelParams, TrelloRemoveLabelResponse } from '@/tools/trello/types' +import type { ToolConfig } from '@/tools/types' + +export const trelloRemoveLabelTool: ToolConfig = + { + id: 'trello_remove_label', + name: 'Trello Remove Label', + description: 'Detach a label from a Trello card', + version: '1.0.0', + + oauth: { + required: true, + provider: 'trello', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Trello OAuth access token', + }, + cardId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Trello card ID to detach the label from (24-character hex string)', + }, + labelId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the label to detach (24-character hex string)', + }, + }, + + request: { + url: (params) => { + if (!params.cardId) { + throw new Error('Card ID is required') + } + if (!params.labelId) { + throw new Error('Label ID is required') + } + const apiKey = env.TRELLO_API_KEY + + if (!apiKey) { + throw new Error('TRELLO_API_KEY environment variable is not set') + } + + const url = new URL( + `${TRELLO_API_BASE_URL}/cards/${params.cardId.trim()}/idLabels/${params.labelId.trim()}` + ) + url.searchParams.set('key', apiKey) + url.searchParams.set('token', params.accessToken) + + return url.toString() + }, + method: 'DELETE', + headers: () => ({ + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json().catch(() => null) + + if (!response.ok) { + const error = extractTrelloErrorMessage(response, data, 'Failed to remove label') + + return { + success: false, + output: { + success: false, + error, + }, + error, + } + } + + return { + success: true, + output: { + success: true, + }, + } + }, + + outputs: { + success: { + type: 'boolean', + description: 'Whether the label was removed from the card', + }, + }, + } diff --git a/apps/sim/tools/trello/remove_member.ts b/apps/sim/tools/trello/remove_member.ts new file mode 100644 index 00000000000..f722efd5e05 --- /dev/null +++ b/apps/sim/tools/trello/remove_member.ts @@ -0,0 +1,99 @@ +import { env } from '@/lib/core/config/env' +import { extractTrelloErrorMessage, TRELLO_API_BASE_URL } from '@/tools/trello/shared' +import type { TrelloRemoveMemberParams, TrelloRemoveMemberResponse } from '@/tools/trello/types' +import type { ToolConfig } from '@/tools/types' + +export const trelloRemoveMemberTool: ToolConfig< + TrelloRemoveMemberParams, + TrelloRemoveMemberResponse +> = { + id: 'trello_remove_member', + name: 'Trello Remove Member', + description: 'Unassign a member from a Trello card', + version: '1.0.0', + + oauth: { + required: true, + provider: 'trello', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Trello OAuth access token', + }, + cardId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Trello card ID to unassign the member from (24-character hex string)', + }, + memberId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the member to unassign (24-character hex string)', + }, + }, + + request: { + url: (params) => { + if (!params.cardId) { + throw new Error('Card ID is required') + } + if (!params.memberId) { + throw new Error('Member ID is required') + } + const apiKey = env.TRELLO_API_KEY + + if (!apiKey) { + throw new Error('TRELLO_API_KEY environment variable is not set') + } + + const url = new URL( + `${TRELLO_API_BASE_URL}/cards/${params.cardId.trim()}/idMembers/${params.memberId.trim()}` + ) + url.searchParams.set('key', apiKey) + url.searchParams.set('token', params.accessToken) + + return url.toString() + }, + method: 'DELETE', + headers: () => ({ + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json().catch(() => null) + + if (!response.ok) { + const error = extractTrelloErrorMessage(response, data, 'Failed to remove member') + + return { + success: false, + output: { + success: false, + error, + }, + error, + } + } + + return { + success: true, + output: { + success: true, + }, + } + }, + + outputs: { + success: { + type: 'boolean', + description: 'Whether the member was removed from the card', + }, + }, +} diff --git a/apps/sim/tools/trello/search.ts b/apps/sim/tools/trello/search.ts new file mode 100644 index 00000000000..6d737ad155a --- /dev/null +++ b/apps/sim/tools/trello/search.ts @@ -0,0 +1,195 @@ +import { getErrorMessage } from '@sim/utils/errors' +import { isRecordLike } from '@sim/utils/object' +import { env } from '@/lib/core/config/env' +import { + extractTrelloErrorMessage, + mapTrelloBoard, + mapTrelloCard, + TRELLO_API_BASE_URL, +} from '@/tools/trello/shared' +import type { TrelloSearchParams, TrelloSearchResponse } from '@/tools/trello/types' +import type { ToolConfig } from '@/tools/types' + +export const trelloSearchTool: ToolConfig = { + id: 'trello_search', + name: 'Trello Search', + description: 'Search Trello cards and boards by keyword', + version: '1.0.0', + + oauth: { + required: true, + provider: 'trello', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Trello OAuth access token', + }, + query: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Search text, supports Trello search operators (e.g. board:, list:, due:)', + }, + idBoards: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Restrict the search to these board IDs', + items: { + type: 'string', + description: 'A Trello board ID', + }, + }, + modelTypes: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated result types to search: cards, boards, or all (default all)', + }, + cardsLimit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of cards to return (1-1000, default 10)', + }, + }, + + request: { + url: (params) => { + if (!params.query) { + throw new Error('Search query is required') + } + const apiKey = env.TRELLO_API_KEY + + if (!apiKey) { + throw new Error('TRELLO_API_KEY environment variable is not set') + } + + const url = new URL(`${TRELLO_API_BASE_URL}/search`) + url.searchParams.set('key', apiKey) + url.searchParams.set('token', params.accessToken) + url.searchParams.set('query', params.query) + url.searchParams.set('modelTypes', params.modelTypes || 'all') + + if (params.idBoards?.length) { + url.searchParams.set('idBoards', params.idBoards.join(',')) + } + + if (params.cardsLimit !== undefined) { + url.searchParams.set('cards_limit', String(params.cardsLimit)) + } + + return url.toString() + }, + method: 'GET', + headers: () => ({ + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json().catch(() => null) + + if (!response.ok) { + const error = extractTrelloErrorMessage(response, data, 'Failed to search Trello') + + return { + success: false, + output: { + cards: [], + boards: [], + count: 0, + error, + }, + error, + } + } + + if (!isRecordLike(data)) { + const error = 'Trello returned an invalid search result' + + return { + success: false, + output: { + cards: [], + boards: [], + count: 0, + error, + }, + error, + } + } + + try { + const rawCards = Array.isArray(data.cards) ? data.cards : [] + const rawBoards = Array.isArray(data.boards) ? data.boards : [] + const cards = rawCards.map((item) => mapTrelloCard(item)) + const boards = rawBoards.map((item) => mapTrelloBoard(item)) + + return { + success: true, + output: { + cards, + boards, + count: cards.length + boards.length, + }, + } + } catch (error) { + const message = getErrorMessage(error, 'Failed to parse Trello search results') + + return { + success: false, + output: { + cards: [], + boards: [], + count: 0, + error: message, + }, + error: message, + } + } + }, + + outputs: { + cards: { + type: 'array', + description: 'Cards matching the search query', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Card ID' }, + name: { type: 'string', description: 'Card name' }, + desc: { type: 'string', description: 'Card description' }, + url: { type: 'string', description: 'Full card URL' }, + idBoard: { type: 'string', description: 'Board ID containing the card' }, + idList: { type: 'string', description: 'List ID containing the card' }, + closed: { type: 'boolean', description: 'Whether the card is archived' }, + }, + }, + }, + boards: { + type: 'array', + description: 'Boards matching the search query', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Board ID' }, + name: { type: 'string', description: 'Board name' }, + desc: { type: 'string', description: 'Board description' }, + url: { type: 'string', description: 'Full board URL' }, + closed: { type: 'boolean', description: 'Whether the board is archived' }, + idOrganization: { + type: 'string', + description: 'Workspace/organization ID that owns the board', + optional: true, + }, + }, + }, + }, + count: { type: 'number', description: 'Total number of cards and boards returned' }, + }, +} diff --git a/apps/sim/tools/trello/shared.ts b/apps/sim/tools/trello/shared.ts index 27617a5ca77..b1c2346af83 100644 --- a/apps/sim/tools/trello/shared.ts +++ b/apps/sim/tools/trello/shared.ts @@ -7,6 +7,7 @@ import type { TrelloBoard, TrelloCard, TrelloChecklist, + TrelloChecklistItem, TrelloComment, TrelloLabel, TrelloList, @@ -84,7 +85,7 @@ function mapTrelloLabel(value: unknown): TrelloLabel | null { } } -function mapTrelloMember(value: unknown): TrelloMember | null { +export function mapTrelloMember(value: unknown): TrelloMember | null { if (!isRecordLike(value) || typeof value.id !== 'string') { return null } @@ -206,6 +207,20 @@ export function mapTrelloChecklist(value: unknown): TrelloChecklist { } } +export function mapTrelloChecklistItem(value: unknown): TrelloChecklistItem { + if (!isRecordLike(value)) { + throw new Error('Trello returned an invalid checklist item object') + } + + return { + id: getRequiredString(value.id, 'id'), + name: getRequiredString(value.name, 'name'), + state: getRequiredString(value.state, 'state'), + pos: getNumber(value.pos), + idChecklist: getOptionalString(value.idChecklist), + } +} + export function mapTrelloAction(value: unknown): TrelloAction { if (!isRecordLike(value)) { throw new Error('Trello returned an invalid action object') diff --git a/apps/sim/tools/trello/types.ts b/apps/sim/tools/trello/types.ts index fdbbbc8d7e3..de253a083e4 100644 --- a/apps/sim/tools/trello/types.ts +++ b/apps/sim/tools/trello/types.ts @@ -87,12 +87,14 @@ export interface TrelloComment extends TrelloAction {} export interface TrelloListListsParams { accessToken: string boardId: string + filter?: string } export interface TrelloListCardsParams { accessToken: string boardId?: string listId?: string + filter?: string } export interface TrelloCreateCardParams { @@ -104,6 +106,7 @@ export interface TrelloCreateCardParams { due?: string dueComplete?: boolean labelIds?: string[] + memberIds?: string[] } export interface TrelloUpdateCardParams { @@ -117,6 +120,11 @@ export interface TrelloUpdateCardParams { dueComplete?: boolean } +export interface TrelloDeleteCardParams { + accessToken: string + cardId: string +} + export interface TrelloGetActionsParams { accessToken: string boardId?: string @@ -124,6 +132,8 @@ export interface TrelloGetActionsParams { filter?: string limit?: number page?: number + since?: string + before?: string } export interface TrelloAddCommentParams { @@ -164,18 +174,68 @@ export interface TrelloAddChecklistParams { pos?: string } +export interface TrelloAddChecklistItemParams { + accessToken: string + checklistId: string + name: string + pos?: string + checked?: boolean +} + +export interface TrelloUpdateChecklistItemParams { + accessToken: string + cardId: string + checkItemId: string + state?: 'complete' | 'incomplete' + name?: string +} + export interface TrelloAddLabelParams { accessToken: string cardId: string labelId: string } +export interface TrelloRemoveLabelParams { + accessToken: string + cardId: string + labelId: string +} + export interface TrelloAddMemberParams { accessToken: string cardId: string memberId: string } +export interface TrelloRemoveMemberParams { + accessToken: string + cardId: string + memberId: string +} + +export interface TrelloListMembersParams { + accessToken: string + boardId: string +} + +export interface TrelloUpdateListParams { + accessToken: string + listId: string + name?: string + closed?: boolean + idBoard?: string + pos?: string +} + +export interface TrelloSearchParams { + accessToken: string + query: string + idBoards?: string[] + modelTypes?: string + cardsLimit?: number +} + export interface TrelloListListsResponse extends ToolResponse { output: { lists: TrelloList[] @@ -256,6 +316,28 @@ export interface TrelloAddChecklistResponse extends ToolResponse { } } +export interface TrelloChecklistItem { + id: string + name: string + state: string + pos: number + idChecklist: string | null +} + +export interface TrelloAddChecklistItemResponse extends ToolResponse { + output: { + item?: TrelloChecklistItem + error?: string + } +} + +export interface TrelloUpdateChecklistItemResponse extends ToolResponse { + output: { + item?: TrelloChecklistItem + error?: string + } +} + export interface TrelloAddLabelResponse extends ToolResponse { output: { labelIds: string[] @@ -263,6 +345,13 @@ export interface TrelloAddLabelResponse extends ToolResponse { } } +export interface TrelloRemoveLabelResponse extends ToolResponse { + output: { + success: boolean + error?: string + } +} + export interface TrelloAddMemberResponse extends ToolResponse { output: { memberIds: string[] @@ -270,17 +359,63 @@ export interface TrelloAddMemberResponse extends ToolResponse { } } +export interface TrelloRemoveMemberResponse extends ToolResponse { + output: { + success: boolean + error?: string + } +} + +export interface TrelloListMembersResponse extends ToolResponse { + output: { + members: TrelloMember[] + count: number + error?: string + } +} + +export interface TrelloUpdateListResponse extends ToolResponse { + output: { + list?: TrelloList + error?: string + } +} + +export interface TrelloDeleteCardResponse extends ToolResponse { + output: { + success: boolean + error?: string + } +} + +export interface TrelloSearchResponse extends ToolResponse { + output: { + cards: TrelloCard[] + boards: TrelloBoard[] + count: number + error?: string + } +} + export type TrelloResponse = | TrelloListListsResponse | TrelloListCardsResponse | TrelloCreateCardResponse | TrelloUpdateCardResponse + | TrelloDeleteCardResponse | TrelloGetActionsResponse | TrelloAddCommentResponse | TrelloCreateBoardResponse | TrelloGetBoardResponse | TrelloCreateListResponse + | TrelloUpdateListResponse | TrelloGetCardResponse | TrelloAddChecklistResponse + | TrelloAddChecklistItemResponse + | TrelloUpdateChecklistItemResponse | TrelloAddLabelResponse + | TrelloRemoveLabelResponse | TrelloAddMemberResponse + | TrelloRemoveMemberResponse + | TrelloListMembersResponse + | TrelloSearchResponse diff --git a/apps/sim/tools/trello/update_checklist_item.ts b/apps/sim/tools/trello/update_checklist_item.ts new file mode 100644 index 00000000000..5c2f2939866 --- /dev/null +++ b/apps/sim/tools/trello/update_checklist_item.ts @@ -0,0 +1,150 @@ +import { getErrorMessage } from '@sim/utils/errors' +import { env } from '@/lib/core/config/env' +import { + extractTrelloErrorMessage, + mapTrelloChecklistItem, + TRELLO_API_BASE_URL, +} from '@/tools/trello/shared' +import type { + TrelloUpdateChecklistItemParams, + TrelloUpdateChecklistItemResponse, +} from '@/tools/trello/types' +import type { ToolConfig } from '@/tools/types' + +export const trelloUpdateChecklistItemTool: ToolConfig< + TrelloUpdateChecklistItemParams, + TrelloUpdateChecklistItemResponse +> = { + id: 'trello_update_checklist_item', + name: 'Trello Update Checklist Item', + description: 'Check off, uncheck, or rename a Trello checklist item', + version: '1.0.0', + + oauth: { + required: true, + provider: 'trello', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Trello OAuth access token', + }, + cardId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Trello card ID that owns the checklist item (24-character hex string)', + }, + checkItemId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Checklist item ID to update (24-character hex string)', + }, + state: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Set the item state to complete or incomplete', + }, + name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'New name for the checklist item', + }, + }, + + request: { + url: (params) => { + if (!params.cardId) { + throw new Error('Card ID is required') + } + if (!params.checkItemId) { + throw new Error('Checklist item ID is required') + } + if (!params.state && !params.name) { + throw new Error('At least one of state or name must be provided to update') + } + const apiKey = env.TRELLO_API_KEY + + if (!apiKey) { + throw new Error('TRELLO_API_KEY environment variable is not set') + } + + const url = new URL( + `${TRELLO_API_BASE_URL}/cards/${params.cardId.trim()}/checkItem/${params.checkItemId.trim()}` + ) + url.searchParams.set('key', apiKey) + url.searchParams.set('token', params.accessToken) + + if (params.state) url.searchParams.set('state', params.state) + if (params.name) url.searchParams.set('name', params.name.trim()) + + return url.toString() + }, + method: 'PUT', + headers: () => ({ + Accept: 'application/json', + }), + }, + + transformResponse: async (response) => { + const data = await response.json().catch(() => null) + + if (!response.ok) { + const error = extractTrelloErrorMessage(response, data, 'Failed to update checklist item') + + return { + success: false, + output: { + error, + }, + error, + } + } + + try { + const item = mapTrelloChecklistItem(data) + + return { + success: true, + output: { + item, + }, + } + } catch (error) { + const message = getErrorMessage(error, 'Failed to parse updated checklist item') + + return { + success: false, + output: { + error: message, + }, + error: message, + } + } + }, + + outputs: { + item: { + type: 'json', + description: 'Updated checklist item (id, name, state, pos, idChecklist)', + optional: true, + properties: { + id: { type: 'string', description: 'Checklist item ID' }, + name: { type: 'string', description: 'Checklist item name' }, + state: { type: 'string', description: 'Item state (complete or incomplete)' }, + pos: { type: 'number', description: 'Item position on the checklist' }, + idChecklist: { + type: 'string', + description: 'Checklist ID containing the item', + optional: true, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/trello/update_list.ts b/apps/sim/tools/trello/update_list.ts new file mode 100644 index 00000000000..604cc3be0fb --- /dev/null +++ b/apps/sim/tools/trello/update_list.ts @@ -0,0 +1,150 @@ +import { getErrorMessage } from '@sim/utils/errors' +import { env } from '@/lib/core/config/env' +import { + extractTrelloErrorMessage, + mapTrelloList, + TRELLO_API_BASE_URL, +} from '@/tools/trello/shared' +import type { TrelloUpdateListParams, TrelloUpdateListResponse } from '@/tools/trello/types' +import type { ToolConfig } from '@/tools/types' + +export const trelloUpdateListTool: ToolConfig = { + id: 'trello_update_list', + name: 'Trello Update List', + description: 'Rename, move, archive, or reopen a Trello list', + version: '1.0.0', + + oauth: { + required: true, + provider: 'trello', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Trello OAuth access token', + }, + listId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Trello list ID (24-character hex string)', + }, + name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'New name of the list', + }, + closed: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Archive the list (true) or reopen it (false)', + }, + idBoard: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Board ID to move the list to (24-character hex string)', + }, + pos: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'New position of the list (top, bottom, or positive float)', + }, + }, + + request: { + url: (params) => { + if (!params.listId) { + throw new Error('List ID is required') + } + const apiKey = env.TRELLO_API_KEY + + if (!apiKey) { + throw new Error('TRELLO_API_KEY environment variable is not set') + } + + const url = new URL(`${TRELLO_API_BASE_URL}/lists/${params.listId.trim()}`) + url.searchParams.set('key', apiKey) + url.searchParams.set('token', params.accessToken) + + return url.toString() + }, + method: 'PUT', + headers: () => ({ + 'Content-Type': 'application/json', + Accept: 'application/json', + }), + body: (params) => { + const body: Record = {} + + if (params.name !== undefined) body.name = params.name + if (params.closed !== undefined) body.closed = params.closed + if (params.idBoard !== undefined) body.idBoard = params.idBoard.trim() + if (params.pos !== undefined) body.pos = params.pos + + if (Object.keys(body).length === 0) { + throw new Error('At least one field must be provided to update') + } + + return body + }, + }, + + transformResponse: async (response) => { + const data = await response.json().catch(() => null) + + if (!response.ok) { + const error = extractTrelloErrorMessage(response, data, 'Failed to update list') + + return { + success: false, + output: { + error, + }, + error, + } + } + + try { + const list = mapTrelloList(data) + + return { + success: true, + output: { + list, + }, + } + } catch (error) { + const message = getErrorMessage(error, 'Failed to parse updated list') + + return { + success: false, + output: { + error: message, + }, + error: message, + } + } + }, + + outputs: { + list: { + type: 'json', + description: 'Updated list (id, name, closed, pos, idBoard)', + optional: true, + properties: { + id: { type: 'string', description: 'List ID' }, + name: { type: 'string', description: 'List name' }, + closed: { type: 'boolean', description: 'Whether the list is archived' }, + pos: { type: 'number', description: 'List position on the board' }, + idBoard: { type: 'string', description: 'Board ID containing the list' }, + }, + }, + }, +}