news 2026/9/16 18:45:13

Corsair Jira 插件接入指南:32 个类型化接口、本地数据同步与 Webhook 事件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Corsair Jira 插件接入指南:32 个类型化接口、本地数据同步与 Webhook 事件

Corsair Jira 插件接入指南:32 个类型化接口、本地数据同步与 Webhook 事件

【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair

导读

@corsair-dev/jira是 Corsair 生态中的 Jira 官方插件,它把 Jira Cloud 的 REST API v3 与 Agile REST API v1.0 封装成 32 个类型安全的jira.api.*操作、6 张本地同步数据表与 3 种入站 Webhook 事件,让 AI Agent 可以直接通过一个客户端完成"查问题、改状态、建项目、排迭代、回评论"等完整工作流。读完本文,你将掌握该插件的安装、认证、端点调用、本地数据查询与 Webhook 接入的全套实战方法,并理解其底层 HTTP 客户端与错误处理机制。


插件概览:一个 Jira 连接层

@corsair-dev/jira位于仓库 packages/jira 目录,是 Corsair 标准插件形态的参考实现之一。从源码结构看,插件由四层组成(见 packages/jira/index.ts):

  • 端点层endpoints/目录按业务域拆分为 issues.ts、comments.ts、projects.ts、sprints.ts、users.ts,覆盖问题、评论、项目、迭代、用户与用户组六大域;
  • 客户端层:client.ts 封装 Jira REST API v3 与 Agile API v1.0 两类请求通道,以及附件上传;
  • 数据模型层:schema/database.ts 定义了 6 个可本地同步的实体(boardscommentsissuesprojectssprintsusers);
  • Webhook 层:webhooks/ 提供 3 种事件的匹配、HMAC 签名校验与多租户识别。

所有端点的输入/输出均通过 Zod 模式定义于 endpoints/types.ts,并在插件注册时暴露为endpointSchemas,这是"类型安全"的来源——调用方与响应方共用同一套模式,非法字段在编译期即被拦截。


安装与环境要求

根据 packages/jira/README.md,使用 pnpm 安装:

pnpm add @corsair-dev/jira

也可以使用你习惯的包管理器(npm / yarn / bun)同时安装corsair与插件本体:

npm install corsair @corsair-dev/jira

插件在 packages/jira/package.json 中声明了两个 peer 依赖:

依赖版本要求用途
corsair>=0.1.0提供插件运行时、HTTP 请求、数据库与 Webhook 基础设施
zod^4.1.13端点输入/输出与 Webhook 负载的模式校验

当前仓库中该插件版本为0.1.5,类型入口为./dist/index.d.ts,产物为 ESM 格式。


快速接入:注册插件与连接租户

在 docs/plugins/jira/overview.mdx 中给出了标准的接入流程。首先创建一个corsair.ts并注册插件:

import Database from 'better-sqlite3'; import { createCorsair } from 'corsair'; import { jira } from '@corsair-dev/jira'; export const corsair = createCorsair({ plugins: [ jira(), ], database: new Database('corsair.db'), kek: process.env.CORSAIR_KEK!, hub: { projectApiKey: process.env.CORSAIR_API_KEY!, signingSecret: process.env.CORSAIR_SIGNING_SECRET!, }, });

Corsair 默认支持多租户(multi-tenancy),用corsair.withTenant(id)隔离不同用户/Jira 站点的凭据与数据。接入方通过 Hub 生成 connect 链接,让租户在浏览器中完成授权,并把结果回传给应用:

const { connectUrl } = await corsair.manage.connect.createLink({ plugin: 'jira', tenantId: 'acme', }); // redirect the user's browser to connectUrl

首次调用时,Corsair 会提示租户提供 API Key(详见下文认证章节)。关于 KEK、Hub 密钥与租户隔离的细节,可分别参考 docs/getting-started/set-up-with-your-agent.mdx 与 docs/concepts/multi-tenancy.mdx。

插件工厂选项

jira()工厂函数接受 JiraPluginOptions 配置:

