mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-07 22:24:06 -05:00
fix(tools): improved slack output ux and jira params (#2462)
* fixed slack output * updated jira * removed comment * change team uuid
This commit is contained in:
@@ -97,10 +97,16 @@ Write a Jira issue
|
||||
| `projectId` | string | Yes | Project ID for the issue |
|
||||
| `summary` | string | Yes | Summary for the issue |
|
||||
| `description` | string | No | Description for the issue |
|
||||
| `priority` | string | No | Priority for the issue |
|
||||
| `assignee` | string | No | Assignee for the issue |
|
||||
| `priority` | string | No | Priority ID or name for the issue \(e.g., "10000" or "High"\) |
|
||||
| `assignee` | string | No | Assignee account ID for the issue |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
| `issueType` | string | Yes | Type of issue to create \(e.g., Task, Story\) |
|
||||
| `labels` | array | No | Labels for the issue \(array of label names\) |
|
||||
| `duedate` | string | No | Due date for the issue \(format: YYYY-MM-DD\) |
|
||||
| `reporter` | string | No | Reporter account ID for the issue |
|
||||
| `environment` | string | No | Environment information for the issue |
|
||||
| `customFieldId` | string | No | Custom field ID \(e.g., customfield_10001\) |
|
||||
| `customFieldValue` | string | No | Value for the custom field |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -110,6 +116,7 @@ Write a Jira issue
|
||||
| `issueKey` | string | Created issue key \(e.g., PROJ-123\) |
|
||||
| `summary` | string | Issue summary |
|
||||
| `url` | string | URL to the created issue |
|
||||
| `assigneeId` | string | Account ID of the assigned user \(if assigned\) |
|
||||
|
||||
### `jira_bulk_read`
|
||||
|
||||
@@ -523,6 +530,30 @@ Remove a watcher from a Jira issue
|
||||
| `issueKey` | string | Issue key |
|
||||
| `watcherAccountId` | string | Removed watcher account ID |
|
||||
|
||||
### `jira_get_users`
|
||||
|
||||
Get Jira users. If an account ID is provided, returns a single user. Otherwise, returns a list of all users.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `accountId` | string | No | Optional account ID to get a specific user. If not provided, returns all users. |
|
||||
| `startAt` | number | No | The index of the first user to return \(for pagination, default: 0\) |
|
||||
| `maxResults` | number | No | Maximum number of users to return \(default: 50\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `users` | json | Array of users with accountId, displayName, emailAddress, active status, and avatarUrls |
|
||||
| `total` | number | Total number of users returned |
|
||||
| `startAt` | number | Pagination start index |
|
||||
| `maxResults` | number | Maximum results per page |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -20,6 +20,12 @@ export async function POST(request: Request) {
|
||||
cloudId: providedCloudId,
|
||||
issueType,
|
||||
parent,
|
||||
labels,
|
||||
duedate,
|
||||
reporter,
|
||||
environment,
|
||||
customFieldId,
|
||||
customFieldValue,
|
||||
} = await request.json()
|
||||
|
||||
if (!domain) {
|
||||
@@ -94,17 +100,57 @@ export async function POST(request: Request) {
|
||||
}
|
||||
|
||||
if (priority !== undefined && priority !== null && priority !== '') {
|
||||
fields.priority = {
|
||||
name: priority,
|
||||
const isNumericId = /^\d+$/.test(priority)
|
||||
fields.priority = isNumericId ? { id: priority } : { name: priority }
|
||||
}
|
||||
|
||||
if (labels !== undefined && labels !== null && Array.isArray(labels) && labels.length > 0) {
|
||||
fields.labels = labels
|
||||
}
|
||||
|
||||
if (duedate !== undefined && duedate !== null && duedate !== '') {
|
||||
fields.duedate = duedate
|
||||
}
|
||||
|
||||
if (reporter !== undefined && reporter !== null && reporter !== '') {
|
||||
fields.reporter = {
|
||||
id: reporter,
|
||||
}
|
||||
}
|
||||
|
||||
if (assignee !== undefined && assignee !== null && assignee !== '') {
|
||||
fields.assignee = {
|
||||
id: assignee,
|
||||
if (environment !== undefined && environment !== null && environment !== '') {
|
||||
fields.environment = {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: environment,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
customFieldId !== undefined &&
|
||||
customFieldId !== null &&
|
||||
customFieldId !== '' &&
|
||||
customFieldValue !== undefined &&
|
||||
customFieldValue !== null &&
|
||||
customFieldValue !== ''
|
||||
) {
|
||||
const fieldId = customFieldId.startsWith('customfield_')
|
||||
? customFieldId
|
||||
: `customfield_${customFieldId}`
|
||||
|
||||
fields[fieldId] = customFieldValue
|
||||
}
|
||||
|
||||
const body = { fields }
|
||||
|
||||
const response = await fetch(url, {
|
||||
@@ -132,16 +178,47 @@ export async function POST(request: Request) {
|
||||
}
|
||||
|
||||
const responseData = await response.json()
|
||||
logger.info('Successfully created Jira issue:', responseData.key)
|
||||
const issueKey = responseData.key || 'unknown'
|
||||
logger.info('Successfully created Jira issue:', issueKey)
|
||||
|
||||
let assigneeId: string | undefined
|
||||
if (assignee !== undefined && assignee !== null && assignee !== '') {
|
||||
const assignUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${issueKey}/assignee`
|
||||
logger.info('Assigning issue to:', assignee)
|
||||
|
||||
const assignResponse = await fetch(assignUrl, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
accountId: assignee,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!assignResponse.ok) {
|
||||
const assignErrorText = await assignResponse.text()
|
||||
logger.warn('Failed to assign issue (issue was created successfully):', {
|
||||
status: assignResponse.status,
|
||||
error: assignErrorText,
|
||||
})
|
||||
} else {
|
||||
assigneeId = assignee
|
||||
logger.info('Successfully assigned issue to:', assignee)
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: responseData.key || 'unknown',
|
||||
issueKey: issueKey,
|
||||
summary: responseData.fields?.summary || 'Issue created',
|
||||
success: true,
|
||||
url: `https://${domain}/browse/${responseData.key}`,
|
||||
url: `https://${domain}/browse/${issueKey}`,
|
||||
...(assigneeId && { assigneeId }),
|
||||
},
|
||||
})
|
||||
} catch (error: any) {
|
||||
|
||||
@@ -1214,31 +1214,25 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
|
||||
let processedTag = tag
|
||||
|
||||
// Check if this is a file property and add [0] automatically
|
||||
// Only include user-accessible fields (matches UserFile interface)
|
||||
const fileProperties = ['id', 'name', 'url', 'size', 'type']
|
||||
const parts = tag.split('.')
|
||||
if (parts.length >= 2 && fileProperties.includes(parts[parts.length - 1])) {
|
||||
const fieldName = parts[parts.length - 2]
|
||||
if (parts.length >= 3 && blockGroup) {
|
||||
const arrayFieldName = parts[1] // e.g., "channels", "files", "users"
|
||||
const block = useWorkflowStore.getState().blocks[blockGroup.blockId]
|
||||
const blockConfig = block ? (getBlock(block.type) ?? null) : null
|
||||
const mergedSubBlocks = getMergedSubBlocks(blockGroup.blockId)
|
||||
|
||||
if (blockGroup) {
|
||||
const block = useWorkflowStore.getState().blocks[blockGroup.blockId]
|
||||
const blockConfig = block ? (getBlock(block.type) ?? null) : null
|
||||
const mergedSubBlocks = getMergedSubBlocks(blockGroup.blockId)
|
||||
const fieldType = getOutputTypeForPath(
|
||||
block,
|
||||
blockConfig,
|
||||
blockGroup.blockId,
|
||||
arrayFieldName,
|
||||
mergedSubBlocks
|
||||
)
|
||||
|
||||
const fieldType = getOutputTypeForPath(
|
||||
block,
|
||||
blockConfig,
|
||||
blockGroup.blockId,
|
||||
fieldName,
|
||||
mergedSubBlocks
|
||||
)
|
||||
|
||||
if (fieldType === 'files') {
|
||||
const blockAndField = parts.slice(0, -1).join('.')
|
||||
const property = parts[parts.length - 1]
|
||||
processedTag = `${blockAndField}[0].${property}`
|
||||
}
|
||||
if (fieldType === 'files' || fieldType === 'array') {
|
||||
const blockName = parts[0]
|
||||
const remainingPath = parts.slice(2).join('.')
|
||||
processedTag = `${blockName}.${arrayFieldName}[0].${remainingPath}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
{ label: 'Delete Issue Link', id: 'delete_link' },
|
||||
{ label: 'Add Watcher', id: 'add_watcher' },
|
||||
{ label: 'Remove Watcher', id: 'remove_watcher' },
|
||||
{ label: 'Get Users', id: 'get_users' },
|
||||
],
|
||||
value: () => 'read',
|
||||
},
|
||||
@@ -194,6 +195,71 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: ['update', 'write'] },
|
||||
},
|
||||
// Write Issue additional fields
|
||||
{
|
||||
id: 'assignee',
|
||||
title: 'Assignee Account ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Assignee account ID (e.g., 5b109f2e9729b51b54dc274d)',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
},
|
||||
{
|
||||
id: 'priority',
|
||||
title: 'Priority',
|
||||
type: 'short-input',
|
||||
placeholder: 'Priority ID or name (e.g., "10000" or "High")',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
},
|
||||
{
|
||||
id: 'labels',
|
||||
title: 'Labels',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated labels (e.g., bug, urgent)',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
},
|
||||
{
|
||||
id: 'duedate',
|
||||
title: 'Due Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD (e.g., 2024-12-31)',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
},
|
||||
{
|
||||
id: 'reporter',
|
||||
title: 'Reporter Account ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Reporter account ID',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
},
|
||||
{
|
||||
id: 'environment',
|
||||
title: 'Environment',
|
||||
type: 'long-input',
|
||||
placeholder: 'Environment information (e.g., Production, Staging)',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
},
|
||||
{
|
||||
id: 'customFieldId',
|
||||
title: 'Custom Field ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., customfield_10001 or 10001',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
},
|
||||
{
|
||||
id: 'teamUuid',
|
||||
title: 'Team UUID',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., b3aa307a-76ea-462d-b6f1-a6e89ce9858a',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
},
|
||||
// Delete Issue fields
|
||||
{
|
||||
id: 'deleteSubtasks',
|
||||
@@ -351,6 +417,28 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
placeholder: 'Enter link ID to delete',
|
||||
condition: { field: 'operation', value: 'delete_link' },
|
||||
},
|
||||
// Get Users fields
|
||||
{
|
||||
id: 'userAccountId',
|
||||
title: 'Account ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter account ID for specific user',
|
||||
condition: { field: 'operation', value: 'get_users' },
|
||||
},
|
||||
{
|
||||
id: 'usersStartAt',
|
||||
title: 'Start At',
|
||||
type: 'short-input',
|
||||
placeholder: 'Pagination start index (default: 0)',
|
||||
condition: { field: 'operation', value: 'get_users' },
|
||||
},
|
||||
{
|
||||
id: 'usersMaxResults',
|
||||
title: 'Max Results',
|
||||
type: 'short-input',
|
||||
placeholder: 'Maximum users to return (default: 50)',
|
||||
condition: { field: 'operation', value: 'get_users' },
|
||||
},
|
||||
// Trigger SubBlocks
|
||||
...getTrigger('jira_issue_created').subBlocks,
|
||||
...getTrigger('jira_issue_updated').subBlocks,
|
||||
@@ -383,6 +471,7 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
'jira_delete_issue_link',
|
||||
'jira_add_watcher',
|
||||
'jira_remove_watcher',
|
||||
'jira_get_users',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
@@ -438,6 +527,8 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
return 'jira_add_watcher'
|
||||
case 'remove_watcher':
|
||||
return 'jira_remove_watcher'
|
||||
case 'get_users':
|
||||
return 'jira_get_users'
|
||||
default:
|
||||
return 'jira_retrieve'
|
||||
}
|
||||
@@ -461,12 +552,29 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
'Project ID is required. Please select a project or enter a project ID manually.'
|
||||
)
|
||||
}
|
||||
// Parse comma-separated strings into arrays
|
||||
const parseCommaSeparated = (value: string | undefined): string[] | undefined => {
|
||||
if (!value || value.trim() === '') return undefined
|
||||
return value
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter((item) => item !== '')
|
||||
}
|
||||
|
||||
const writeParams = {
|
||||
projectId: effectiveProjectId,
|
||||
summary: params.summary || '',
|
||||
description: params.description || '',
|
||||
issueType: params.issueType || 'Task',
|
||||
parent: params.parentIssue ? { key: params.parentIssue } : undefined,
|
||||
assignee: params.assignee || undefined,
|
||||
priority: params.priority || undefined,
|
||||
labels: parseCommaSeparated(params.labels),
|
||||
duedate: params.duedate || undefined,
|
||||
reporter: params.reporter || undefined,
|
||||
environment: params.environment || undefined,
|
||||
customFieldId: params.customFieldId || undefined,
|
||||
customFieldValue: params.customFieldValue || undefined,
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
@@ -704,6 +812,16 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
accountId: params.accountId,
|
||||
}
|
||||
}
|
||||
case 'get_users': {
|
||||
return {
|
||||
...baseParams,
|
||||
accountId: params.userAccountId || undefined,
|
||||
startAt: params.usersStartAt ? Number.parseInt(params.usersStartAt) : undefined,
|
||||
maxResults: params.usersMaxResults
|
||||
? Number.parseInt(params.usersMaxResults)
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
default:
|
||||
return baseParams
|
||||
}
|
||||
@@ -722,6 +840,15 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
summary: { type: 'string', description: 'Issue summary' },
|
||||
description: { type: 'string', description: 'Issue description' },
|
||||
issueType: { type: 'string', description: 'Issue type' },
|
||||
// Write operation additional inputs
|
||||
assignee: { type: 'string', description: 'Assignee account ID' },
|
||||
priority: { type: 'string', description: 'Priority ID or name' },
|
||||
labels: { type: 'string', description: 'Comma-separated labels for the issue' },
|
||||
duedate: { type: 'string', description: 'Due date in YYYY-MM-DD format' },
|
||||
reporter: { type: 'string', description: 'Reporter account ID' },
|
||||
environment: { type: 'string', description: 'Environment information' },
|
||||
customFieldId: { type: 'string', description: 'Custom field ID (e.g., customfield_10001)' },
|
||||
customFieldValue: { type: 'string', description: 'Value for the custom field' },
|
||||
// Delete operation inputs
|
||||
deleteSubtasks: { type: 'string', description: 'Whether to delete subtasks (true/false)' },
|
||||
// Assign/Watcher operation inputs
|
||||
@@ -758,6 +885,13 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
linkType: { type: 'string', description: 'Type of link (e.g., "Blocks", "Relates")' },
|
||||
linkComment: { type: 'string', description: 'Optional comment for issue link' },
|
||||
linkId: { type: 'string', description: 'Link ID for delete operation' },
|
||||
// Get Users operation inputs
|
||||
userAccountId: {
|
||||
type: 'string',
|
||||
description: 'Account ID for specific user lookup (optional)',
|
||||
},
|
||||
usersStartAt: { type: 'string', description: 'Pagination start index for users' },
|
||||
usersMaxResults: { type: 'string', description: 'Maximum users to return' },
|
||||
},
|
||||
outputs: {
|
||||
// Common outputs across all Jira operations
|
||||
@@ -834,6 +968,12 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
// jira_add_watcher, jira_remove_watcher outputs
|
||||
watcherAccountId: { type: 'string', description: 'Watcher account ID' },
|
||||
|
||||
// jira_get_users outputs
|
||||
users: {
|
||||
type: 'json',
|
||||
description: 'Array of users with accountId, displayName, emailAddress, active status',
|
||||
},
|
||||
|
||||
// jira_bulk_read outputs
|
||||
// Note: bulk_read returns an array in the output field, each item contains:
|
||||
// ts, issueKey, summary, description, status, assignee, created, updated
|
||||
|
||||
217
apps/sim/tools/jira/get_users.ts
Normal file
217
apps/sim/tools/jira/get_users.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface JiraGetUsersParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
accountId?: string
|
||||
startAt?: number
|
||||
maxResults?: number
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface JiraUser {
|
||||
accountId: string
|
||||
accountType?: string
|
||||
active: boolean
|
||||
displayName: string
|
||||
emailAddress?: string
|
||||
avatarUrls?: {
|
||||
'16x16'?: string
|
||||
'24x24'?: string
|
||||
'32x32'?: string
|
||||
'48x48'?: string
|
||||
}
|
||||
timeZone?: string
|
||||
self?: string
|
||||
}
|
||||
|
||||
export interface JiraGetUsersResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
users: JiraUser[]
|
||||
total?: number
|
||||
startAt?: number
|
||||
maxResults?: number
|
||||
}
|
||||
}
|
||||
|
||||
export const jiraGetUsersTool: ToolConfig<JiraGetUsersParams, JiraGetUsersResponse> = {
|
||||
id: 'jira_get_users',
|
||||
name: 'Jira Get Users',
|
||||
description:
|
||||
'Get Jira users. If an account ID is provided, returns a single user. Otherwise, returns a list of all users.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'jira',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Jira',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Your Jira domain (e.g., yourcompany.atlassian.net)',
|
||||
},
|
||||
accountId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Optional account ID to get a specific user. If not provided, returns all users.',
|
||||
},
|
||||
startAt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The index of the first user to return (for pagination, default: 0)',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of users to return (default: 50)',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description:
|
||||
'Jira Cloud ID for the instance. If not provided, it will be fetched using the domain.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: JiraGetUsersParams) => {
|
||||
if (params.cloudId) {
|
||||
if (params.accountId) {
|
||||
return `https://api.atlassian.com/ex/jira/${params.cloudId}/rest/api/3/user?accountId=${encodeURIComponent(params.accountId)}`
|
||||
}
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.startAt !== undefined) queryParams.append('startAt', String(params.startAt))
|
||||
if (params.maxResults !== undefined)
|
||||
queryParams.append('maxResults', String(params.maxResults))
|
||||
const queryString = queryParams.toString()
|
||||
return `https://api.atlassian.com/ex/jira/${params.cloudId}/rest/api/3/users/search${queryString ? `?${queryString}` : ''}`
|
||||
}
|
||||
return 'https://api.atlassian.com/oauth/token/accessible-resources'
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params: JiraGetUsersParams) => ({
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraGetUsersParams) => {
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
|
||||
let usersUrl: string
|
||||
if (params!.accountId) {
|
||||
usersUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/user?accountId=${encodeURIComponent(params!.accountId)}`
|
||||
} else {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params!.startAt !== undefined) queryParams.append('startAt', String(params!.startAt))
|
||||
if (params!.maxResults !== undefined)
|
||||
queryParams.append('maxResults', String(params!.maxResults))
|
||||
const queryString = queryParams.toString()
|
||||
usersUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/users/search${queryString ? `?${queryString}` : ''}`
|
||||
}
|
||||
|
||||
const usersResponse = await fetch(usersUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!usersResponse.ok) {
|
||||
let message = `Failed to get Jira users (${usersResponse.status})`
|
||||
try {
|
||||
const err = await usersResponse.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await usersResponse.json()
|
||||
|
||||
const users = params!.accountId ? [data] : data
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
users: users.map((user: any) => ({
|
||||
accountId: user.accountId,
|
||||
accountType: user.accountType,
|
||||
active: user.active,
|
||||
displayName: user.displayName,
|
||||
emailAddress: user.emailAddress,
|
||||
avatarUrls: user.avatarUrls,
|
||||
timeZone: user.timeZone,
|
||||
self: user.self,
|
||||
})),
|
||||
total: params!.accountId ? 1 : users.length,
|
||||
startAt: params!.startAt || 0,
|
||||
maxResults: params!.maxResults || 50,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
let message = `Failed to get Jira users (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
const users = params?.accountId ? [data] : data
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
users: users.map((user: any) => ({
|
||||
accountId: user.accountId,
|
||||
accountType: user.accountType,
|
||||
active: user.active,
|
||||
displayName: user.displayName,
|
||||
emailAddress: user.emailAddress,
|
||||
avatarUrls: user.avatarUrls,
|
||||
timeZone: user.timeZone,
|
||||
self: user.self,
|
||||
})),
|
||||
total: params?.accountId ? 1 : users.length,
|
||||
startAt: params?.startAt || 0,
|
||||
maxResults: params?.maxResults || 50,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
users: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Array of users with accountId, displayName, emailAddress, active status, and avatarUrls',
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of users returned' },
|
||||
startAt: { type: 'number', description: 'Pagination start index' },
|
||||
maxResults: { type: 'number', description: 'Maximum results per page' },
|
||||
},
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { jiraDeleteIssueLinkTool } from '@/tools/jira/delete_issue_link'
|
||||
import { jiraDeleteWorklogTool } from '@/tools/jira/delete_worklog'
|
||||
import { jiraGetAttachmentsTool } from '@/tools/jira/get_attachments'
|
||||
import { jiraGetCommentsTool } from '@/tools/jira/get_comments'
|
||||
import { jiraGetUsersTool } from '@/tools/jira/get_users'
|
||||
import { jiraGetWorklogsTool } from '@/tools/jira/get_worklogs'
|
||||
import { jiraRemoveWatcherTool } from '@/tools/jira/remove_watcher'
|
||||
import { jiraRetrieveTool } from '@/tools/jira/retrieve'
|
||||
@@ -44,4 +45,5 @@ export {
|
||||
jiraDeleteIssueLinkTool,
|
||||
jiraAddWatcherTool,
|
||||
jiraRemoveWatcherTool,
|
||||
jiraGetUsersTool,
|
||||
}
|
||||
|
||||
@@ -69,6 +69,12 @@ export interface JiraWriteParams {
|
||||
cloudId?: string
|
||||
issueType: string
|
||||
parent?: { key: string }
|
||||
labels?: string[]
|
||||
duedate?: string
|
||||
reporter?: string
|
||||
environment?: string
|
||||
customFieldId?: string
|
||||
customFieldValue?: string
|
||||
}
|
||||
|
||||
export interface JiraWriteResponse extends ToolResponse {
|
||||
|
||||
@@ -46,14 +46,14 @@ export const jiraWriteTool: ToolConfig<JiraWriteParams, JiraWriteResponse> = {
|
||||
priority: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Priority for the issue',
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Priority ID or name for the issue (e.g., "10000" or "High")',
|
||||
},
|
||||
assignee: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Assignee for the issue',
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Assignee account ID for the issue',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
@@ -68,6 +68,42 @@ export const jiraWriteTool: ToolConfig<JiraWriteParams, JiraWriteResponse> = {
|
||||
visibility: 'hidden',
|
||||
description: 'Type of issue to create (e.g., Task, Story)',
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Labels for the issue (array of label names)',
|
||||
},
|
||||
duedate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Due date for the issue (format: YYYY-MM-DD)',
|
||||
},
|
||||
reporter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Reporter account ID for the issue',
|
||||
},
|
||||
environment: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Environment information for the issue',
|
||||
},
|
||||
customFieldId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom field ID (e.g., customfield_10001)',
|
||||
},
|
||||
customFieldValue: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Value for the custom field',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
@@ -89,6 +125,12 @@ export const jiraWriteTool: ToolConfig<JiraWriteParams, JiraWriteResponse> = {
|
||||
cloudId: params.cloudId,
|
||||
issueType: params.issueType,
|
||||
parent: params.parent,
|
||||
labels: params.labels,
|
||||
duedate: params.duedate,
|
||||
reporter: params.reporter,
|
||||
environment: params.environment,
|
||||
customFieldId: params.customFieldId,
|
||||
customFieldValue: params.customFieldValue,
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -134,5 +176,6 @@ export const jiraWriteTool: ToolConfig<JiraWriteParams, JiraWriteResponse> = {
|
||||
issueKey: { type: 'string', description: 'Created issue key (e.g., PROJ-123)' },
|
||||
summary: { type: 'string', description: 'Issue summary' },
|
||||
url: { type: 'string', description: 'URL to the created issue' },
|
||||
assigneeId: { type: 'string', description: 'Account ID of the assigned user (if assigned)' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -463,6 +463,7 @@ import {
|
||||
jiraDeleteWorklogTool,
|
||||
jiraGetAttachmentsTool,
|
||||
jiraGetCommentsTool,
|
||||
jiraGetUsersTool,
|
||||
jiraGetWorklogsTool,
|
||||
jiraRemoveWatcherTool,
|
||||
jiraRetrieveTool,
|
||||
@@ -1478,6 +1479,7 @@ export const tools: Record<string, ToolConfig> = {
|
||||
jira_delete_issue_link: jiraDeleteIssueLinkTool,
|
||||
jira_add_watcher: jiraAddWatcherTool,
|
||||
jira_remove_watcher: jiraRemoveWatcherTool,
|
||||
jira_get_users: jiraGetUsersTool,
|
||||
kalshi_get_markets: kalshiGetMarketsTool,
|
||||
kalshi_get_market: kalshiGetMarketTool,
|
||||
kalshi_get_events: kalshiGetEventsTool,
|
||||
|
||||
@@ -110,10 +110,15 @@ export const slackListChannelsTool: ToolConfig<SlackListChannelsParams, SlackLis
|
||||
creator: channel.creator,
|
||||
}))
|
||||
|
||||
const ids = channels.map((channel: { id: string }) => channel.id)
|
||||
const names = channels.map((channel: { name: string }) => channel.name)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
channels,
|
||||
ids,
|
||||
names,
|
||||
count: channels.length,
|
||||
},
|
||||
}
|
||||
@@ -142,6 +147,14 @@ export const slackListChannelsTool: ToolConfig<SlackListChannelsParams, SlackLis
|
||||
},
|
||||
},
|
||||
},
|
||||
ids: {
|
||||
type: 'array',
|
||||
description: 'Array of channel IDs for easy access',
|
||||
},
|
||||
names: {
|
||||
type: 'array',
|
||||
description: 'Array of channel names for easy access',
|
||||
},
|
||||
count: {
|
||||
type: 'number',
|
||||
description: 'Total number of channels returned',
|
||||
|
||||
@@ -102,10 +102,15 @@ export const slackListUsersTool: ToolConfig<SlackListUsersParams, SlackListUsers
|
||||
status_emoji: user.profile?.status_emoji || '',
|
||||
}))
|
||||
|
||||
const ids = users.map((user: { id: string }) => user.id)
|
||||
const names = users.map((user: { name: string }) => user.name)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
users,
|
||||
ids,
|
||||
names,
|
||||
count: users.length,
|
||||
},
|
||||
}
|
||||
@@ -133,6 +138,14 @@ export const slackListUsersTool: ToolConfig<SlackListUsersParams, SlackListUsers
|
||||
},
|
||||
},
|
||||
},
|
||||
ids: {
|
||||
type: 'array',
|
||||
description: 'Array of user IDs for easy access',
|
||||
},
|
||||
names: {
|
||||
type: 'array',
|
||||
description: 'Array of usernames for easy access',
|
||||
},
|
||||
count: {
|
||||
type: 'number',
|
||||
description: 'Total number of users returned',
|
||||
|
||||
@@ -245,6 +245,8 @@ export interface SlackChannel {
|
||||
export interface SlackListChannelsResponse extends ToolResponse {
|
||||
output: {
|
||||
channels: SlackChannel[]
|
||||
ids: string[]
|
||||
names: string[]
|
||||
count: number
|
||||
}
|
||||
}
|
||||
@@ -291,6 +293,8 @@ export interface SlackUser {
|
||||
export interface SlackListUsersResponse extends ToolResponse {
|
||||
output: {
|
||||
users: SlackUser[]
|
||||
ids: string[]
|
||||
names: string[]
|
||||
count: number
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user