918bbed0fc
- 修复 deps.py 中 get_vn_service 的引用错误 (vn_service.vn_service -> vn_service) - 移除登录页面上的明文密码提示 - 改进前端错误处理,避免数据加载失败导致登录显示错误
446 lines
10 KiB
JavaScript
446 lines
10 KiB
JavaScript
/**
|
|
* Gitea MCP 适配层
|
|
*
|
|
* 封装 Gitea MCP 工具调用,提供统一的接口
|
|
*/
|
|
|
|
import fs from 'fs'
|
|
import path from 'path'
|
|
|
|
/**
|
|
* 加载 Gitea 配置
|
|
*/
|
|
function loadConfig() {
|
|
const configPath = path.join(process.cwd(), '.claude', 'gitea-config.json')
|
|
try {
|
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
|
return config
|
|
} catch (error) {
|
|
throw new Error(`无法加载配置文件: ${configPath}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取仓库信息
|
|
*/
|
|
function getRepoInfo() {
|
|
const config = loadConfig()
|
|
return {
|
|
baseUrl: config.gitea.baseUrl,
|
|
owner: config.gitea.owner,
|
|
repo: config.gitea.repo,
|
|
defaultBranch: config.gitea.defaultBranch
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gitea MCP 适配器类
|
|
*/
|
|
export class GiteaMCPAdapter {
|
|
constructor() {
|
|
this.config = loadConfig()
|
|
this.repoInfo = getRepoInfo()
|
|
}
|
|
|
|
/**
|
|
* 获取仓库信息
|
|
*/
|
|
getRepoInfo() {
|
|
return this.repoInfo
|
|
}
|
|
|
|
/**
|
|
* 创建 Issue
|
|
*
|
|
* @param {Object} options - Issue 选项
|
|
* @param {string} options.title - Issue 标题
|
|
* @param {string} options.body - Issue 正文
|
|
* @param {string[]} options.labels - Issue 标签
|
|
* @returns {Promise<Object>} Issue 信息
|
|
*/
|
|
async createIssue(options) {
|
|
const { title, body, labels = [] } = options
|
|
const { owner, repo } = this.repoInfo
|
|
|
|
try {
|
|
// 调用 Gitea MCP 工具创建 Issue
|
|
const result = await mcp__gitea__issue_write({
|
|
method: 'create',
|
|
owner,
|
|
repo,
|
|
title,
|
|
body,
|
|
labels
|
|
})
|
|
|
|
return {
|
|
id: result.id,
|
|
number: result.number,
|
|
url: `${this.repoInfo.baseUrl}/${owner}/${repo}/issues/${result.number}`,
|
|
title: result.title,
|
|
body: result.body
|
|
}
|
|
} catch (error) {
|
|
throw new Error(`创建 Issue 失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新 Issue
|
|
*
|
|
* @param {number} issueNumber - Issue 编号
|
|
* @param {Object} options - 更新选项
|
|
* @returns {Promise<Object>} 更新后的 Issue 信息
|
|
*/
|
|
async updateIssue(issueNumber, options) {
|
|
const { owner, repo } = this.repoInfo
|
|
const { state, body } = options
|
|
|
|
try {
|
|
const result = await mcp__gitea__issue_write({
|
|
method: 'update',
|
|
owner,
|
|
repo,
|
|
issue_number: issueNumber,
|
|
state,
|
|
body
|
|
})
|
|
|
|
return result
|
|
} catch (error) {
|
|
throw new Error(`更新 Issue 失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 关闭 Issue
|
|
*
|
|
* @param {number} issueNumber - Issue 编号
|
|
* @returns {Promise<Object>} 关闭后的 Issue 信息
|
|
*/
|
|
async closeIssue(issueNumber) {
|
|
return this.updateIssue(issueNumber, { state: 'closed' })
|
|
}
|
|
|
|
/**
|
|
* 读取 Issue Comments
|
|
*
|
|
* @param {number} issueNumber - Issue 编号
|
|
* @returns {Promise<Array>} Comments 列表
|
|
*/
|
|
async getIssueComments(issueNumber) {
|
|
const { owner, repo } = this.repoInfo
|
|
|
|
try {
|
|
const comments = await mcp__gitea__issue_read({
|
|
method: 'get_comments',
|
|
owner,
|
|
repo,
|
|
issue_number: issueNumber
|
|
})
|
|
|
|
return comments
|
|
} catch (error) {
|
|
throw new Error(`读取 Issue Comments 失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 添加 Issue Comment
|
|
*
|
|
* @param {number} issueNumber - Issue 编号
|
|
* @param {string} body - Comment 内容
|
|
* @returns {Promise<Object>} Comment 信息
|
|
*/
|
|
async createIssueComment(issueNumber, body) {
|
|
const { owner, repo } = this.repoInfo
|
|
|
|
try {
|
|
const comment = await mcp__gitea__issue_write({
|
|
method: 'add_comment',
|
|
owner,
|
|
repo,
|
|
issue_number: issueNumber,
|
|
body
|
|
})
|
|
|
|
return comment
|
|
} catch (error) {
|
|
throw new Error(`添加 Comment 失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 创建分支
|
|
*
|
|
* @param {string} branchName - 分支名称
|
|
* @param {string} oldBranch - 源分支(默认为主分支)
|
|
* @returns {Promise<Object>} 分支信息
|
|
*/
|
|
async createBranch(branchName, oldBranch = null) {
|
|
const { owner, repo, defaultBranch } = this.repoInfo
|
|
const sourceBranch = oldBranch || defaultBranch
|
|
|
|
try {
|
|
const result = await mcp__gitea__create_branch({
|
|
owner,
|
|
repo,
|
|
branch: branchName,
|
|
old_branch: sourceBranch
|
|
})
|
|
|
|
return result
|
|
} catch (error) {
|
|
throw new Error(`创建分支失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 列出分支
|
|
*
|
|
* @returns {Promise<Array>} 分支列表
|
|
*/
|
|
async listBranches() {
|
|
const { owner, repo } = this.repoInfo
|
|
|
|
try {
|
|
const branches = await mcp__gitea__list_branches({
|
|
owner,
|
|
repo
|
|
})
|
|
|
|
return branches
|
|
} catch (error) {
|
|
throw new Error(`列出分支失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 创建 Pull Request
|
|
*
|
|
* @param {Object} options - PR 选项
|
|
* @param {string} options.title - PR 标题
|
|
* @param {string} options.body - PR 正文
|
|
* @param {string} options.head - 源分支
|
|
* @param {string} options.base - 目标分支
|
|
* @returns {Promise<Object>} PR 信息
|
|
*/
|
|
async createPullRequest(options) {
|
|
const { title, body, head, base } = options
|
|
const { owner, repo, defaultBranch } = this.repoInfo
|
|
const targetBranch = base || defaultBranch
|
|
|
|
try {
|
|
const result = await mcp__gitea__pull_request_write({
|
|
method: 'create',
|
|
owner,
|
|
repo,
|
|
title,
|
|
body,
|
|
head,
|
|
base: targetBranch
|
|
})
|
|
|
|
return {
|
|
id: result.id,
|
|
number: result.number,
|
|
url: `${this.repoInfo.baseUrl}/${owner}/${repo}/pulls/${result.number}`,
|
|
title: result.title,
|
|
body: result.body,
|
|
head: result.head,
|
|
base: result.base
|
|
}
|
|
} catch (error) {
|
|
throw new Error(`创建 PR 失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 合并 Pull Request
|
|
*
|
|
* @param {number} pullNumber - PR 编号
|
|
* @param {Object} options - 合并选项
|
|
* @returns {Promise<Object>} 合并后的 PR 信息
|
|
*/
|
|
async mergePullRequest(pullNumber, options = {}) {
|
|
const { owner, repo } = this.repoInfo
|
|
const { deleteBranch = true, mergeStyle = 'merge' } = options
|
|
|
|
try {
|
|
const result = await mcp__gitea__pull_request_write({
|
|
method: 'merge',
|
|
owner,
|
|
repo,
|
|
pull_number: pullNumber,
|
|
delete_branch_after_merge: deleteBranch,
|
|
merge_style: mergeStyle
|
|
})
|
|
|
|
return result
|
|
} catch (error) {
|
|
throw new Error(`合并 PR 失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取 Pull Request
|
|
*
|
|
* @param {number} pullNumber - PR 编号
|
|
* @returns {Promise<Object>} PR 信息
|
|
*/
|
|
async getPullRequest(pullNumber) {
|
|
const { owner, repo } = this.repoInfo
|
|
|
|
try {
|
|
const pr = await mcp__gitea__pull_request_read({
|
|
method: 'get',
|
|
owner,
|
|
repo,
|
|
pull_number: pullNumber
|
|
})
|
|
|
|
return pr
|
|
} catch (error) {
|
|
throw new Error(`获取 PR 失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 列出 Pull Request
|
|
*
|
|
* @param {Object} options - 查询选项
|
|
* @returns {Promise<Array>} PR 列表
|
|
*/
|
|
async listPullRequests(options = {}) {
|
|
const { owner, repo } = this.repoInfo
|
|
const { state = 'open' } = options
|
|
|
|
try {
|
|
const prs = await mcp__gitea__list_pull_requests({
|
|
owner,
|
|
repo,
|
|
state
|
|
})
|
|
|
|
return prs
|
|
} catch (error) {
|
|
throw new Error(`列出 PR 失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 创建或更新文件
|
|
*
|
|
* @param {Object} options - 文件选项
|
|
* @param {string} options.path - 文件路径
|
|
* @param {string} options.content - 文件内容
|
|
* @param {string} options.message - Commit 消息
|
|
* @param {string} options.branch - 分支名称
|
|
* @param {string} options.sha - 文件 SHA(更新时需要)
|
|
* @returns {Promise<Object>} 文件信息
|
|
*/
|
|
async createOrUpdateFile(options) {
|
|
const { path: filePath, content, message, branch, sha } = options
|
|
const { owner, repo } = this.repoInfo
|
|
|
|
try {
|
|
const result = await mcp__gitea__create_or_update_file({
|
|
owner,
|
|
repo,
|
|
path: filePath,
|
|
content: Buffer.from(content).toString('base64'),
|
|
message,
|
|
branch_name: branch,
|
|
sha
|
|
})
|
|
|
|
return result
|
|
} catch (error) {
|
|
throw new Error(`创建/更新文件失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取文件内容
|
|
*
|
|
* @param {string} filePath - 文件路径
|
|
* @param {string} ref - 分支或 commit
|
|
* @returns {Promise<Object>} 文件内容
|
|
*/
|
|
async getFileContents(filePath, ref = null) {
|
|
const { owner, repo, defaultBranch } = this.repoInfo
|
|
const targetRef = ref || defaultBranch
|
|
|
|
try {
|
|
const file = await mcp__gitea__get_file_contents({
|
|
owner,
|
|
repo,
|
|
path: filePath,
|
|
ref: targetRef
|
|
})
|
|
|
|
return file
|
|
} catch (error) {
|
|
throw new Error(`获取文件内容失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 列出提交
|
|
*
|
|
* @param {Object} options - 查询选项
|
|
* @returns {Promise<Array>} 提交列表
|
|
*/
|
|
async listCommits(options = {}) {
|
|
const { owner, repo } = this.repoInfo
|
|
const { sha, path } = options
|
|
|
|
try {
|
|
const commits = await mcp__gitea__list_commits({
|
|
owner,
|
|
repo,
|
|
sha,
|
|
path
|
|
})
|
|
|
|
return commits
|
|
} catch (error) {
|
|
throw new Error(`列出提交失败: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 带重试的操作
|
|
*
|
|
* @param {Function} operation - 操作函数
|
|
* @param {number} maxRetries - 最大重试次数
|
|
* @returns {Promise<any>} 操作结果
|
|
*/
|
|
async withRetry(operation, maxRetries = 3) {
|
|
let lastError
|
|
for (let i = 0; i < maxRetries; i++) {
|
|
try {
|
|
return await operation()
|
|
} catch (error) {
|
|
lastError = error
|
|
console.warn(`操作失败,重试 ${i + 1}/${maxRetries}: ${error.message}`)
|
|
// 等待后重试
|
|
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)))
|
|
}
|
|
}
|
|
throw lastError
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 创建适配器实例
|
|
*/
|
|
export function createGiteaAdapter() {
|
|
return new GiteaMCPAdapter()
|
|
}
|
|
|
|
/**
|
|
* 导出单例
|
|
*/
|
|
export default createGiteaAdapter()
|