选项类型说明
authType'api_key'认证方式,默认为api_key
keystring全局 API Key,优先级高于数据库中的租户凭据
webhookSecretstring全局 Webhook 签名密钥(也可由租户单独存储)
cloudUrlstringJira Cloud 地址,如https://your-domain.atlassian.net,所有 API 调用必需
hooksobject端点执行前/后的生命周期钩子
webhookHooksobjectWebhook 处理前/后的钩子
errorHandlersCorsairErrorHandler自定义错误处理器,覆盖插件默认行为
permissionsPluginPermissionsConfig权限配置,使用 Jira 端点树的点分路径,非法路径会在编译期报错

认证:API Key 与租户凭据

packages/jira/README.md 明确:Auth: API key,Corsair 会在租户首次使用时提示输入凭据。Jira Cloud 使用 Basic Auth,即邮箱 + API Token的组合(email:apiToken),客户端会将其 Base64 编码后放入Authorization头(见 client.ts)。

获取并存储凭据

根据 docs/plugins/jira/get-credentials.mdx:

  1. 登录 Atlassian 账号,进入Security → API tokens页面;
  2. 点击Create API token,命名(如 "Corsair Integration")后创建;
  3. 立即复制 Token(只显示一次),妥善保存。

随后通过 Corsair CLI 存储为租户凭据:

pnpm corsair setup --plugin=jira api_key=your-api-token

