@zzp123/mcp-zentao 1.3.2 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { AssignFeedbackRequest, Bug, BugStatus, Build, CloseFeedbackRequest, CreateBuildRequest, CreateBugRequest, CreateExecutionRequest, CreateFeedbackRequest, CreatePlanRequest, CreateProductRequest, CreateProgramRequest, CreateProjectRequest, CreateStoryRequest, CreateTaskRequest, CreateTestCaseRequest, CreateTicketRequest, CreateUserRequest, Execution, Feedback, FileUploadResponse, Module, ModuleType, Plan, Product, Program, Project, Release, Story, Task, TaskStatus, TaskUpdate, TestCase, Ticket, UpdateBuildRequest, UpdateBugRequest, UpdateExecutionRequest, UpdateFeedbackRequest, UpdatePlanRequest, UpdateProductRequest, UpdateProgramRequest, UpdateProjectRequest, UpdateStoryRequest, UpdateTestCaseRequest, UpdateTicketRequest, UpdateUserRequest, UploadFileRequest, UserDetail, ZentaoConfig } from '../types/zentao';
1
+ import { AssignFeedbackRequest, Bug, BugStatus, Build, CloseFeedbackRequest, CreateBuildRequest, CreateBugRequest, CreateExecutionRequest, CreateFeedbackRequest, CreatePlanRequest, CreateProductRequest, CreateProgramRequest, CreateProjectRequest, CreateStoryRequest, CreateTaskRequest, CreateTestCaseRequest, CreateTicketRequest, CreateUserRequest, Execution, Feedback, FileUploadResponse, Module, ModuleType, Plan, Product, Program, Project, Release, ResolveBugRequest, Story, Task, TaskStatus, TaskUpdate, TestCase, Ticket, UpdateBuildRequest, UpdateBugRequest, UpdateExecutionRequest, UpdateFeedbackRequest, UpdatePlanRequest, UpdateProductRequest, UpdateProgramRequest, UpdateProjectRequest, UpdateStoryRequest, UpdateTestCaseRequest, UpdateTicketRequest, UpdateUserRequest, UploadFileRequest, UserDetail, ZentaoConfig } from '../types/zentao';
2
2
  export declare class ZentaoAPI {
3
3
  private config;
4
4
  private client;
@@ -11,6 +11,7 @@ export declare class ZentaoAPI {
11
11
  getProducts(): Promise<Product[]>;
12
12
  getMyBugs(status?: BugStatus, productId?: number): Promise<Bug[]>;
13
13
  getBugDetail(bugId: number): Promise<Bug>;
14
+ resolveBug(bugId: number, resolution: ResolveBugRequest): Promise<Bug>;
14
15
  updateTask(taskId: number, update: TaskUpdate): Promise<Task>;
15
16
  finishTask(taskId: number, update?: TaskUpdate): Promise<Task>;
16
17
  createTask(task: CreateTaskRequest): Promise<Task>;
@@ -158,6 +158,25 @@ export class ZentaoAPI {
158
158
  throw error;
159
159
  }
160
160
  }
161
+ async resolveBug(bugId, resolution) {
162
+ try {
163
+ console.log(`正在解决Bug ${bugId}...`);
164
+ // 验证必填字段
165
+ if (resolution.resolution === 'fixed' && !resolution.resolvedBuild) {
166
+ throw new Error('当解决方案为"已解决(fixed)"时,必须提供解决版本(resolvedBuild)');
167
+ }
168
+ if (resolution.resolution === 'duplicate' && !resolution.duplicateBug) {
169
+ throw new Error('当解决方案为"重复Bug(duplicate)"时,必须提供重复Bug的ID(duplicateBug)');
170
+ }
171
+ const response = await this.request('POST', `/bugs/${bugId}/resolve`, undefined, resolution);
172
+ console.log('Bug解决响应:', response);
173
+ return response;
174
+ }
175
+ catch (error) {
176
+ console.error('解决Bug失败:', error);
177
+ throw error;
178
+ }
179
+ }
161
180
  async updateTask(taskId, update) {
162
181
  try {
163
182
  console.log(`正在更新任务 ${taskId}...`);
package/dist/index.js CHANGED
@@ -109,6 +109,29 @@ server.tool("getBugDetail", {
109
109
  content: [{ type: "text", text: JSON.stringify(bug, null, 2) }]
110
110
  };
111
111
  });
112
+ // Add resolveBug tool
113
+ server.tool("resolveBug", {
114
+ bugId: z.number(),
115
+ resolution: z.enum(['fixed', 'bydesign', 'duplicate', 'external', 'notrepro', 'postponed', 'willnotfix']),
116
+ resolvedBuild: z.string().optional(),
117
+ assignedTo: z.string().optional(),
118
+ comment: z.string().optional(),
119
+ duplicateBug: z.number().optional()
120
+ }, async ({ bugId, resolution, resolvedBuild, assignedTo, comment, duplicateBug }) => {
121
+ if (!zentaoApi)
122
+ throw new Error("Please initialize Zentao API first");
123
+ const resolveBugRequest = {
124
+ resolution,
125
+ resolvedBuild,
126
+ assignedTo,
127
+ comment,
128
+ duplicateBug
129
+ };
130
+ const bug = await zentaoApi.resolveBug(bugId, resolveBugRequest);
131
+ return {
132
+ content: [{ type: "text", text: JSON.stringify(bug, null, 2) }]
133
+ };
134
+ });
112
135
  // Add updateTask tool
113
136
  server.tool("updateTask", {
114
137
  taskId: z.number(),
@@ -976,18 +999,26 @@ server.tool("uploadImageFromClipboard", {
976
999
  filename: z.string().optional(),
977
1000
  uid: z.string().optional()
978
1001
  }, async ({ filename, uid }) => {
979
- if (!zentaoApi)
980
- throw new Error("Please initialize Zentao API first");
1002
+ if (!zentaoApi) {
1003
+ return {
1004
+ content: [{
1005
+ type: "text",
1006
+ text: JSON.stringify({ error: "请先初始化禅道API配置" }, null, 2)
1007
+ }],
1008
+ isError: true
1009
+ };
1010
+ }
981
1011
  const fs = await import('fs');
982
1012
  const path = await import('path');
983
1013
  const { execSync } = await import('child_process');
984
1014
  try {
1015
+ console.log('[uploadImageFromClipboard] 开始执行...');
985
1016
  // 读取系统剪贴板图片
986
- let base64Data;
987
1017
  let fileBuffer;
988
1018
  let finalFilename;
989
1019
  // Windows: 使用 PowerShell 读取剪贴板
990
1020
  if (process.platform === 'win32') {
1021
+ console.log('[uploadImageFromClipboard] Windows平台,使用PowerShell读取剪贴板...');
991
1022
  const psScript = `
992
1023
  Add-Type -AssemblyName System.Windows.Forms
993
1024
  $img = [System.Windows.Forms.Clipboard]::GetImage()
@@ -1005,64 +1036,118 @@ server.tool("uploadImageFromClipboard", {
1005
1036
  stdio: ['pipe', 'pipe', 'pipe']
1006
1037
  }).trim();
1007
1038
  if (!result || result.includes('NoImage') || result.includes('Error')) {
1008
- throw new Error('剪贴板中没有图片。请先复制图片,不要粘贴到输入框,直接告诉我"上传"。');
1039
+ console.error('[uploadImageFromClipboard] 剪贴板中没有图片');
1040
+ return {
1041
+ content: [{
1042
+ type: "text",
1043
+ text: JSON.stringify({
1044
+ error: "剪贴板中没有图片",
1045
+ tip: "请先复制图片,不要粘贴到输入框,直接告诉我'上传'"
1046
+ }, null, 2)
1047
+ }],
1048
+ isError: true
1049
+ };
1009
1050
  }
1010
- base64Data = result;
1011
- fileBuffer = Buffer.from(base64Data, 'base64');
1051
+ fileBuffer = Buffer.from(result, 'base64');
1012
1052
  finalFilename = filename || `clipboard_${Date.now()}.png`;
1053
+ console.log(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes`);
1013
1054
  }
1014
1055
  // macOS: 使用 pngpaste
1015
1056
  else if (process.platform === 'darwin') {
1057
+ console.log('[uploadImageFromClipboard] macOS平台,使用pngpaste读取剪贴板...');
1016
1058
  const tempFile = path.join(process.cwd(), '.temp_clipboard.png');
1017
1059
  try {
1018
1060
  execSync(`pngpaste "${tempFile}"`);
1019
1061
  fileBuffer = fs.readFileSync(tempFile);
1020
1062
  fs.unlinkSync(tempFile);
1021
1063
  finalFilename = filename || `clipboard_${Date.now()}.png`;
1064
+ console.log(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes`);
1022
1065
  }
1023
- catch {
1024
- throw new Error('剪贴板中没有图片。请先安装 pngpaste: brew install pngpaste');
1066
+ catch (err) {
1067
+ console.error('[uploadImageFromClipboard] macOS读取剪贴板失败:', err);
1068
+ return {
1069
+ content: [{
1070
+ type: "text",
1071
+ text: JSON.stringify({
1072
+ error: "剪贴板中没有图片或pngpaste未安装",
1073
+ tip: "请先安装 pngpaste: brew install pngpaste"
1074
+ }, null, 2)
1075
+ }],
1076
+ isError: true
1077
+ };
1025
1078
  }
1026
1079
  }
1027
1080
  // Linux: 使用 xclip
1028
1081
  else {
1082
+ console.log('[uploadImageFromClipboard] Linux平台,使用xclip读取剪贴板...');
1029
1083
  try {
1030
1084
  const buffer = execSync('xclip -selection clipboard -t image/png -o', {
1031
1085
  maxBuffer: 10 * 1024 * 1024
1032
1086
  });
1033
1087
  fileBuffer = buffer;
1034
1088
  finalFilename = filename || `clipboard_${Date.now()}.png`;
1089
+ console.log(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes`);
1035
1090
  }
1036
- catch {
1037
- throw new Error('剪贴板中没有图片。请先安装 xclip: sudo apt-get install xclip');
1091
+ catch (err) {
1092
+ console.error('[uploadImageFromClipboard] Linux读取剪贴板失败:', err);
1093
+ return {
1094
+ content: [{
1095
+ type: "text",
1096
+ text: JSON.stringify({
1097
+ error: "剪贴板中没有图片或xclip未安装",
1098
+ tip: "请先安装 xclip: sudo apt-get install xclip"
1099
+ }, null, 2)
1100
+ }],
1101
+ isError: true
1102
+ };
1038
1103
  }
1039
1104
  }
1040
1105
  // 创建 img 文件夹
1041
1106
  const imgDir = path.join(process.cwd(), 'img');
1042
1107
  if (!fs.existsSync(imgDir)) {
1108
+ console.log(`[uploadImageFromClipboard] 创建目录: ${imgDir}`);
1043
1109
  fs.mkdirSync(imgDir, { recursive: true });
1044
1110
  }
1045
1111
  // 保存到 img 文件夹
1046
1112
  const savedPath = path.join(imgDir, finalFilename);
1047
1113
  fs.writeFileSync(savedPath, fileBuffer);
1114
+ console.log(`[uploadImageFromClipboard] 图片已保存到: ${savedPath}`);
1048
1115
  // 上传到禅道
1049
- const result = await zentaoApi.uploadFile({
1116
+ console.log('[uploadImageFromClipboard] 开始上传到禅道...');
1117
+ const uploadResult = await zentaoApi.uploadFile({
1050
1118
  file: fileBuffer,
1051
1119
  filename: finalFilename,
1052
1120
  uid
1053
1121
  });
1122
+ console.log('[uploadImageFromClipboard] 上传成功,结果:', uploadResult);
1054
1123
  const response = {
1055
- upload: result,
1124
+ success: true,
1125
+ upload: uploadResult,
1056
1126
  savedPath: savedPath,
1127
+ filename: finalFilename,
1128
+ fileSize: fileBuffer.length,
1057
1129
  message: `图片已保存到本地并上传到禅道`
1058
1130
  };
1059
- return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
1131
+ console.log('[uploadImageFromClipboard] 返回结果:', response);
1132
+ return {
1133
+ content: [{
1134
+ type: "text",
1135
+ text: JSON.stringify(response, null, 2)
1136
+ }]
1137
+ };
1060
1138
  }
1061
1139
  catch (error) {
1140
+ console.error('[uploadImageFromClipboard] 发生错误:', error);
1141
+ const errorResponse = {
1142
+ success: false,
1143
+ error: error.message || String(error),
1144
+ stack: error.stack,
1145
+ tip: "请检查:\n1. 确保已复制图片到剪贴板\n2. 不要粘贴到输入框,直接说'上传剪贴板图片'\n3. 或使用命令行: upload.bat"
1146
+ };
1062
1147
  return {
1063
1148
  content: [{
1064
1149
  type: "text",
1065
- text: `错误: ${error.message}\n\n提示:\n1. 确保已复制图片到剪贴板\n2. 不要粘贴到输入框,直接说"上传剪贴板图片"\n3. 或使用命令行: upload.bat`
1150
+ text: JSON.stringify(errorResponse, null, 2)
1066
1151
  }],
1067
1152
  isError: true
1068
1153
  };
@@ -50,6 +50,14 @@ export interface TaskUpdate {
50
50
  }
51
51
  export type TaskStatus = 'wait' | 'doing' | 'done' | 'all';
52
52
  export type BugStatus = 'active' | 'resolved' | 'closed' | 'all';
53
+ export type ResolutionType = 'fixed' | 'bydesign' | 'duplicate' | 'external' | 'notrepro' | 'postponed' | 'willnotfix';
54
+ export interface ResolveBugRequest {
55
+ resolution: ResolutionType;
56
+ resolvedBuild?: string;
57
+ assignedTo?: string;
58
+ comment?: string;
59
+ duplicateBug?: number;
60
+ }
53
61
  export interface User {
54
62
  id: number;
55
63
  account: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zzp123/mcp-zentao",
3
- "version": "1.3.2",
3
+ "version": "1.4.1",
4
4
  "description": "禅道项目管理系统的高级API集成包,提供任务管理、Bug跟踪等功能的完整封装,专为Cursor IDE设计的MCP扩展",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",