@hangox/pm-cli 1.1.6 → 1.2.0

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.
package/dist/api.d.ts ADDED
@@ -0,0 +1,462 @@
1
+ /**
2
+ * API 响应类型
3
+ */
4
+ interface ApiResponse<T = unknown> {
5
+ success: boolean;
6
+ data?: T;
7
+ message?: string;
8
+ msg?: string;
9
+ api_error_msg?: string;
10
+ post_error_msg?: string;
11
+ }
12
+ /**
13
+ * 工时条目
14
+ */
15
+ interface TimeEntry {
16
+ id: number;
17
+ project: {
18
+ id: number;
19
+ name: string;
20
+ };
21
+ issue?: {
22
+ id: number;
23
+ };
24
+ user: {
25
+ id: number;
26
+ name: string;
27
+ };
28
+ activity: {
29
+ id: number;
30
+ name: string;
31
+ };
32
+ hours: number;
33
+ comments: string;
34
+ spent_on: string;
35
+ created_on: string;
36
+ updated_on: string;
37
+ }
38
+ /**
39
+ * 工时查询参数
40
+ */
41
+ interface TimeEntryQueryParams {
42
+ token: string;
43
+ host: string;
44
+ project: string;
45
+ from_date?: string;
46
+ to_date?: string;
47
+ user_id?: number;
48
+ activity_id?: number;
49
+ member_of_group_id?: number;
50
+ tracker_id?: number;
51
+ version_id?: number;
52
+ offset?: number;
53
+ limit?: number;
54
+ }
55
+ /**
56
+ * 问题详情
57
+ */
58
+ interface Issue {
59
+ id: number;
60
+ project: {
61
+ id: number;
62
+ name: string;
63
+ };
64
+ tracker: {
65
+ id: number;
66
+ name: string;
67
+ };
68
+ status: {
69
+ id: number;
70
+ name: string;
71
+ };
72
+ priority: {
73
+ id: number;
74
+ name: string;
75
+ };
76
+ author: {
77
+ id: number;
78
+ name: string;
79
+ };
80
+ assigned_to?: {
81
+ id: number;
82
+ name: string;
83
+ };
84
+ parent?: {
85
+ id: number;
86
+ };
87
+ subject: string;
88
+ description: string;
89
+ start_date?: string;
90
+ due_date?: string;
91
+ done_ratio: number;
92
+ estimated_hours?: number;
93
+ spent_hours?: number;
94
+ created_on: string;
95
+ updated_on: string;
96
+ closed_on?: string;
97
+ }
98
+ /**
99
+ * 带子单的问题详情(递归结构)
100
+ */
101
+ interface IssueWithChildren extends Issue {
102
+ children?: IssueWithChildren[];
103
+ level: number;
104
+ }
105
+ /**
106
+ * 用户信息
107
+ */
108
+ interface User {
109
+ id: number;
110
+ login: string;
111
+ firstname: string;
112
+ lastname: string;
113
+ mail: string;
114
+ created_on: string;
115
+ last_login_on?: string;
116
+ }
117
+ /**
118
+ * 项目信息
119
+ */
120
+ interface Project {
121
+ id: number;
122
+ name: string;
123
+ identifier: string;
124
+ description: string;
125
+ status: number;
126
+ is_public: boolean;
127
+ created_on: string;
128
+ updated_on: string;
129
+ }
130
+ /**
131
+ * CLI 配置
132
+ */
133
+ interface CliConfig {
134
+ default: {
135
+ token?: string;
136
+ host?: string;
137
+ project?: string;
138
+ userId?: string;
139
+ userName?: string;
140
+ userMail?: string;
141
+ };
142
+ profiles: Record<string, {
143
+ host?: string;
144
+ project?: string;
145
+ token?: string;
146
+ userId?: string;
147
+ userName?: string;
148
+ userMail?: string;
149
+ }>;
150
+ }
151
+ /**
152
+ * 凭据信息(合并后的配置)
153
+ */
154
+ interface Credentials {
155
+ token: string;
156
+ host: string;
157
+ project: string;
158
+ }
159
+ /**
160
+ * PM 链接解析结果
161
+ */
162
+ interface PmLinkInfo {
163
+ host: string;
164
+ issueId: string;
165
+ }
166
+ /**
167
+ * 同步子单结果
168
+ */
169
+ interface SyncResult {
170
+ /** 源单子任务总数 */
171
+ totalTasks: number;
172
+ /** 成功创建的数量 */
173
+ totalCreated: number;
174
+ /** 跳过的数量(同名任务) */
175
+ totalSkipped: number;
176
+ /** 失败的数量 */
177
+ totalFailed: number;
178
+ /** 创建成功的详情 */
179
+ created: Array<{
180
+ sourceId: number;
181
+ newId: number;
182
+ subject: string;
183
+ parentId: number;
184
+ }>;
185
+ /** 跳过的详情 */
186
+ skipped: Array<{
187
+ sourceId: number;
188
+ subject: string;
189
+ reason: string;
190
+ }>;
191
+ /** 失败的详情 */
192
+ failed: Array<{
193
+ sourceId: number;
194
+ subject: string;
195
+ error: string;
196
+ }>;
197
+ }
198
+ /**
199
+ * 批量创建结果
200
+ */
201
+ interface BatchCreateResult {
202
+ /** 成功创建的数量 */
203
+ totalCreated: number;
204
+ /** 失败的数量 */
205
+ totalFailed: number;
206
+ /** 任务总数 */
207
+ totalTasks: number;
208
+ /** 总工时(天) */
209
+ totalEstimatedHours: number;
210
+ /** 创建成功的详情 */
211
+ created: Array<{
212
+ newId: number;
213
+ subject: string;
214
+ parentId: number;
215
+ estimatedHours?: number;
216
+ }>;
217
+ /** 失败的详情 */
218
+ failed: Array<{
219
+ subject: string;
220
+ parentId: number;
221
+ error: string;
222
+ }>;
223
+ }
224
+
225
+ /**
226
+ * 问题服务
227
+ */
228
+ declare class IssueService {
229
+ /**
230
+ * 获取问题详情
231
+ */
232
+ getIssue(token: string, host: string, project: string, issueId: number, includeChildren?: boolean, includeRelations?: boolean): Promise<ApiResponse<Issue>>;
233
+ /**
234
+ * 创建问题
235
+ */
236
+ createIssue(params: Record<string, unknown>): Promise<ApiResponse<Issue>>;
237
+ /**
238
+ * 更新问题
239
+ */
240
+ updateIssue(params: Record<string, unknown>): Promise<ApiResponse<Issue>>;
241
+ /**
242
+ * 获取问题附件
243
+ */
244
+ getIssueAttachments(token: string, host: string, project: string, issueId: number): Promise<ApiResponse<unknown>>;
245
+ /**
246
+ * 获取问题字段选项
247
+ */
248
+ getIssueFieldOptions(token: string, host: string, project: string): Promise<ApiResponse<unknown>>;
249
+ /**
250
+ * 自定义查询
251
+ */
252
+ customQuery(token: string, host: string, project: string, queryId: number, limit?: number, offset?: number): Promise<ApiResponse<Issue[]>>;
253
+ /**
254
+ * V6 过滤器查询
255
+ */
256
+ filterQueryV6(token: string, host: string, project: string, mode: 'normal' | 'simple' | 'advanced', filterParams: Record<string, unknown>): Promise<ApiResponse<Issue[]>>;
257
+ /**
258
+ * 递归获取问题及其子单(包含完整详情)
259
+ * @param depth 递归深度,默认 10
260
+ * @param currentLevel 当前层级(内部使用)
261
+ *
262
+ * 实现逻辑:
263
+ * 1. 调用 getIssue(includeChildren=true) 获取当前问题详情
264
+ * 2. API 返回的 children 字段只包含简略信息(status 是字符串,缺少 assigned_to 等)
265
+ * 3. 从 children 中提取子单 ID,递归调用 getIssue 获取每个子单的完整详情
266
+ * 4. 用完整详情替换原始的简略 children
267
+ */
268
+ getIssueWithChildren(token: string, host: string, project: string, issueId: number, depth?: number, currentLevel?: number): Promise<ApiResponse<IssueWithChildren>>;
269
+ /**
270
+ * 获取直接子单的 ID 列表
271
+ */
272
+ getDirectChildren(token: string, host: string, project: string, parentId: number): Promise<ApiResponse<number[]>>;
273
+ /**
274
+ * 查询子任务(按根任务和负责人过滤)
275
+ * 使用两步查询:先过滤获取 ID 列表,再批量获取详情
276
+ */
277
+ queryChildren(token: string, host: string, project: string, rootId: number, assignedToId?: number, perPage?: number): Promise<ApiResponse<unknown>>;
278
+ /**
279
+ * 批量获取多个问题详情
280
+ * @param issueIds 问题 ID 数组
281
+ * @param options 可选参数
282
+ * @returns 返回所有问题的详情数组(包含成功和失败的结果)
283
+ */
284
+ getMultipleIssues(token: string, host: string, project: string, issueIds: number[], options?: {
285
+ includeChildren?: boolean;
286
+ includeRelations?: boolean;
287
+ depth?: number;
288
+ }): Promise<ApiResponse<Array<{
289
+ id: number;
290
+ success: boolean;
291
+ data?: Issue | IssueWithChildren;
292
+ error?: string;
293
+ }>>>;
294
+ /**
295
+ * 同步子单:从源父单复制子单到目标父单
296
+ * @param sourceParentId 源父单 ID
297
+ * @param targetParentId 目标父单 ID
298
+ * @param assignedToMail 新子单的指派人邮箱
299
+ * @param options 选项
300
+ */
301
+ syncChildIssues(token: string, host: string, project: string, sourceParentId: number, targetParentId: number, assignedToMail: string, options?: {
302
+ dryRun?: boolean;
303
+ depth?: number;
304
+ skipExisting?: boolean;
305
+ }): Promise<ApiResponse<SyncResult>>;
306
+ /**
307
+ * 计算所有子任务数量(递归)
308
+ */
309
+ private countAllChildren;
310
+ /**
311
+ * 收集所有任务名称(递归)
312
+ */
313
+ private collectAllTaskNames;
314
+ /**
315
+ * 递归同步子单
316
+ */
317
+ private syncChildrenRecursive;
318
+ /**
319
+ * 从 Markdown 批量创建子单
320
+ * @param parentId 父单 ID
321
+ * @param markdown Markdown 文本
322
+ * @param assignedToMail 指派人邮箱
323
+ * @param options 选项
324
+ */
325
+ batchCreateFromMarkdown(token: string, host: string, project: string, parentId: number, markdown: string, assignedToMail: string, options?: {
326
+ dryRun?: boolean;
327
+ interval?: number;
328
+ extraCustomFields?: Record<number, string>;
329
+ }): Promise<ApiResponse<BatchCreateResult>>;
330
+ /**
331
+ * 递归批量创建子单
332
+ */
333
+ private batchCreateRecursive;
334
+ }
335
+ declare const issueService: IssueService;
336
+
337
+ /**
338
+ * 工时条目服务
339
+ */
340
+ declare class TimeEntryService {
341
+ /**
342
+ * 查询工时条目
343
+ */
344
+ queryTimeEntries(params: TimeEntryQueryParams): Promise<ApiResponse<TimeEntry[]>>;
345
+ /**
346
+ * 获取工时条目选项(活动类型列表等)
347
+ * 使用 time_entry GET API
348
+ */
349
+ getTimeEntryOptions(token: string, host: string, project: string, issueId?: number): Promise<ApiResponse<unknown>>;
350
+ /**
351
+ * 创建工时条目
352
+ * 使用 time_entry API(非 save_time_entry)
353
+ */
354
+ createTimeEntry(params: Record<string, unknown>): Promise<ApiResponse<TimeEntry>>;
355
+ /**
356
+ * 更新工时条目
357
+ */
358
+ updateTimeEntry(params: Record<string, unknown>): Promise<ApiResponse<TimeEntry>>;
359
+ /**
360
+ * 删除工时条目
361
+ */
362
+ deleteTimeEntry(token: string, host: string, timeEntryId: number): Promise<ApiResponse<unknown>>;
363
+ }
364
+ declare const timeEntryService: TimeEntryService;
365
+
366
+ /**
367
+ * 用户服务
368
+ */
369
+ declare class UserService {
370
+ /**
371
+ * 测试连接 (通过获取项目列表来验证)
372
+ */
373
+ testConnection(token: string, host: string, project: string): Promise<ApiResponse<unknown>>;
374
+ /**
375
+ * 获取项目列表
376
+ */
377
+ getProjects(token: string, host: string): Promise<ApiResponse<Project[]>>;
378
+ /**
379
+ * 获取项目用户
380
+ */
381
+ getProjectUsers(token: string, host: string, project: string): Promise<ApiResponse<User[]>>;
382
+ /**
383
+ * 获取主机信息
384
+ */
385
+ getHostInfo(token: string, host: string): Promise<ApiResponse<unknown>>;
386
+ }
387
+ declare const userService: UserService;
388
+
389
+ /**
390
+ * 网易易协作 API 客户端
391
+ */
392
+ declare class ApiClient {
393
+ private static readonly BASE_URL;
394
+ private static readonly DEFAULT_TIMEOUT;
395
+ /**
396
+ * 发送 GET 请求
397
+ */
398
+ get<T>(endpoint: string, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
399
+ /**
400
+ * 发送 POST 请求
401
+ */
402
+ post<T>(endpoint: string, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
403
+ /**
404
+ * 处理错误
405
+ */
406
+ private handleError;
407
+ /**
408
+ * 构建查询字符串
409
+ */
410
+ private buildQueryString;
411
+ /**
412
+ * 构建表单数据
413
+ */
414
+ private buildFormData;
415
+ }
416
+ declare const apiClient: ApiClient;
417
+
418
+ /**
419
+ * 合并凭据(按优先级)
420
+ * 优先级: 命令行参数 > 环境变量 > profile 配置 > 默认配置
421
+ */
422
+ declare function resolveCredentials(options: {
423
+ token?: string;
424
+ host?: string;
425
+ project?: string;
426
+ profile?: string;
427
+ config?: string;
428
+ }): Partial<Credentials>;
429
+ /**
430
+ * 验证凭据是否完整
431
+ */
432
+ declare function validateCredentials(creds: Partial<Credentials>, requiredFields?: (keyof Credentials)[]): {
433
+ valid: boolean;
434
+ missing: string[];
435
+ };
436
+
437
+ /**
438
+ * 解析 PM 链接
439
+ * 支持两种格式:
440
+ * 1. 路径格式: https://a19.pm.netease.com/v6/issues/213112
441
+ * 2. 查询参数格式: https://a19.pm.netease.com/v6/issues?project_id=7&issue_id=254946
442
+ *
443
+ * @param url PM 链接
444
+ * @returns 解析结果,包含 host 和 issueId;如果解析失败返回 null
445
+ */
446
+ declare function parsePmLink(url: string): PmLinkInfo | null;
447
+ /**
448
+ * 检查字符串是否是 PM 链接
449
+ */
450
+ declare function isPmLink(str: string): boolean;
451
+ /**
452
+ * 从文本中提取所有 PM 链接
453
+ */
454
+ declare function extractPmLinks(text: string): PmLinkInfo[];
455
+
456
+ type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
457
+ /**
458
+ * 设置日志级别(供第三方调用时按需开启)
459
+ */
460
+ declare function setLogLevel(level: LogLevel): void;
461
+
462
+ export { ApiClient, type ApiResponse, type BatchCreateResult, type CliConfig, type Credentials, type Issue, IssueService, type IssueWithChildren, type LogLevel, type PmLinkInfo, type Project, type SyncResult, type TimeEntry, type TimeEntryQueryParams, TimeEntryService, type User, UserService, apiClient, extractPmLinks, isPmLink, issueService, parsePmLink, resolveCredentials, setLogLevel, timeEntryService, userService, validateCredentials };