Jira Cloud URL(如https://yourcompany.atlassian.net)会作为独立账号字段cloud_url存储。这一点在 index.ts 的jiraAuthConfig中有专门设计——它在基础api_key配置上扩展了account: ['cloud_url']字段,使云端地址可以通过corsair auth动态设置,而无需硬编码在插件选项里。

keyBuilder 的取 key 优先级

从 index.ts 的keyBuilder实现可以梳理出凭据解析顺序:

  1. Webhook 来源:优先使用options.webhookSecret;否则从ctx.keys.get_webhook_signature()读取租户的webhook_signature,缺失则抛出[auth-missing:jira:webhook_signature]错误;
  2. 端点来源:优先使用options.key(全局配置);否则从ctx.keys.get_api_key()读取租户api_key,缺失则抛出AuthMissingError('jira', 'api_key')

每个端点处理器还会通过ctx.keys.get_cloud_url()获取目标站点地址,二者共同构成一次完整调用的认证上下文。

OAuth 配置说明

尽管文档推荐并使用 API Key,源码中的oauthConfig仍内置了 Atlassian OAuth 端点与授权范围(read:jira-workwrite:jira-workread:jira-useroffline_access,audience 为api.atlassian.com)。可以推断这是为未来或自托管场景预留的 OAuth 能力;当前默认与文档化认证方式以 API Key 为准。


端点总览:32 个类型化操作

下表完整列出了插件暴露的全部操作(来自 packages/jira/README.md,同时与 index.ts 中的jiraEndpointsNestedjiraEndpointMeta一致)。操作 ID 为jira.api.<operation>的完整路径,风险级别分为readwritedestructive三档:

OperationOperation IDRiskDescription
comments.addjira.api.comments.addwriteAdd a comment to a Jira issue
comments.deletejira.api.comments.deletedestructiveDelete a comment from a Jira issue [DESTRUCTIVE]
comments.getjira.api.comments.getreadGet a specific comment on a Jira issue
comments.listjira.api.comments.listreadList all comments on a Jira issue
comments.updatejira.api.comments.updatewriteUpdate a comment on a Jira issue
groups.createjira.api.groups.createwriteCreate a new Jira group
groups.getAlljira.api.groups.getAllreadGet all Jira groups
issues.addAttachmentjira.api.issues.addAttachmentwriteAdd an attachment to a Jira issue
issues.addWatcherjira.api.issues.addWatcherwriteAdd a watcher to a Jira issue
issues.assignjira.api.issues.assignwriteAssign a Jira issue to a user
issues.bulkCreatejira.api.issues.bulkCreatewriteBulk create multiple Jira issues
issues.bulkFetchjira.api.issues.bulkFetchreadBulk fetch multiple Jira issues by ID or key
issues.createjira.api.issues.createwriteCreate a new Jira issue
issues.deletejira.api.issues.deletedestructiveDelete a Jira issue [DESTRUCTIVE]
issues.editjira.api.issues.editwriteEdit an existing Jira issue
issues.getjira.api.issues.getreadGet a Jira issue by ID or key
issues.getTransitionsjira.api.issues.getTransitionsreadGet available transitions for a Jira issue
issues.linkIssuesjira.api.issues.linkIssueswriteLink two Jira issues together
issues.removeWatcherjira.api.issues.removeWatcherwriteRemove a watcher from a Jira issue
issues.searchjira.api.issues.searchreadSearch issues using JQL
issues.transitionjira.api.issues.transitionwriteTransition a Jira issue to a new status
projects.createjira.api.projects.createwriteCreate a new Jira project
projects.getjira.api.projects.getreadGet a Jira project by ID or key
projects.getRolesjira.api.projects.getRolesreadGet project roles for a Jira project
projects.listjira.api.projects.listreadList Jira projects
sprints.createjira.api.sprints.createwriteCreate a new sprint on a Jira board
sprints.listjira.api.sprints.listreadList sprints for a Jira board
sprints.listBoardsjira.api.sprints.listBoardsreadList Jira boards
sprints.moveIssuesjira.api.sprints.moveIssueswriteMove issues to a sprint
users.findjira.api.users.findreadSearch for Jira users
users.getAlljira.api.users.getAllreadGet all Jira users
users.getCurrentjira.api.users.getCurrentreadGet the currently authenticated Jira user

每个操作的完整输入/输出字段、类型与必填性,均可在 docs/plugins/jira/api.mdx 中查阅(该页由插件 Zod 模式生成)。


核心操作实战

以下示例均基于corsair.withTenant('acme')获取的租户实例调用,参数采用 Zod 模式中的snake_case命名(见 endpoints/types.ts)。

问题(Issues)域

创建问题——最小参数为project_keysummary,其余可选:

const tenant = corsair.withTenant('acme'); await tenant.jira.api.issues.create({ project_key: 'DEMO', summary: 'Fix login redirect bug', issue_type: 'Task', // 默认为 'Task' description: 'Users are redirected to /home instead of /dashboard', assignee: '712020:abc123', // accountId priority: 'High', labels: ['frontend', 'auth'], due_date: '2026-09-30', });

从 issues.ts 的实现可见,插件会把输入映射为 Jira REST v3 的fields结构,其中description会通过makeAdf()自动包装为 Atlassian Document Format(ADF),即{ version: 1, type: 'doc', content: [{ type: 'paragraph', content: [{ type: 'text', text }] }] }——你无需关心 ADF 细节,传普通字符串即可。

查询与搜索

// 按 ID 或 Key 获取 await tenant.jira.api.issues.get({ issue_id_or_key: 'DEMO-42', fields: 'summary,status,assignee', expand: 'renderedFields', }); // JQL 搜索 await tenant.jira.api.issues.search({ jql: 'project = DEMO AND status = "In Progress" ORDER BY updated DESC', start_at: 0, max_results: 50, });

状态流转——通常先取可用流转,再执行:

const { transitions } = await tenant.jira.api.issues.getTransitions({ issue_id_or_key: 'DEMO-42', }); await tenant.jira.api.issues.transition({ issue_id_or_key: 'DEMO-42', transition_id: transitions![0].id!, comment: 'Moving to Done per sprint review', });

transition支持附带评论,插件会将其同样包装为 ADF 追加到变更记录中。

批量操作

await tenant.jira.api.issues.bulkCreate({ issues: [ { project_key: 'DEMO', summary: 'Issue A', issue_type: 'Task' }, { project_key: 'DEMO', summary: 'Issue B', priority: 'High' }, ], }); await tenant.jira.api.issues.bulkFetch({ issue_ids_or_keys: ['DEMO-1', 'DEMO-2'], fields: ['summary', 'status'], });

附件上传——两种方式二选一:file_content(Base64 内容)或file_url(远程地址),Zod 模式通过refine强制至少提供其一:

// 方式一:Base64 内容 await tenant.jira.api.issues.addAttachment({ issue_id_or_key: 'DEMO-42', file_name: 'screenshot.png', file_content: 'iVBORw0KGgoAAAANSUhEUg...', mime_type: 'image/png', }); // 方式二:远程 URL await tenant.jira.api.issues.addAttachment({ issue_id_or_key: 'DEMO-42', file_name: 'design.pdf', file_url: 'https://cdn.example.com/design.pdf', });

评论(Comments)域

// 添加评论(支持 visibility 限制到角色/群组) await tenant.jira.api.comments.add({ issue_id_or_key: 'DEMO-42', comment: 'Fixed in build #1201, please verify', visibility_type: 'role', visibility_value: 'Developers', }); // 分页列出评论 await tenant.jira.api.comments.list({ issue_id_or_key: 'DEMO-42', start_at: 0, max_results: 20, order_by: '-created', });

项目、迭代、用户与用户组

// 创建项目(默认 software 类型、UNASSIGNED 指派策略) await tenant.jira.api.projects.create({ key: 'DEMO', name: 'Demo Project', project_type_key: 'software', description: 'Demo project for the AI assistant', }); // 列出项目 / 获取角色 await tenant.jira.api.projects.list({ query: 'demo', max_results: 10 }); await tenant.jira.api.projects.getRoles({ project_id_or_key: 'DEMO' }); // 迭代域(走 Agile API v1.0) const { values: boards } = await tenant.jira.api.sprints.listBoards({ project_key_or_id: 'DEMO', }); await tenant.jira.api.sprints.create({ origin_board_id: boards![0]!.id!, name: 'Sprint 24', goal: 'Ship onboarding v2', }); await tenant.jira.api.sprints.moveIssues({ sprint_id: 124, issue_keys: ['DEMO-42', 'DEMO-43'], }); // 用户域 await tenant.jira.api.users.getCurrent({}); await tenant.jira.api.users.find({ query: 'alice', max_results: 5 }); // 用户组域 await tenant.jira.api.groups.getAll({}); await tenant.jira.api.groups.create({ name: 'support-team' });

注意:项目、迭代、用户等端点同样会在成功后把返回实体写入本地数据库(详见"本地数据同步"章节),实现"远端操作 + 本地可查询"的双通道一致性。


源码级原理:HTTP 客户端与错误处理

双 API 通道

client.ts 提供了两个请求入口:

  • makeJiraRequest:基础路径为${cloudUrl}/rest/api/3,服务所有非迭代端点;
  • makeJiraAgileRequest:基础路径为${cloudUrl}/rest/agile/1.0,服务看板/迭代端点(sprints.*)。

两者统一采用 Basic Auth(Basic base64(email:apiToken)),cloudUrl会先经过sanitizeCloudUrl去除尾部斜杠再拼接。GET/DELETE 支持 query 参数,POST/PUT/PATCH 携带 JSON body。

限流与重试

插件内置了 Jira 限流配置JIRA_RATE_LIMIT_CONFIG:启用限流、最多重试 3 次、初始退避 1 秒、退避倍率 2,并读取Retry-After响应头。当请求经过corsair/httprequest()时,该配置会被传入以驱动自动重试。

附件上传的特殊处理

uploadJiraAttachment不使用 JSON,而是构造multipart/form-data

  • 若提供file_url,先fetch拉取内容,mime 类型从响应头自动识别;
  • 否则把file_content(Base64)解码为 Buffer;
  • 请求携带X-Atlassian-Token: no-check头(Jira 附件接口的防 CSRF 要求),且故意不设置 Content-Type,由 fetch 自动生成 multipart boundary。

分层错误处理

error-handlers.ts 定义了四个错误处理器,与corsair/httpApiError协同:

处理器匹配条件行为
RATE_LIMIT_ERRORHTTP 429 或消息含rate_limit/ratelimited/429最多重试 5 次,尊重Retry-After
AUTH_ERRORHTTP 401 或unauthorized/authentication failed/invalid_auth不重试,提示检查email:apiToken格式
PERMISSION_ERRORHTTP 403 或permission_denied/forbidden/access_denied不重试,记录告警
DEFAULT兜底记录错误,不重试

你可以在jira({ errorHandlers: ... })中覆盖或扩展这些默认行为(index.ts中通过{ ...errorHandlers, ...options.errorHandlers }合并)。


本地数据同步:6 个可搜索实体

插件会把调用结果与 Webhook 事件持续同步到本地数据库。6 个实体及其 Zod 模型定义在 schema/database.ts:

实体主要字段
boardsid(number)、nametypeprojectIdprojectKeyprojectName
commentsidissueKeybodyauthorAccountIdauthorDisplayNamecreatedupdated
issuesidkeysummarydescriptionstatusassignee*reporter*priorityissueTypeprojectKeyprojectIdlabels
projectsidkeynamedescriptionprojectTypeKeyleadAccountIdleadDisplayName
sprintsid(number)、namestategoalstartDateendDateoriginBoardId
usersaccountIddisplayNameemailAddressactivetimeZonelocale

同步写入通过ctx.db.<entity>.upsertByEntityId(...)完成,例如 issues.ts 在creategetsearchbulkCreatebulkFetch成功后都会落库;Webhook 处理器也会在收到事件时更新对应行(见 webhooks/new-issue.ts)。editassign等只持有issue_id_or_key(可能是 Key 而非数字 ID)的操作会刻意跳过落库,交由下一次issues.get刷新数据——源码注释中明确记录了这一设计取舍。

查询本地数据

const rows = await corsair.jira.db.issues.search({ data: { status: 'In Progress', projectKey: 'DEMO' }, limit: 100, offset: 0, });

每个实体的可过滤字段与操作符各不相同,docs/plugins/jira/database.mdx 给出了完整矩阵,规律如下:

  • 字符串字段:支持equalscontainsstartsWithendsWithin
  • 数字字段(如boards.idsprints.idsprints.originBoardId):支持equalsgtgteltltein
  • createdAt日期字段:支持equalsbeforeafterbetween
  • users.active布尔字段仅支持equals

所有.search()均接受limitoffset做分页。


Webhook:3 种事件与签名验证

packages/jira/README.md 说明插件处理 3 种 Webhook 事件,映射关系见 index.ts 的jiraWebhooksNested

事件路径Webhook Event 值触发时机
issues.newIssuejira:issue_created新建问题
issues.updatedIssuejira:issue_updated问题被更新(含 changelog 变更明细)
projects.newProjectproject_created新建项目

接收 Webhook 的 HTTP Handler

把 Jira 的订阅 URL 指向你的 Corsair HTTP 处理端点(Next.js 路由示例来自 docs/plugins/jira/webhooks.mdx):

import { processWebhook } from 'corsair'; import { corsair } from '@/server/corsair'; export async function POST(request: Request) { const headers = Object.fromEntries(request.headers); const body = await request.json(); const result = await processWebhook(corsair, headers, body); return result.response; }

匹配与签名验证

  • 事件匹配createJiraMatch(webhookEvent)(webhooks/types.ts)解析请求体并比对webhookEvent字段;插件级匹配器则检查请求头中是否含x-atlassian-webhook-identifier(index.ts 的pluginWebhookMatcher)。
  • 签名验证verifyJiraWebhookSignature使用 HMAC-SHA256 校验x-hub-signature头(格式为sha256=<hash>),并用crypto.timingSafeEqual做常数时间比较,防止时序攻击。签名密钥来自options.webhookSecret或租户的webhook_signature。验证失败时返回 HTTP 401。
  • 多租户识别:tenant-matcher.ts 从负载的issue.selfissue.fields.project.selfproject.selfuser.self中提取站点 host,得到cloud_url后按此维度路由到对应租户。

使用 webhookHooks 处理事件

jira({ webhookHooks: { issues: { newIssue: { before(ctx, args) { // 事件处理前:记录、鉴权、去重 return { ctx, args }; }, after(ctx, response) { // 事件处理后:通知、审计 }, }, updatedIssue: { before(ctx, args) { return { ctx, args }; }, after(ctx, response) {}, }, }, projects: { newProject: { before(ctx, args) { return { ctx, args }; }, after(ctx, response) {}, }, }, }, })

各事件的完整负载结构(issueuserchangelogproject的嵌套类型)可在 docs/plugins/jira/webhooks.mdx 查阅,Zod 模式定义见 webhooks/types.ts。收到事件后,插件默认行为是同步对应实体到本地数据库(如newIssue会 upsert 问题与用户行),随后触发after钩子。


权限配置:限制 Agent 能做什么

JiraPluginOptions.permissions用于控制 AI Agent 允许执行的操作,采用点分路径引用端点树(如'issues.delete''comments.delete'),路径非法时会产生编译期类型错误:

jira({ permissions: { 'issues.delete': false, // 禁止删除问题 'issues.transition': true, // 允许状态流转 'comments.add': true, // 允许添加评论 }, })

该机制与 docs/concepts/permissions.mdx 中的权限模型一致,配合端点元数据中的riskLevelread/write/destructive),可对不同风险等级的操作做精细化放行。


测试与验证

插件自带两套测试,可作为验证行为与理解调用链的参考:

  • api.test.ts:针对真实 Jira Cloud 的集成型类型测试,通过环境变量JIRA_API_KEYJIRA_CLOUD_URL驱动,覆盖usersprojectsissuescomments等域,每个响应都会用JiraEndpointOutputSchemas.*.parse()做运行时校验;
  • client.test.ts 与 webhooks/types.test.ts:客户端与 Webhook 类型层面的测试。

本地运行测试与构建:

pnpm --filter @corsair-dev/jira test pnpm --filter @corsair-dev/jira build # tsc --build --force && tsup

许可与参考

插件以Apache-2.0许可发布(见 packages/jira/README.md 与 packages/jira/package.json)。更完整的参考文档(含每个端点的输入/输出类型、数据库过滤操作符、Webhook 负载示例)位于 docs/plugins/jira 目录:

  • overview.mdx:接入总览与快速开始
  • api.mdx:全部jira.api.*操作与类型参考
  • database.mdx:同步实体与搜索过滤操作符
  • webhooks.mdx:事件路径、负载与webhookHooks示例
  • get-credentials.mdx:API Token 与 Webhook 密钥获取步骤

若要将这些操作暴露给 AI 助手调用,可参考 docs/mcp-adapters/mcp-adapters.mdx 的 MCP 适配方案,把插件能力直接映射为 MCP 工具。

【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 18:44:25

心理咨询行业的发展前景与趋势深度分析-中国心理学会心理咨询师水平评价-长春心理咨询培训机构

心理咨询行业的发展前景与趋势深度分析中国心理学会心理咨询师水平评价-心理咨询培训机构 很多人选择学心理咨询时会考虑&#xff1a;这个行业未来会怎样&#xff1f;值不值得投入&#xff1f;今天就来做一个相对客观的行业前景分析&#xff0c;帮你做出理性的判断。 一、行业发…

作者头像 李华
网站建设 2026/9/16 18:43:49

WeChatMsg 免费教程:本地导出微信聊天记录并生成年度聊天报告

WeChatMsg 免费教程&#xff1a;本地导出微信聊天记录并生成年度聊天报告 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/w…

作者头像 李华
网站建设 2026/9/16 18:42:50

Litestar 官方基准测试全解析:方法论、六大场景与性能解读指南

Litestar 官方基准测试全解析&#xff1a;方法论、六大场景与性能解读指南 【免费下载链接】litestar Light, flexible and extensible ASGI framework | Built to scale 项目地址: https://gitcode.com/GitHub_Trending/li/litestar 导读 性能是 Web 框架选型中最受关…

作者头像 李华