chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:34:48 +08:00
commit 77bb5bf71f
3762 changed files with 353249 additions and 0 deletions
+300
View File
@@ -0,0 +1,300 @@
import { z, IntegrationDefinitionProps, ActionDefinition } from '@botpress/sdk'
import { linearIdsSchema, userProfileSchema, issueSchema } from './schemas'
const _channels = ['issue'] as const
export type Target = {
displayName: string
tags: { [key: string]: string }
channel: (typeof _channels)[number]
}
const findTarget = {
title: 'Find Target',
description: 'Find a target on Linear',
input: {
schema: z.object({
query: z.string().min(2).title('Search query').describe('Issue title, description, or number'),
channel: z.enum(['issue']).title('Channel').describe('The channel to search in'),
}),
},
output: {
schema: z.object({
targets: z
.array(
z.object({
displayName: z.string().title('Display Name').describe('The name of the issue'),
tags: z.record(z.string()).title('Tags').describe('The tags applied to the issue'),
channel: z.enum(['issue']).title('Channel').describe('The channel of the search result'),
})
)
.title('Results')
.describe('The list of issues found'),
}),
},
} as const satisfies ActionDefinition
const getIssue = {
title: 'Get Issue',
description: 'Get an issue by its ID',
input: {
schema: z.object({
issueId: z.string().title('Issue ID').describe('The issue ID on Linear. Ex: {{event.payload.linearIds.issueId}}'),
}),
},
output: {
schema: issueSchema.title('Issue').describe('The issue found'),
},
} as const satisfies ActionDefinition
const listIssues = {
title: 'List Issues',
description: 'List issues from Linear',
input: {
schema: z.object({
count: z.number().optional().default(10).title('Fetch Amount').describe('The number of issues to return'),
startCursor: z.string().optional().title('Start Cursor').describe('The cursor to start from'),
teamId: z.string().optional().title('Team ID').describe('The team ID to filter by'),
startDate: z.string().optional().title('Start Date').describe('Ignore issues created before this date'),
}),
},
output: {
schema: z.object({
issues: z
.array(issueSchema.extend({ linearIds: linearIdsSchema }))
.title('Issues')
.describe('The list of matching issues'),
nextCursor: z.string().optional().title('Next Cursor').describe('The cursor to fetch the next page'),
}),
},
} as const satisfies ActionDefinition
const userSchema = z.object({
id: z.string().title('ID').describe('The user ID on Linear'),
email: z.string().title('Email').describe("The user's email address"),
name: z.string().title('Name').describe("The user's nickname"),
displayName: z.string().title('Display Name').describe("The user's display name"),
avatarUrl: z.string().optional().title('Avatar URL').describe('The URL of the user avatar'),
description: z
.string()
.optional()
.title('Description')
.describe("The user's description, such as their title or bio"),
lastSeen: z.string().optional().title('Last Seen').describe('The last time the user was seen'),
updatedAt: z.string().datetime().title('Updated At').describe('The last time the user was updated'),
})
const listUsers = {
title: 'List Users',
description: 'List users from Linear',
input: {
schema: z.object({
count: z.number().optional().default(10).title('Fetch Amount').describe('The number of users to return'),
startCursor: z.string().optional().title('Start Cursor').describe('The cursor to start from'),
}),
},
output: {
schema: z.object({
users: z.array(userSchema).title('Users').describe('The list of matching users'),
nextCursor: z.string().optional().title('Next Cursor').describe('The cursor to fetch the next page'),
}),
},
} as const satisfies ActionDefinition
const listTeams = {
title: 'List Teams',
description: 'List teams from Linear',
input: {
schema: z.object({}),
},
output: {
schema: z.object({
teams: z
.array(
z.object({
id: z.string().title('ID').describe('The unique identifier of the entity'),
key: z.string().title('Key').describe("The team's key"),
name: z.string().title('Name').describe("The team's name"),
description: z.string().optional().title('Description').describe("The team's description"),
icon: z.string().optional().title('Icon').describe('The icon of the team'),
})
)
.title('Teams')
.describe('The list of teams'),
}),
},
} as const satisfies ActionDefinition
const listStates = {
title: 'List States',
description: 'List states from Linear',
input: {
schema: z.object({
count: z.number().optional().default(10).title('Fetch Amount').describe('The number of states to return'),
startCursor: z.string().optional().title('Start Cursor').describe('The cursor to start from'),
}),
},
output: {
schema: z.object({
states: z
.array(
z.object({
id: z.string().title('ID').describe('The unique identifier of the entity'),
name: z.string().title('Name').describe("The state's name"),
})
)
.title('States')
.describe('The list of states'),
nextCursor: z.string().optional().title('Next Cursor').describe('The cursor to fetch the next page'),
}),
},
} as const satisfies ActionDefinition
const markAsDuplicate = {
title: 'Mark Issue as Duplicate',
description: 'Mark an issue as a duplicate of another',
input: {
schema: z.object({
issueId: z.string().title('Issue ID').describe('The issue ID on Linear. Ex: {{event.payload.linearIds.issueId}}'),
relatedIssueId: z.string().title('Related Issue ID').describe('The ID of the existing issue on Linear'),
}),
},
output: {
schema: z.object({}),
},
} as const satisfies ActionDefinition
const getUser = {
title: 'Get User Profile',
description: 'Get a user profile from Linear',
input: {
schema: z.object({
linearUserId: z
.string()
.optional()
.title('User ID')
.describe(
"The user's ID on Linear. Ex: {{event.payload.linearIds.creatorId}}. If omitted, returns the current user."
),
}),
},
output: {
schema: userProfileSchema.title('User').describe('The user profile'),
},
} as const satisfies ActionDefinition
const updateIssue = {
title: 'Update Issue',
description: 'Update an issue on Linear',
input: {
schema: z.object({
issueId: z.string().title('Issue ID').describe('The issue ID on Linear. Example: {{event.payload.id}}'),
priority: z.number().optional().title('Priority').describe('0 = none, 1 = urgent, 2 = high, 3 = medium, 4 = low'),
teamName: z
.string()
.optional()
.title('Move to team...')
.describe('Type a name to change the assigned team of the issue'),
labels: z
.array(z.string())
.optional()
.title('Set labels')
.describe('One or multiple labels to assign to this issue'),
project: z.string().optional().title('Associate to project...').describe('A project to associate to this issue'),
}),
},
output: {
schema: z.object({
issue: issueSchema.optional().title('Issue').describe('The updated issue'),
}),
},
} as const satisfies ActionDefinition
const createIssue = {
title: 'Create Issue',
description: 'Create an issue on Linear',
input: {
schema: z.object({
title: z.string().min(1).title('Title').describe("The issue's title"),
description: z.string().title('Description').describe('The content of the issue'),
priority: z.number().optional().title('Priority').describe('0 = none, 1 = urgent, 2 = high, 3 = medium, 4 = low'),
teamName: z.string().title('Team Name').describe('Name of the team to assign the issue to'),
labels: z.array(z.string()).optional().title('Labels').describe('One or multiple labels to assign to this issue'),
project: z.string().optional().title('Project').describe('A project to associate to this issue'),
}),
},
output: {
schema: z.object({
issue: issueSchema.title('Issue').describe('The created issue'),
}),
},
} as const satisfies ActionDefinition
const deleteIssue = {
title: 'Delete Issue',
description: 'Delete an issue on Linear',
input: {
schema: z.object({
id: z.string().title('Issue ID').describe('The issue ID on Linear. Ex: {{event.payload.linearIds.issueId}}'),
}),
},
output: {
schema: z.object({}),
},
} as const satisfies ActionDefinition
const sendRawGraphqlQuery = {
title: 'Send Raw GraphQL Query',
description: 'Send a raw GraphQL query to the linear API',
input: {
schema: z.object({
query: z.string().title('Query').describe('The GraphQL query'),
parameters: z
.array(
z.object({
name: z.string().title('Name').describe('The parameter name'),
value: z.any().title('Value').describe('The parameter value'),
})
)
.optional()
.title('Parameters')
.describe('The query parameters'),
}),
},
output: {
schema: z.object({
result: z.unknown().title('Result').describe('The query result'),
}),
},
} as const satisfies ActionDefinition
const resolveComment = {
title: 'Resolve Comment',
description: 'Resolve a comment by id',
input: {
schema: z.object({
id: z.string().title('ID').describe('The comment ID'),
}),
},
output: {
schema: z.object({
success: z.boolean().title('Success').describe('Whether the operation was successful'),
}),
},
} as const satisfies ActionDefinition
export const actions = {
findTarget,
listIssues,
listTeams,
listUsers,
listStates,
markAsDuplicate,
getIssue,
getUser,
updateIssue,
createIssue,
deleteIssue,
sendRawGraphqlQuery,
resolveComment,
} as const satisfies IntegrationDefinitionProps['actions']
+30
View File
@@ -0,0 +1,30 @@
import { EventDefinition, IntegrationDefinitionProps } from '@botpress/sdk'
import { issueEventSchema, targets } from './schemas'
const issueCreated = {
title: 'Issue Created',
description: 'Triggered when an issue is created',
schema: issueEventSchema.extend({
targets: targets.title('Created Issue').describe('The issue that was created'),
}),
} as const satisfies EventDefinition
const issueUpdated = {
title: 'Issue Updated',
description: 'Triggered when an issue is updated',
schema: issueEventSchema.extend({
targets: targets.title('Updated Issue').describe('The issue that was updated'),
}),
} as const satisfies EventDefinition
const issueDeleted = {
title: 'Issue Deleted',
description: 'Triggered when an issue is deleted',
schema: issueEventSchema.omit({ userId: true, conversationId: true }),
} as const satisfies EventDefinition
export const events = {
issueCreated,
issueUpdated,
issueDeleted,
} as const satisfies IntegrationDefinitionProps['events']
+102
View File
@@ -0,0 +1,102 @@
import { z, IntegrationDefinitionProps, messages, ConfigurationDefinition } from '@botpress/sdk'
import { issueSchema } from './schemas'
export { actions } from './actions'
export { events } from './events'
export { states } from './states'
export * as schemas from './schemas'
export const configuration = {
identifier: {
linkTemplateScript: 'linkTemplate.vrl',
},
schema: z.object({
displayName: z.string().optional().title('Display Name').describe('The name displayed in message transmissions'),
avatarUrl: z.string().optional().title('Avatar URL').describe('The web address for the profile picture'),
}),
} as const satisfies ConfigurationDefinition
export const configurations = {
apiKey: {
title: 'API Key',
description: 'Configure Linear with an API Key.',
schema: z.object({
apiKey: z.string().title('API Key').describe('The API key for Linear'),
webhookSigningSecret: z
.string()
.secret()
.optional()
.title('Webhook Signing Secret (deprecated)')
.describe(
'The secret key for verifying incoming Linear webhook events if the webhook has been registered manually. ' +
"If you don't provide this value, a webhook will be created automatically."
),
}),
},
} as const satisfies IntegrationDefinitionProps['configurations']
export const channels = {
issue: {
title: 'Issue',
description: 'A linear issue',
messages: { ...messages.defaults, markdown: messages.markdown, bloc: messages.markdownBloc },
message: {
tags: {
id: {
title: 'Comment ID',
description: 'The ID of the comment on Linear',
},
},
},
conversation: {
tags: {
id: {
title: 'Issue ID',
description: 'The ID of the issue on Linear',
},
title: {
title: 'Issue Title',
description: 'The title of the issue',
},
url: {
title: 'Issue URL',
description: 'The URL of the issue on Linear',
},
parentId: {
title: 'Parent Issue ID',
description: 'The ID of the parent issue on Linear',
},
parentTitle: {
title: 'Parent Issue Title',
description: 'The title of the parent issue',
},
parentUrl: {
title: 'Parent Issue URL',
description: 'The URL of the parent issue on Linear',
},
},
},
},
} as const satisfies IntegrationDefinitionProps['channels']
export const user = {
tags: {
id: {
title: 'User ID',
description: 'The ID of the user on Linear',
},
},
} as const satisfies IntegrationDefinitionProps['user']
export const entities = {
issue: {
title: 'Issue',
description: 'A linear issue',
schema: issueSchema,
},
issueConversation: {
title: 'Issue Conversation',
description: 'A conversation representing a linear issue',
schema: z.object({ id: z.string().title('Issue ID').describe('The issue ID on Linear') }),
},
} as const satisfies IntegrationDefinitionProps['entities']
+138
View File
@@ -0,0 +1,138 @@
import { z } from '@botpress/sdk'
import { User } from '@linear/sdk'
import { assert, Equals } from 'tsafe/assert'
export const targets = z.object({
issue: z
.record(z.string().title('Issue').describe('The issue'))
.optional()
.title('Target Issue')
.describe('The target issue on Linear'),
})
type TransformDatesToStrings<T> = {
[K in keyof T]: T[K] extends Date | undefined ? string : T[K]
}
type LinearUserProfile = Pick<
TransformDatesToStrings<User>,
| 'admin'
| 'archivedAt'
| 'avatarUrl'
| 'createdAt'
| 'description'
| 'displayName'
| 'guest'
| 'email'
| 'isMe'
| 'url'
| 'timezone'
| 'name'
> & { linearId: string }
export const userProfileSchema = z.object({
linearId: z.string().title('User ID').describe('Linear User ID'),
admin: z.boolean().title('Is Admin?').describe('Indicates if the user is an admin of the organization'),
archivedAt: z.string().datetime().optional().title('Archival Date').describe('Date when the user was archived'),
avatarUrl: z.string().url().optional().title('Avatar URL').describe('The URL for the profile picture'),
createdAt: z.string().datetime().title('Creation Date').describe('Date when the user was created'),
description: z
.string()
.optional()
.title('Description')
.describe('A short description of the user, either its title or bio'),
displayName: z
.string()
.title('Display Name')
.describe("The user's display (nick) name. Unique within each organization"),
guest: z
.boolean()
.title('Is Guest?')
.describe('Whether the user is a guest in the workspace and limited to accessing a subset of teams'),
email: z.string().title('Email').describe("The user's email address"),
isMe: z.boolean().title('Is Self?').describe('Whether the user is the currently authenticated user'),
url: z.string().title('Profile URL').describe("User's profile URL"),
timezone: z.string().optional().title('Timezone').describe('The local timezone of the user'),
name: z.string().title('Full Name').describe("The user's full name"),
})
export const linearIdsSchema = z
.object({
creatorId: z
.string()
.optional()
.title("Creator's User ID")
.describe('The internal Linear User ID of the user who created the issue'),
labelIds: z
.array(z.string())
.optional()
.title('Label IDs')
.describe('The internal Linear Label IDs associated with the issue'),
issueId: z.string().title('Issue ID').describe('The internal Linear Issue ID'),
teamId: z.string().optional().title('Team ID').describe('The internal Linear Team ID'),
projectId: z.string().optional().title('Project ID').describe('The internal Linear Project ID'),
assigneeId: z.string().optional().title('Assignee User ID').describe('The internal Linear Assignee ID'),
subscriberIds: z
.array(z.string())
.optional()
.title('Subscriber IDs')
.describe('The internal Linear Subscriber User IDs'),
})
.describe('The Linear IDs of the referenced entities')
const commonIssueProperties = {
title: z.string().title('Title').describe('The issue title on Linear, such as "Fix the bug'),
number: z.number().title('Number').describe('The issue number on Linear, such as "123" in XXX-123'),
createdAt: z.string().datetime().title('Created At').describe('The ISO date the issue was created'),
updatedAt: z.string().datetime().title('Updated At').describe('The ISO date the issue was last updated'),
identifier: z.string().title('Identifier').describe("Issue's human readable identifier (e.g. XXX-123)"),
url: z.string().title('Issue URL').describe('The URL of the issue on Linear'),
priority: z
.number()
.title('Priority')
.describe('Priority of the issue, such as "1" for "Urgent", 0 for "No Priority"'),
description: z
.string()
.optional()
.title('Description')
.describe('A markdown description of the issue. Images and videos are inlined using markdown links.'),
}
export const issueSchema = z.object({
...commonIssueProperties,
id: z.string().title('Issue ID').describe('The issue ID on Linear'),
estimate: z.number().optional().title('Points Estimate').describe('The estimate of the issue in points'),
})
export const issueEventSchema = z.object({
...commonIssueProperties,
teamName: z
.string()
.optional()
.title('Team Name')
.describe('The name of the Linear team the issue currently belongs to, such as "Customer Support"'),
teamKey: z
.string()
.optional()
.title('Team Key')
.describe('The key of the Linear team the issue currently belongs to, such as "XXX" in XXX-123'),
status: z.string().title('Status').describe('The issue State name (such as "In Progress"'),
statusColor: z.string().title('Status Color').describe('The issue State color (#000000 format)'),
statusType: z.string().title('Status Type').describe('The issue State type'),
labels: z
.array(z.string().title('Label Name').describe('The name of the label'))
.optional()
.title('Applied Labels')
.describe('Label names'),
linearIds: linearIdsSchema.title('Linear IDs').describe('The Linear IDs of the referenced entities'),
userId: z
.string()
.optional()
.title('Botpress User ID')
.describe(
'The Botpress User ID of the individual who initiated the issue. If not provided, it indicates the issue was generated by the bot.'
),
conversationId: z.string().title('Botpress Conversation ID').describe('Botpress Conversation ID of the issue'),
})
assert<Equals<z.infer<typeof userProfileSchema>, LinearUserProfile>>()
+53
View File
@@ -0,0 +1,53 @@
import { z, IntegrationDefinitionProps } from '@botpress/sdk'
import { userProfileSchema } from './schemas'
const oauthCredentialsSchema = z.object({
accessToken: z.string().title('Access Token').describe('The access token for Linear'),
refreshToken: z.string().title('Refresh Token').describe('The refresh token needed when the access token expires'),
expiresAt: z.string().title('Expires At').describe('The time when the access token expires'),
})
export const states = {
credentials: {
type: 'integration',
schema: oauthCredentialsSchema,
},
adminCredentials: {
type: 'integration',
schema: oauthCredentialsSchema.describe(
'User-actor OAuth credentials used only for admin operations (webhook register/unregister)'
),
},
environment: {
type: 'integration',
schema: z.object({
env: z
.enum(['preview', 'production'])
.title('Environment')
.describe('The environment where the integration is installed'),
source: z.string().optional().title('Source').describe('The source of the OAuth request, eg: "desk"'),
wizardPhase: z
.enum(['admin', 'app'])
.optional()
.title('Wizard Phase')
.describe('Which leg of the two-phase OAuth wizard is currently expected on /oauth callback'),
runtimeActor: z
.enum(['user', 'app'])
.optional()
.title('Runtime Actor')
.describe('Which Linear OAuth actor type was used for the runtime credentials'),
}),
},
// TODO: delete these 2 states when the backend stop considering state deletion as breaking change
configuration: {
type: 'integration',
schema: z.object({
botUserId: z.string().optional().title('Bot User ID').describe('The ID of the bot user'),
}),
},
profile: {
type: 'user',
schema: userProfileSchema,
},
} satisfies IntegrationDefinitionProps['states']