chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
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) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
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
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import {
|
||||
extractTrelloErrorMessage,
|
||||
mapTrelloChecklist,
|
||||
TRELLO_API_BASE_URL,
|
||||
} from '@/tools/trello/shared'
|
||||
import type { TrelloAddChecklistParams, TrelloAddChecklistResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloAddChecklistTool: ToolConfig<
|
||||
TrelloAddChecklistParams,
|
||||
TrelloAddChecklistResponse
|
||||
> = {
|
||||
id: 'trello_add_checklist',
|
||||
name: 'Trello Add Checklist',
|
||||
description: 'Add a checklist to 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 add the checklist to (24-character hex string)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the checklist',
|
||||
},
|
||||
pos: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Position of the checklist (top, bottom, or positive float)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
if (!params.cardId) {
|
||||
throw new Error('Card ID is required')
|
||||
}
|
||||
if (!params.name) {
|
||||
throw new Error('Checklist 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}/cards/${params.cardId.trim()}/checklists`)
|
||||
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)
|
||||
|
||||
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')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const checklist = mapTrelloChecklist(data)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
checklist,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error, 'Failed to parse created checklist')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error: message,
|
||||
},
|
||||
error: message,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
checklist: {
|
||||
type: 'json',
|
||||
description: 'Created checklist (id, name, idCard, idBoard, pos)',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Checklist ID' },
|
||||
name: { type: 'string', description: 'Checklist name' },
|
||||
idCard: { type: 'string', description: 'Card ID containing the checklist' },
|
||||
idBoard: {
|
||||
type: 'string',
|
||||
description: 'Board ID containing the checklist',
|
||||
optional: true,
|
||||
},
|
||||
pos: { type: 'number', description: 'Checklist position on the card' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import {
|
||||
extractTrelloErrorMessage,
|
||||
mapTrelloComment,
|
||||
TRELLO_API_BASE_URL,
|
||||
} from '@/tools/trello/shared'
|
||||
import type { TrelloAddCommentParams, TrelloAddCommentResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloAddCommentTool: ToolConfig<TrelloAddCommentParams, TrelloAddCommentResponse> = {
|
||||
id: 'trello_add_comment',
|
||||
name: 'Trello Add Comment',
|
||||
description: 'Add a comment to 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 (24-character hex string)',
|
||||
},
|
||||
text: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment text',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
if (!params.cardId) {
|
||||
throw new Error('Card ID is required')
|
||||
}
|
||||
if (!params.text) {
|
||||
throw new Error('Comment text 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()}/actions/comments`)
|
||||
url.searchParams.set('key', apiKey)
|
||||
url.searchParams.set('token', params.accessToken)
|
||||
url.searchParams.set('text', params.text)
|
||||
|
||||
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 comment')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const comment = mapTrelloComment(data)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
comment,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error, 'Failed to parse created comment')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error: message,
|
||||
},
|
||||
error: message,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
comment: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Created comment action (id, type, date, idMemberCreator, text, memberCreator, card, board, list)',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Action ID' },
|
||||
type: { type: 'string', description: 'Action type' },
|
||||
date: { type: 'string', description: 'Action timestamp' },
|
||||
idMemberCreator: {
|
||||
type: 'string',
|
||||
description: 'ID of the member who created the comment',
|
||||
},
|
||||
text: {
|
||||
type: 'string',
|
||||
description: 'Comment text',
|
||||
optional: true,
|
||||
},
|
||||
memberCreator: {
|
||||
type: 'object',
|
||||
description: 'Member who created the comment',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Member ID' },
|
||||
fullName: {
|
||||
type: 'string',
|
||||
description: 'Member full name',
|
||||
optional: true,
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
description: 'Member username',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
card: {
|
||||
type: 'object',
|
||||
description: 'Card referenced by the comment',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Card ID' },
|
||||
name: { type: 'string', description: 'Card name' },
|
||||
shortLink: {
|
||||
type: 'string',
|
||||
description: 'Short card link',
|
||||
optional: true,
|
||||
},
|
||||
idShort: {
|
||||
type: 'number',
|
||||
description: 'Board-local card number',
|
||||
optional: true,
|
||||
},
|
||||
due: {
|
||||
type: 'string',
|
||||
description: 'Card due date',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
board: {
|
||||
type: 'object',
|
||||
description: 'Board referenced by the comment',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Board ID' },
|
||||
name: { type: 'string', description: 'Board name' },
|
||||
shortLink: {
|
||||
type: 'string',
|
||||
description: 'Short board link',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
list: {
|
||||
type: 'object',
|
||||
description: 'List referenced by the comment',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'List ID' },
|
||||
name: { type: 'string', description: 'List name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { extractTrelloErrorMessage, getIdArray, TRELLO_API_BASE_URL } from '@/tools/trello/shared'
|
||||
import type { TrelloAddLabelParams, TrelloAddLabelResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloAddLabelTool: ToolConfig<TrelloAddLabelParams, TrelloAddLabelResponse> = {
|
||||
id: 'trello_add_label',
|
||||
name: 'Trello Add Label',
|
||||
description: 'Attach an existing label to 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 attach the label to (24-character hex string)',
|
||||
},
|
||||
labelId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the label to attach (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`)
|
||||
url.searchParams.set('key', apiKey)
|
||||
url.searchParams.set('token', params.accessToken)
|
||||
url.searchParams.set('value', params.labelId.trim())
|
||||
|
||||
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 label')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
labelIds: [],
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
labelIds: getIdArray(data),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
labelIds: {
|
||||
type: 'array',
|
||||
description: 'Label IDs now applied to the card',
|
||||
items: {
|
||||
type: 'string',
|
||||
description: 'A Trello label ID',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { extractTrelloErrorMessage, getIdArray, TRELLO_API_BASE_URL } from '@/tools/trello/shared'
|
||||
import type { TrelloAddMemberParams, TrelloAddMemberResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloAddMemberTool: ToolConfig<TrelloAddMemberParams, TrelloAddMemberResponse> = {
|
||||
id: 'trello_add_member',
|
||||
name: 'Trello Add Member',
|
||||
description: 'Assign a member to 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 assign the member to (24-character hex string)',
|
||||
},
|
||||
memberId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the member to assign (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`)
|
||||
url.searchParams.set('key', apiKey)
|
||||
url.searchParams.set('token', params.accessToken)
|
||||
url.searchParams.set('value', params.memberId.trim())
|
||||
|
||||
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 member')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
memberIds: [],
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
memberIds: getIdArray(data),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
memberIds: {
|
||||
type: 'array',
|
||||
description: 'Member IDs now assigned to the card',
|
||||
items: {
|
||||
type: 'string',
|
||||
description: 'A Trello member ID',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import {
|
||||
extractTrelloErrorMessage,
|
||||
mapTrelloBoard,
|
||||
TRELLO_API_BASE_URL,
|
||||
} from '@/tools/trello/shared'
|
||||
import type { TrelloCreateBoardParams, TrelloCreateBoardResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloCreateBoardTool: ToolConfig<TrelloCreateBoardParams, TrelloCreateBoardResponse> =
|
||||
{
|
||||
id: 'trello_create_board',
|
||||
name: 'Trello Create Board',
|
||||
description: 'Create a new Trello board',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'trello',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Trello OAuth access token',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the board',
|
||||
},
|
||||
desc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Description of the board',
|
||||
},
|
||||
idOrganization: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID or name of the workspace/organization the board belongs to',
|
||||
},
|
||||
defaultLists: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to create the default lists (To Do, Doing, Done) on the new board',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
if (!params.name) {
|
||||
throw new Error('Board 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}/boards`)
|
||||
url.searchParams.set('key', apiKey)
|
||||
url.searchParams.set('token', params.accessToken)
|
||||
url.searchParams.set('name', params.name.trim())
|
||||
|
||||
if (params.desc) url.searchParams.set('desc', params.desc)
|
||||
if (params.idOrganization)
|
||||
url.searchParams.set('idOrganization', params.idOrganization.trim())
|
||||
if (params.defaultLists !== undefined) {
|
||||
url.searchParams.set('defaultLists', String(params.defaultLists))
|
||||
}
|
||||
|
||||
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 create board')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const board = mapTrelloBoard(data)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
board,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error, 'Failed to parse created board')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error: message,
|
||||
},
|
||||
error: message,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
board: {
|
||||
type: 'json',
|
||||
description: 'Created board (id, name, desc, url, closed, idOrganization)',
|
||||
optional: true,
|
||||
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 closed' },
|
||||
idOrganization: {
|
||||
type: 'string',
|
||||
description: 'ID of the workspace/organization the board belongs to',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import {
|
||||
extractTrelloErrorMessage,
|
||||
mapTrelloCard,
|
||||
TRELLO_API_BASE_URL,
|
||||
} from '@/tools/trello/shared'
|
||||
import type { TrelloCreateCardParams, TrelloCreateCardResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloCreateCardTool: ToolConfig<TrelloCreateCardParams, TrelloCreateCardResponse> = {
|
||||
id: 'trello_create_card',
|
||||
name: 'Trello Create Card',
|
||||
description: 'Create a new card in 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: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name/title of the card',
|
||||
},
|
||||
desc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Description of the card',
|
||||
},
|
||||
pos: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Position of the card (top, bottom, or positive float)',
|
||||
},
|
||||
due: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Due date (ISO 8601 format)',
|
||||
},
|
||||
dueComplete: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the due date should be marked complete',
|
||||
},
|
||||
labelIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Label IDs to attach to the card',
|
||||
items: {
|
||||
type: 'string',
|
||||
description: 'A Trello label ID',
|
||||
},
|
||||
},
|
||||
memberIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Member IDs to assign to the card',
|
||||
items: {
|
||||
type: 'string',
|
||||
description: 'A Trello member ID',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
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`)
|
||||
url.searchParams.set('key', apiKey)
|
||||
url.searchParams.set('token', params.accessToken)
|
||||
|
||||
return url.toString()
|
||||
},
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
if (!params.name) {
|
||||
throw new Error('Card name is required')
|
||||
}
|
||||
if (!params.listId) {
|
||||
throw new Error('List ID is required')
|
||||
}
|
||||
|
||||
const body: Record<string, unknown> = {
|
||||
idList: params.listId.trim(),
|
||||
name: params.name,
|
||||
}
|
||||
|
||||
if (params.desc) body.desc = params.desc
|
||||
if (params.pos) body.pos = params.pos
|
||||
if (params.due) body.due = params.due
|
||||
if (params.dueComplete !== undefined) body.dueComplete = params.dueComplete
|
||||
if (params.labelIds?.length) body.idLabels = params.labelIds
|
||||
if (params.memberIds?.length) body.idMembers = params.memberIds
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json().catch(() => null)
|
||||
|
||||
if (!response.ok) {
|
||||
const error = extractTrelloErrorMessage(response, data, 'Failed to create card')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const card = mapTrelloCard(data)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
card,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error, 'Failed to parse created card')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error: message,
|
||||
},
|
||||
error: message,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
card: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Created card (id, name, desc, url, idBoard, idList, closed, labelIds, labels, due, dueComplete)',
|
||||
optional: true,
|
||||
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' },
|
||||
labelIds: {
|
||||
type: 'array',
|
||||
description: 'Label IDs applied to the card',
|
||||
items: {
|
||||
type: 'string',
|
||||
description: 'A Trello label ID',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
description: 'Labels applied to the card',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Label ID' },
|
||||
name: { type: 'string', description: 'Label name' },
|
||||
color: {
|
||||
type: 'string',
|
||||
description: 'Label color',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
due: {
|
||||
type: 'string',
|
||||
description: 'Card due date in ISO 8601 format',
|
||||
optional: true,
|
||||
},
|
||||
dueComplete: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the due date is complete',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
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 { TrelloCreateListParams, TrelloCreateListResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloCreateListTool: ToolConfig<TrelloCreateListParams, TrelloCreateListResponse> = {
|
||||
id: 'trello_create_list',
|
||||
name: 'Trello Create List',
|
||||
description: 'Create a new list on 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 the list belongs to (24-character hex string)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the list',
|
||||
},
|
||||
pos: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Position of the list (top, bottom, or positive float)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
if (!params.name) {
|
||||
throw new Error('List name is required')
|
||||
}
|
||||
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}/lists`)
|
||||
url.searchParams.set('key', apiKey)
|
||||
url.searchParams.set('token', params.accessToken)
|
||||
url.searchParams.set('name', params.name.trim())
|
||||
url.searchParams.set('idBoard', params.boardId.trim())
|
||||
|
||||
if (params.pos) url.searchParams.set('pos', params.pos)
|
||||
|
||||
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 create 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 created list')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error: message,
|
||||
},
|
||||
error: message,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
list: {
|
||||
type: 'json',
|
||||
description: 'Created 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' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { extractTrelloErrorMessage, TRELLO_API_BASE_URL } from '@/tools/trello/shared'
|
||||
import type { TrelloDeleteCardParams, TrelloDeleteCardResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloDeleteCardTool: ToolConfig<TrelloDeleteCardParams, TrelloDeleteCardResponse> = {
|
||||
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',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import {
|
||||
extractTrelloErrorMessage,
|
||||
mapTrelloAction,
|
||||
TRELLO_API_BASE_URL,
|
||||
} from '@/tools/trello/shared'
|
||||
import type { TrelloGetActionsParams, TrelloGetActionsResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloGetActionsTool: ToolConfig<TrelloGetActionsParams, TrelloGetActionsResponse> = {
|
||||
id: 'trello_get_actions',
|
||||
name: 'Trello Get Actions',
|
||||
description: 'Get activity/actions from a board or card',
|
||||
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: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Trello board ID (24-character hex string). Either boardId or cardId required',
|
||||
},
|
||||
cardId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Trello card ID (24-character hex string). Either boardId or cardId required',
|
||||
},
|
||||
filter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter actions by type (e.g., "commentCard,updateCard,createCard" or "all")',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of board actions to return',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for action results',
|
||||
},
|
||||
since: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Only return actions after this date (ISO 8601 timestamp) or action ID, for paging through long histories',
|
||||
},
|
||||
before: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Only return actions before this date (ISO 8601 timestamp) or action ID, for paging through long histories',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
if (!params.boardId && !params.cardId) {
|
||||
throw new Error('Either boardId or cardId is required')
|
||||
}
|
||||
if (params.boardId && params.cardId) {
|
||||
throw new Error('Provide either boardId or cardId, not both')
|
||||
}
|
||||
|
||||
const apiKey = env.TRELLO_API_KEY
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error('TRELLO_API_KEY environment variable is not set')
|
||||
}
|
||||
|
||||
const path = params.boardId
|
||||
? `/boards/${params.boardId.trim()}/actions`
|
||||
: `/cards/${params.cardId?.trim()}/actions`
|
||||
const url = new URL(`${TRELLO_API_BASE_URL}${path}`)
|
||||
url.searchParams.set('key', apiKey)
|
||||
url.searchParams.set('token', params.accessToken)
|
||||
|
||||
if (params.filter) {
|
||||
url.searchParams.set('filter', params.filter)
|
||||
}
|
||||
|
||||
if (params.limit !== undefined) {
|
||||
url.searchParams.set('limit', String(params.limit))
|
||||
}
|
||||
|
||||
if (params.page !== undefined) {
|
||||
url.searchParams.set('page', String(params.page))
|
||||
}
|
||||
|
||||
if (params.since) {
|
||||
url.searchParams.set('since', params.since)
|
||||
}
|
||||
|
||||
if (params.before) {
|
||||
url.searchParams.set('before', params.before)
|
||||
}
|
||||
|
||||
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 get Trello actions')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
actions: [],
|
||||
count: 0,
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.isArray(data)) {
|
||||
const error = 'Trello returned an invalid action collection'
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
actions: [],
|
||||
count: 0,
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const actions = data.map((item) => mapTrelloAction(item))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
actions,
|
||||
count: actions.length,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error, 'Failed to parse Trello actions')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
actions: [],
|
||||
count: 0,
|
||||
error: message,
|
||||
},
|
||||
error: message,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
actions: {
|
||||
type: 'array',
|
||||
description:
|
||||
'Action items (id, type, date, idMemberCreator, text, memberCreator, card, board, list)',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Action ID' },
|
||||
type: { type: 'string', description: 'Action type' },
|
||||
date: { type: 'string', description: 'Action timestamp' },
|
||||
idMemberCreator: {
|
||||
type: 'string',
|
||||
description: 'ID of the member who created the action',
|
||||
},
|
||||
text: {
|
||||
type: 'string',
|
||||
description: 'Comment text when present',
|
||||
optional: true,
|
||||
},
|
||||
memberCreator: {
|
||||
type: 'object',
|
||||
description: 'Member who created the action',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Member ID' },
|
||||
fullName: {
|
||||
type: 'string',
|
||||
description: 'Member full name',
|
||||
optional: true,
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
description: 'Member username',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
card: {
|
||||
type: 'object',
|
||||
description: 'Card referenced by the action',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Card ID' },
|
||||
name: { type: 'string', description: 'Card name' },
|
||||
shortLink: {
|
||||
type: 'string',
|
||||
description: 'Short card link',
|
||||
optional: true,
|
||||
},
|
||||
idShort: {
|
||||
type: 'number',
|
||||
description: 'Board-local card number',
|
||||
optional: true,
|
||||
},
|
||||
due: {
|
||||
type: 'string',
|
||||
description: 'Card due date',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
board: {
|
||||
type: 'object',
|
||||
description: 'Board referenced by the action',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Board ID' },
|
||||
name: { type: 'string', description: 'Board name' },
|
||||
shortLink: {
|
||||
type: 'string',
|
||||
description: 'Short board link',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
list: {
|
||||
type: 'object',
|
||||
description: 'List referenced by the action',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'List ID' },
|
||||
name: { type: 'string', description: 'List name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of actions returned' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import {
|
||||
extractTrelloErrorMessage,
|
||||
mapTrelloBoard,
|
||||
TRELLO_API_BASE_URL,
|
||||
} from '@/tools/trello/shared'
|
||||
import type { TrelloGetBoardParams, TrelloGetBoardResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloGetBoardTool: ToolConfig<TrelloGetBoardParams, TrelloGetBoardResponse> = {
|
||||
id: 'trello_get_board',
|
||||
name: 'Trello Get Board',
|
||||
description: 'Retrieve a single Trello board by ID',
|
||||
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()}`)
|
||||
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 get board')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const board = mapTrelloBoard(data)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
board,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error, 'Failed to parse board')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error: message,
|
||||
},
|
||||
error: message,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
board: {
|
||||
type: 'json',
|
||||
description: 'Board (id, name, desc, url, closed, idOrganization)',
|
||||
optional: true,
|
||||
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 closed' },
|
||||
idOrganization: {
|
||||
type: 'string',
|
||||
description: 'ID of the workspace/organization the board belongs to',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import {
|
||||
extractTrelloErrorMessage,
|
||||
mapTrelloCard,
|
||||
TRELLO_API_BASE_URL,
|
||||
} from '@/tools/trello/shared'
|
||||
import type { TrelloGetCardParams, TrelloGetCardResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloGetCardTool: ToolConfig<TrelloGetCardParams, TrelloGetCardResponse> = {
|
||||
id: 'trello_get_card',
|
||||
name: 'Trello Get Card',
|
||||
description: 'Retrieve a single Trello card by ID',
|
||||
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 (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: '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 get card')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const card = mapTrelloCard(data)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
card,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error, 'Failed to parse card')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error: message,
|
||||
},
|
||||
error: message,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
card: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Card (id, name, desc, url, idBoard, idList, closed, labelIds, labels, due, dueComplete)',
|
||||
optional: true,
|
||||
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' },
|
||||
labelIds: {
|
||||
type: 'array',
|
||||
description: 'Label IDs applied to the card',
|
||||
items: {
|
||||
type: 'string',
|
||||
description: 'A Trello label ID',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
description: 'Labels applied to the card',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Label ID' },
|
||||
name: { type: 'string', description: 'Label name' },
|
||||
color: {
|
||||
type: 'string',
|
||||
description: 'Label color',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
due: {
|
||||
type: 'string',
|
||||
description: 'Card due date in ISO 8601 format',
|
||||
optional: true,
|
||||
},
|
||||
dueComplete: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the due date is complete',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { trelloAddChecklistTool } from '@/tools/trello/add_checklist'
|
||||
import { trelloAddChecklistItemTool } from '@/tools/trello/add_checklist_item'
|
||||
import { trelloAddCommentTool } from '@/tools/trello/add_comment'
|
||||
import { trelloAddLabelTool } from '@/tools/trello/add_label'
|
||||
import { trelloAddMemberTool } from '@/tools/trello/add_member'
|
||||
import { trelloCreateBoardTool } from '@/tools/trello/create_board'
|
||||
import { trelloCreateCardTool } from '@/tools/trello/create_card'
|
||||
import { trelloCreateListTool } from '@/tools/trello/create_list'
|
||||
import { trelloDeleteCardTool } from '@/tools/trello/delete_card'
|
||||
import { trelloGetActionsTool } from '@/tools/trello/get_actions'
|
||||
import { trelloGetBoardTool } from '@/tools/trello/get_board'
|
||||
import { trelloGetCardTool } from '@/tools/trello/get_card'
|
||||
import { trelloListCardsTool } from '@/tools/trello/list_cards'
|
||||
import { trelloListListsTool } from '@/tools/trello/list_lists'
|
||||
import { trelloListMembersTool } from '@/tools/trello/list_members'
|
||||
import { trelloRemoveLabelTool } from '@/tools/trello/remove_label'
|
||||
import { trelloRemoveMemberTool } from '@/tools/trello/remove_member'
|
||||
import { trelloSearchTool } from '@/tools/trello/search'
|
||||
import { trelloUpdateCardTool } from '@/tools/trello/update_card'
|
||||
import { trelloUpdateChecklistItemTool } from '@/tools/trello/update_checklist_item'
|
||||
import { trelloUpdateListTool } from '@/tools/trello/update_list'
|
||||
|
||||
export {
|
||||
trelloListListsTool,
|
||||
trelloListCardsTool,
|
||||
trelloCreateCardTool,
|
||||
trelloUpdateCardTool,
|
||||
trelloDeleteCardTool,
|
||||
trelloGetActionsTool,
|
||||
trelloAddCommentTool,
|
||||
trelloCreateBoardTool,
|
||||
trelloGetBoardTool,
|
||||
trelloCreateListTool,
|
||||
trelloUpdateListTool,
|
||||
trelloGetCardTool,
|
||||
trelloAddChecklistTool,
|
||||
trelloAddChecklistItemTool,
|
||||
trelloUpdateChecklistItemTool,
|
||||
trelloAddLabelTool,
|
||||
trelloRemoveLabelTool,
|
||||
trelloAddMemberTool,
|
||||
trelloRemoveMemberTool,
|
||||
trelloListMembersTool,
|
||||
trelloSearchTool,
|
||||
}
|
||||
|
||||
export * from '@/tools/trello/types'
|
||||
@@ -0,0 +1,192 @@
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import {
|
||||
extractTrelloErrorMessage,
|
||||
mapTrelloCard,
|
||||
TRELLO_API_BASE_URL,
|
||||
} from '@/tools/trello/shared'
|
||||
import type { TrelloListCardsParams, TrelloListCardsResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloListCardsTool: ToolConfig<TrelloListCardsParams, TrelloListCardsResponse> = {
|
||||
id: 'trello_list_cards',
|
||||
name: 'Trello List Cards',
|
||||
description: 'List cards from a Trello board or list',
|
||||
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: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Trello board ID to list open cards from. Provide either boardId or listId',
|
||||
},
|
||||
listId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Trello list ID to list cards from. Provide either boardId or listId',
|
||||
},
|
||||
filter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Which cards to return: open, closed, or all (defaults to open)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const apiKey = env.TRELLO_API_KEY
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error('TRELLO_API_KEY environment variable is not set')
|
||||
}
|
||||
|
||||
if (params.boardId && params.listId) {
|
||||
throw new Error('Provide either a board ID or list ID, not both')
|
||||
}
|
||||
|
||||
if (!params.listId && !params.boardId) {
|
||||
throw new Error('Either a board ID or list ID is required')
|
||||
}
|
||||
|
||||
const path = params.listId
|
||||
? `/lists/${params.listId.trim()}/cards`
|
||||
: `/boards/${params.boardId?.trim()}/cards`
|
||||
const url = new URL(`${TRELLO_API_BASE_URL}${path}`)
|
||||
url.searchParams.set('key', apiKey)
|
||||
url.searchParams.set('token', params.accessToken)
|
||||
|
||||
if (params.filter) {
|
||||
url.searchParams.set('filter', params.filter)
|
||||
}
|
||||
|
||||
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 Trello cards')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
cards: [],
|
||||
count: 0,
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.isArray(data)) {
|
||||
const error = 'Trello returned an invalid card collection'
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
cards: [],
|
||||
count: 0,
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const cards = data.map((item) => mapTrelloCard(item))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
cards,
|
||||
count: cards.length,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error, 'Failed to parse Trello cards')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
cards: [],
|
||||
count: 0,
|
||||
error: message,
|
||||
},
|
||||
error: message,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
cards: {
|
||||
type: 'array',
|
||||
description: 'Cards returned from the selected Trello board or list',
|
||||
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' },
|
||||
labelIds: {
|
||||
type: 'array',
|
||||
description: 'Label IDs applied to the card',
|
||||
items: {
|
||||
type: 'string',
|
||||
description: 'A Trello label ID',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
description: 'Labels applied to the card',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Label ID' },
|
||||
name: { type: 'string', description: 'Label name' },
|
||||
color: {
|
||||
type: 'string',
|
||||
description: 'Label color',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
due: {
|
||||
type: 'string',
|
||||
description: 'Card due date in ISO 8601 format',
|
||||
optional: true,
|
||||
},
|
||||
dueComplete: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the due date is complete',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of cards returned' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
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 { TrelloListListsParams, TrelloListListsResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloListListsTool: ToolConfig<TrelloListListsParams, TrelloListListsResponse> = {
|
||||
id: 'trello_list_lists',
|
||||
name: 'Trello Get Lists',
|
||||
description: 'List all lists on 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)',
|
||||
},
|
||||
filter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Which lists to return: open, closed, or all (defaults to open)',
|
||||
},
|
||||
},
|
||||
|
||||
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')
|
||||
}
|
||||
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Trello access token is missing')
|
||||
}
|
||||
|
||||
const url = new URL(`${TRELLO_API_BASE_URL}/boards/${params.boardId.trim()}/lists`)
|
||||
url.searchParams.set('key', apiKey)
|
||||
url.searchParams.set('token', params.accessToken)
|
||||
|
||||
if (params.filter) {
|
||||
url.searchParams.set('filter', params.filter)
|
||||
}
|
||||
|
||||
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 Trello lists')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
lists: [],
|
||||
count: 0,
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.isArray(data)) {
|
||||
const error = 'Trello returned an invalid list collection'
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
lists: [],
|
||||
count: 0,
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const lists = data.map((item) => mapTrelloList(item))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
lists,
|
||||
count: lists.length,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error, 'Failed to parse Trello lists')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
lists: [],
|
||||
count: 0,
|
||||
error: message,
|
||||
},
|
||||
error: message,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
lists: {
|
||||
type: 'array',
|
||||
description: 'Lists on the selected board',
|
||||
items: {
|
||||
type: 'object',
|
||||
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' },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of lists returned' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import {
|
||||
extractTrelloErrorMessage,
|
||||
mapTrelloMember,
|
||||
TRELLO_API_BASE_URL,
|
||||
} from '@/tools/trello/shared'
|
||||
import type { TrelloListMembersParams, TrelloListMembersResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloListMembersTool: ToolConfig<TrelloListMembersParams, TrelloListMembersResponse> =
|
||||
{
|
||||
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<typeof member> => 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' },
|
||||
},
|
||||
}
|
||||
@@ -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<TrelloRemoveLabelParams, TrelloRemoveLabelResponse> =
|
||||
{
|
||||
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',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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<TrelloSearchParams, TrelloSearchResponse> = {
|
||||
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' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
import { isRecordLike } from '@sim/utils/object'
|
||||
import type {
|
||||
TrelloAction,
|
||||
TrelloActionBoardTarget,
|
||||
TrelloActionCardTarget,
|
||||
TrelloActionListTarget,
|
||||
TrelloBoard,
|
||||
TrelloCard,
|
||||
TrelloChecklist,
|
||||
TrelloChecklistItem,
|
||||
TrelloComment,
|
||||
TrelloLabel,
|
||||
TrelloList,
|
||||
TrelloMember,
|
||||
} from '@/tools/trello/types'
|
||||
|
||||
export const TRELLO_API_BASE_URL = 'https://api.trello.com/1'
|
||||
|
||||
function getRequiredString(value: unknown, field: string): string {
|
||||
if (typeof value === 'string' && value.trim().length > 0) {
|
||||
return value
|
||||
}
|
||||
|
||||
throw new Error(`Trello response is missing required field: ${field}`)
|
||||
}
|
||||
|
||||
function getOptionalString(value: unknown): string | null {
|
||||
return typeof value === 'string' ? value : null
|
||||
}
|
||||
|
||||
function getOptionalBoolean(value: unknown): boolean | null {
|
||||
return typeof value === 'boolean' ? value : null
|
||||
}
|
||||
|
||||
function getNumber(value: unknown): number {
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return value
|
||||
}
|
||||
|
||||
if (typeof value === 'string' && value.trim().length > 0) {
|
||||
const parsed = Number(value)
|
||||
if (Number.isFinite(parsed)) {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function getOptionalNumber(value: unknown): number | null {
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return value
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function getIdArray(value: unknown): string[] {
|
||||
if (!Array.isArray(value)) {
|
||||
return []
|
||||
}
|
||||
|
||||
return value.flatMap((item) => {
|
||||
if (typeof item === 'string' && item.trim().length > 0) {
|
||||
return [item]
|
||||
}
|
||||
|
||||
if (isRecordLike(item) && typeof item.id === 'string' && item.id.trim().length > 0) {
|
||||
return [item.id]
|
||||
}
|
||||
|
||||
return []
|
||||
})
|
||||
}
|
||||
|
||||
function mapTrelloLabel(value: unknown): TrelloLabel | null {
|
||||
if (!isRecordLike(value) || typeof value.id !== 'string') {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
id: value.id,
|
||||
name: typeof value.name === 'string' ? value.name : '',
|
||||
color: getOptionalString(value.color),
|
||||
}
|
||||
}
|
||||
|
||||
export function mapTrelloMember(value: unknown): TrelloMember | null {
|
||||
if (!isRecordLike(value) || typeof value.id !== 'string') {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
id: value.id,
|
||||
fullName: getOptionalString(value.fullName),
|
||||
username: getOptionalString(value.username),
|
||||
}
|
||||
}
|
||||
|
||||
function mapActionCardTarget(value: unknown): TrelloActionCardTarget | null {
|
||||
if (!isRecordLike(value) || typeof value.id !== 'string' || typeof value.name !== 'string') {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
id: value.id,
|
||||
name: value.name,
|
||||
shortLink: getOptionalString(value.shortLink),
|
||||
idShort: getOptionalNumber(value.idShort),
|
||||
due: getOptionalString(value.due),
|
||||
}
|
||||
}
|
||||
|
||||
function mapActionBoardTarget(value: unknown): TrelloActionBoardTarget | null {
|
||||
if (!isRecordLike(value) || typeof value.id !== 'string' || typeof value.name !== 'string') {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
id: value.id,
|
||||
name: value.name,
|
||||
shortLink: getOptionalString(value.shortLink),
|
||||
}
|
||||
}
|
||||
|
||||
function mapActionListTarget(value: unknown): TrelloActionListTarget | null {
|
||||
if (!isRecordLike(value) || typeof value.id !== 'string' || typeof value.name !== 'string') {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
id: value.id,
|
||||
name: value.name,
|
||||
}
|
||||
}
|
||||
|
||||
export function mapTrelloList(value: unknown): TrelloList {
|
||||
if (!isRecordLike(value)) {
|
||||
throw new Error('Trello returned an invalid list object')
|
||||
}
|
||||
|
||||
return {
|
||||
id: getRequiredString(value.id, 'id'),
|
||||
name: getRequiredString(value.name, 'name'),
|
||||
closed: typeof value.closed === 'boolean' ? value.closed : false,
|
||||
pos: getNumber(value.pos),
|
||||
idBoard: getRequiredString(value.idBoard, 'idBoard'),
|
||||
}
|
||||
}
|
||||
|
||||
export function mapTrelloCard(value: unknown): TrelloCard {
|
||||
if (!isRecordLike(value)) {
|
||||
throw new Error('Trello returned an invalid card object')
|
||||
}
|
||||
|
||||
const rawLabels = Array.isArray(value.labels) ? value.labels : []
|
||||
const labels = rawLabels
|
||||
.map((label) => mapTrelloLabel(label))
|
||||
.filter((label): label is TrelloLabel => label !== null)
|
||||
const labelIds = getIdArray(value.idLabels)
|
||||
|
||||
if (labelIds.length === 0) {
|
||||
labelIds.push(...rawLabels.filter((label): label is string => typeof label === 'string'))
|
||||
}
|
||||
|
||||
return {
|
||||
id: getRequiredString(value.id, 'id'),
|
||||
name: getRequiredString(value.name, 'name'),
|
||||
desc: typeof value.desc === 'string' ? value.desc : '',
|
||||
url: getRequiredString(value.url, 'url'),
|
||||
idBoard: getRequiredString(value.idBoard, 'idBoard'),
|
||||
idList: getRequiredString(value.idList, 'idList'),
|
||||
closed: typeof value.closed === 'boolean' ? value.closed : false,
|
||||
labelIds,
|
||||
labels,
|
||||
due: getOptionalString(value.due),
|
||||
dueComplete: getOptionalBoolean(value.dueComplete),
|
||||
}
|
||||
}
|
||||
|
||||
export function mapTrelloBoard(value: unknown): TrelloBoard {
|
||||
if (!isRecordLike(value)) {
|
||||
throw new Error('Trello returned an invalid board object')
|
||||
}
|
||||
|
||||
return {
|
||||
id: getRequiredString(value.id, 'id'),
|
||||
name: getRequiredString(value.name, 'name'),
|
||||
desc: typeof value.desc === 'string' ? value.desc : '',
|
||||
url: getRequiredString(value.url, 'url'),
|
||||
closed: typeof value.closed === 'boolean' ? value.closed : false,
|
||||
idOrganization: getOptionalString(value.idOrganization),
|
||||
}
|
||||
}
|
||||
|
||||
export function mapTrelloChecklist(value: unknown): TrelloChecklist {
|
||||
if (!isRecordLike(value)) {
|
||||
throw new Error('Trello returned an invalid checklist object')
|
||||
}
|
||||
|
||||
return {
|
||||
id: getRequiredString(value.id, 'id'),
|
||||
name: getRequiredString(value.name, 'name'),
|
||||
idCard: getRequiredString(value.idCard, 'idCard'),
|
||||
idBoard: getOptionalString(value.idBoard),
|
||||
pos: getNumber(value.pos),
|
||||
}
|
||||
}
|
||||
|
||||
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')
|
||||
}
|
||||
|
||||
const data = isRecordLike(value.data) ? value.data : null
|
||||
|
||||
return {
|
||||
id: getRequiredString(value.id, 'id'),
|
||||
type: getRequiredString(value.type, 'type'),
|
||||
date: getRequiredString(value.date, 'date'),
|
||||
idMemberCreator: getRequiredString(value.idMemberCreator, 'idMemberCreator'),
|
||||
text: data ? getOptionalString(data.text) : null,
|
||||
memberCreator: mapTrelloMember(value.memberCreator),
|
||||
card: data ? mapActionCardTarget(data.card) : null,
|
||||
board: data ? mapActionBoardTarget(data.board) : null,
|
||||
list: data ? mapActionListTarget(data.list) : null,
|
||||
}
|
||||
}
|
||||
|
||||
export function mapTrelloComment(value: unknown): TrelloComment {
|
||||
return mapTrelloAction(value)
|
||||
}
|
||||
|
||||
export function extractTrelloErrorMessage(
|
||||
response: Response,
|
||||
data: unknown,
|
||||
fallback: string
|
||||
): string {
|
||||
const parts: string[] = []
|
||||
|
||||
if (isRecordLike(data)) {
|
||||
const message = data.message
|
||||
const error = data.error
|
||||
|
||||
if (typeof message === 'string' && message.trim().length > 0) {
|
||||
parts.push(message)
|
||||
}
|
||||
|
||||
if (typeof error === 'string' && error.trim().length > 0 && error !== message) {
|
||||
parts.push(error)
|
||||
}
|
||||
}
|
||||
|
||||
if (parts.length > 0) {
|
||||
return `${fallback}: ${parts.join(' - ')}`
|
||||
}
|
||||
|
||||
if (response.statusText) {
|
||||
return `${fallback}: ${response.status} ${response.statusText}`
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface TrelloBoard {
|
||||
id: string
|
||||
name: string
|
||||
desc: string
|
||||
url: string
|
||||
closed: boolean
|
||||
idOrganization: string | null
|
||||
}
|
||||
|
||||
export interface TrelloChecklist {
|
||||
id: string
|
||||
name: string
|
||||
idCard: string
|
||||
idBoard: string | null
|
||||
pos: number
|
||||
}
|
||||
|
||||
export interface TrelloLabel {
|
||||
id: string
|
||||
name: string
|
||||
color: string | null
|
||||
}
|
||||
|
||||
export interface TrelloMember {
|
||||
id: string
|
||||
fullName: string | null
|
||||
username: string | null
|
||||
}
|
||||
|
||||
export interface TrelloList {
|
||||
id: string
|
||||
name: string
|
||||
closed: boolean
|
||||
pos: number
|
||||
idBoard: string
|
||||
}
|
||||
|
||||
export interface TrelloCard {
|
||||
id: string
|
||||
name: string
|
||||
desc: string
|
||||
url: string
|
||||
idBoard: string
|
||||
idList: string
|
||||
closed: boolean
|
||||
labelIds: string[]
|
||||
labels: TrelloLabel[]
|
||||
due: string | null
|
||||
dueComplete: boolean | null
|
||||
}
|
||||
|
||||
export interface TrelloActionCardTarget {
|
||||
id: string
|
||||
name: string
|
||||
shortLink: string | null
|
||||
idShort: number | null
|
||||
due: string | null
|
||||
}
|
||||
|
||||
export interface TrelloActionBoardTarget {
|
||||
id: string
|
||||
name: string
|
||||
shortLink: string | null
|
||||
}
|
||||
|
||||
export interface TrelloActionListTarget {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface TrelloAction {
|
||||
id: string
|
||||
type: string
|
||||
date: string
|
||||
idMemberCreator: string
|
||||
text: string | null
|
||||
memberCreator: TrelloMember | null
|
||||
card: TrelloActionCardTarget | null
|
||||
board: TrelloActionBoardTarget | null
|
||||
list: TrelloActionListTarget | null
|
||||
}
|
||||
|
||||
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 {
|
||||
accessToken: string
|
||||
listId: string
|
||||
name: string
|
||||
desc?: string
|
||||
pos?: string
|
||||
due?: string
|
||||
dueComplete?: boolean
|
||||
labelIds?: string[]
|
||||
memberIds?: string[]
|
||||
}
|
||||
|
||||
export interface TrelloUpdateCardParams {
|
||||
accessToken: string
|
||||
cardId: string
|
||||
name?: string
|
||||
desc?: string
|
||||
closed?: boolean
|
||||
idList?: string
|
||||
due?: string
|
||||
dueComplete?: boolean
|
||||
}
|
||||
|
||||
export interface TrelloDeleteCardParams {
|
||||
accessToken: string
|
||||
cardId: string
|
||||
}
|
||||
|
||||
export interface TrelloGetActionsParams {
|
||||
accessToken: string
|
||||
boardId?: string
|
||||
cardId?: string
|
||||
filter?: string
|
||||
limit?: number
|
||||
page?: number
|
||||
since?: string
|
||||
before?: string
|
||||
}
|
||||
|
||||
export interface TrelloAddCommentParams {
|
||||
accessToken: string
|
||||
cardId: string
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface TrelloCreateBoardParams {
|
||||
accessToken: string
|
||||
name: string
|
||||
desc?: string
|
||||
idOrganization?: string
|
||||
defaultLists?: boolean
|
||||
}
|
||||
|
||||
export interface TrelloGetBoardParams {
|
||||
accessToken: string
|
||||
boardId: string
|
||||
}
|
||||
|
||||
export interface TrelloCreateListParams {
|
||||
accessToken: string
|
||||
boardId: string
|
||||
name: string
|
||||
pos?: string
|
||||
}
|
||||
|
||||
export interface TrelloGetCardParams {
|
||||
accessToken: string
|
||||
cardId: string
|
||||
}
|
||||
|
||||
export interface TrelloAddChecklistParams {
|
||||
accessToken: string
|
||||
cardId: string
|
||||
name: string
|
||||
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[]
|
||||
count: number
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TrelloListCardsResponse extends ToolResponse {
|
||||
output: {
|
||||
cards: TrelloCard[]
|
||||
count: number
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TrelloCreateCardResponse extends ToolResponse {
|
||||
output: {
|
||||
card?: TrelloCard
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TrelloUpdateCardResponse extends ToolResponse {
|
||||
output: {
|
||||
card?: TrelloCard
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TrelloGetActionsResponse extends ToolResponse {
|
||||
output: {
|
||||
actions: TrelloAction[]
|
||||
count: number
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TrelloAddCommentResponse extends ToolResponse {
|
||||
output: {
|
||||
comment?: TrelloComment
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TrelloCreateBoardResponse extends ToolResponse {
|
||||
output: {
|
||||
board?: TrelloBoard
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TrelloGetBoardResponse extends ToolResponse {
|
||||
output: {
|
||||
board?: TrelloBoard
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TrelloCreateListResponse extends ToolResponse {
|
||||
output: {
|
||||
list?: TrelloList
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TrelloGetCardResponse extends ToolResponse {
|
||||
output: {
|
||||
card?: TrelloCard
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TrelloAddChecklistResponse extends ToolResponse {
|
||||
output: {
|
||||
checklist?: TrelloChecklist
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
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[]
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TrelloRemoveLabelResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface TrelloAddMemberResponse extends ToolResponse {
|
||||
output: {
|
||||
memberIds: string[]
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -0,0 +1,201 @@
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import {
|
||||
extractTrelloErrorMessage,
|
||||
mapTrelloCard,
|
||||
TRELLO_API_BASE_URL,
|
||||
} from '@/tools/trello/shared'
|
||||
import type { TrelloUpdateCardParams, TrelloUpdateCardResponse } from '@/tools/trello/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const trelloUpdateCardTool: ToolConfig<TrelloUpdateCardParams, TrelloUpdateCardResponse> = {
|
||||
id: 'trello_update_card',
|
||||
name: 'Trello Update Card',
|
||||
description: 'Update an existing card on Trello',
|
||||
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 (24-character hex string)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name/title of the card',
|
||||
},
|
||||
desc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New description of the card',
|
||||
},
|
||||
closed: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Archive/close the card (true) or reopen it (false)',
|
||||
},
|
||||
idList: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Trello list ID to move card to (24-character hex string)',
|
||||
},
|
||||
due: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Due date (ISO 8601 format)',
|
||||
},
|
||||
dueComplete: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Mark the due date as complete',
|
||||
},
|
||||
},
|
||||
|
||||
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: 'PUT',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
if (params.name !== undefined) body.name = params.name
|
||||
if (params.desc !== undefined) body.desc = params.desc
|
||||
if (params.closed !== undefined) body.closed = params.closed
|
||||
if (params.idList !== undefined) body.idList = params.idList.trim()
|
||||
if (params.due !== undefined) body.due = params.due
|
||||
if (params.dueComplete !== undefined) body.dueComplete = params.dueComplete
|
||||
|
||||
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 card')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error,
|
||||
},
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const card = mapTrelloCard(data)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
card,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
const message = getErrorMessage(error, 'Failed to parse updated card')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
error: message,
|
||||
},
|
||||
error: message,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
card: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Updated card (id, name, desc, url, idBoard, idList, closed, labelIds, labels, due, dueComplete)',
|
||||
optional: true,
|
||||
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' },
|
||||
labelIds: {
|
||||
type: 'array',
|
||||
description: 'Label IDs applied to the card',
|
||||
items: {
|
||||
type: 'string',
|
||||
description: 'A Trello label ID',
|
||||
},
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
description: 'Labels applied to the card',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Label ID' },
|
||||
name: { type: 'string', description: 'Label name' },
|
||||
color: {
|
||||
type: 'string',
|
||||
description: 'Label color',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
due: {
|
||||
type: 'string',
|
||||
description: 'Card due date in ISO 8601 format',
|
||||
optional: true,
|
||||
},
|
||||
dueComplete: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the due date is complete',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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<TrelloUpdateListParams, TrelloUpdateListResponse> = {
|
||||
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<string, unknown> = {}
|
||||
|
||||
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' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user