@hangox/pm-cli 0.0.1 → 0.2.2
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/index.js +930 -137
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -334,51 +334,210 @@ function validateCredentials(creds, requiredFields = ["token", "host", "project"
|
|
|
334
334
|
|
|
335
335
|
// src/utils/output.ts
|
|
336
336
|
import { writeFileSync as writeFileSync2 } from "fs";
|
|
337
|
+
|
|
338
|
+
// src/utils/key-translator.ts
|
|
339
|
+
var KEY_MAP = {
|
|
340
|
+
// 问题 (Issue) 相关字段
|
|
341
|
+
id: "\u7F16\u53F7",
|
|
342
|
+
subject: "\u6807\u9898",
|
|
343
|
+
description: "\u63CF\u8FF0",
|
|
344
|
+
status: "\u72B6\u6001",
|
|
345
|
+
tracker: "\u8DDF\u8E2A\u5668",
|
|
346
|
+
priority: "\u4F18\u5148\u7EA7",
|
|
347
|
+
author: "\u521B\u5EFA\u8005",
|
|
348
|
+
assigned_to: "\u8D1F\u8D23\u4EBA",
|
|
349
|
+
project: "\u9879\u76EE",
|
|
350
|
+
parent: "\u7236\u5355",
|
|
351
|
+
parent_id: "\u7236\u5355\u7F16\u53F7",
|
|
352
|
+
parent_issue_id: "\u7236\u5355\u7F16\u53F7",
|
|
353
|
+
root_id: "\u6839\u5355\u7F16\u53F7",
|
|
354
|
+
children: "\u5B50\u5355\u5217\u8868",
|
|
355
|
+
level: "\u5C42\u7EA7",
|
|
356
|
+
fixed_version: "\u76EE\u6807\u7248\u672C",
|
|
357
|
+
version: "\u7248\u672C",
|
|
358
|
+
start_date: "\u5F00\u59CB\u65E5\u671F",
|
|
359
|
+
due_date: "\u622A\u6B62\u65E5\u671F",
|
|
360
|
+
estimated_hours: "\u9884\u4F30\u5DE5\u65F6",
|
|
361
|
+
spent_hours: "\u5DF2\u7528\u5DE5\u65F6",
|
|
362
|
+
done_ratio: "\u5B8C\u6210\u5EA6",
|
|
363
|
+
created_on: "\u521B\u5EFA\u65F6\u95F4",
|
|
364
|
+
updated_on: "\u66F4\u65B0\u65F6\u95F4",
|
|
365
|
+
closed_on: "\u5173\u95ED\u65F6\u95F4",
|
|
366
|
+
relations: "\u5173\u8054\u95EE\u9898",
|
|
367
|
+
custom_fields: "\u81EA\u5B9A\u4E49\u5B57\u6BB5",
|
|
368
|
+
attachments: "\u9644\u4EF6",
|
|
369
|
+
journals: "\u5386\u53F2\u8BB0\u5F55",
|
|
370
|
+
watchers: "\u8DDF\u8E2A\u8005",
|
|
371
|
+
watcher_user_ids: "\u8DDF\u8E2A\u8005\u7F16\u53F7",
|
|
372
|
+
// 通用对象字段
|
|
373
|
+
name: "\u540D\u79F0",
|
|
374
|
+
value: "\u503C",
|
|
375
|
+
mail: "\u90AE\u7BB1",
|
|
376
|
+
login: "\u767B\u5F55\u540D",
|
|
377
|
+
firstname: "\u540D",
|
|
378
|
+
lastname: "\u59D3",
|
|
379
|
+
identifier: "\u6807\u8BC6\u7B26",
|
|
380
|
+
is_public: "\u662F\u5426\u516C\u5F00",
|
|
381
|
+
// 工时 (TimeEntry) 相关字段
|
|
382
|
+
hours: "\u5DE5\u65F6",
|
|
383
|
+
spent_on: "\u5DE5\u65F6\u65E5\u671F",
|
|
384
|
+
activity: "\u6D3B\u52A8\u7C7B\u578B",
|
|
385
|
+
activity_id: "\u6D3B\u52A8\u7C7B\u578B\u7F16\u53F7",
|
|
386
|
+
comments: "\u5907\u6CE8",
|
|
387
|
+
time_entries: "\u5DE5\u65F6\u6761\u76EE",
|
|
388
|
+
time_entry: "\u5DE5\u65F6\u6761\u76EE",
|
|
389
|
+
total_count: "\u603B\u6570",
|
|
390
|
+
total_hours: "\u603B\u5DE5\u65F6",
|
|
391
|
+
// 用户 (User) 相关字段
|
|
392
|
+
user: "\u7528\u6237",
|
|
393
|
+
user_id: "\u7528\u6237\u7F16\u53F7",
|
|
394
|
+
author_mail: "\u4F5C\u8005\u90AE\u7BB1",
|
|
395
|
+
assigned_to_mail: "\u8D1F\u8D23\u4EBA\u90AE\u7BB1",
|
|
396
|
+
assigned_to_id: "\u8D1F\u8D23\u4EBA\u7F16\u53F7",
|
|
397
|
+
follows: "\u8DDF\u8FDBQA",
|
|
398
|
+
last_login_on: "\u6700\u540E\u767B\u5F55\u65F6\u95F4",
|
|
399
|
+
// API 响应字段
|
|
400
|
+
success: "\u6210\u529F",
|
|
401
|
+
data: "\u6570\u636E",
|
|
402
|
+
error: "\u9519\u8BEF",
|
|
403
|
+
message: "\u6D88\u606F",
|
|
404
|
+
msg: "\u6D88\u606F",
|
|
405
|
+
api_error_msg: "\u63A5\u53E3\u9519\u8BEF",
|
|
406
|
+
post_error_msg: "\u8BF7\u6C42\u9519\u8BEF",
|
|
407
|
+
output: "\u8F93\u51FA\u8DEF\u5F84",
|
|
408
|
+
issues: "\u95EE\u9898\u5217\u8868",
|
|
409
|
+
list: "\u5217\u8868",
|
|
410
|
+
total: "\u603B\u6570",
|
|
411
|
+
// 查询/统计相关字段
|
|
412
|
+
offset: "\u504F\u79FB\u91CF",
|
|
413
|
+
limit: "\u9650\u5236\u6570",
|
|
414
|
+
page: "\u9875\u7801",
|
|
415
|
+
per_page: "\u6BCF\u9875\u6570\u91CF",
|
|
416
|
+
period: "\u65F6\u95F4\u6BB5",
|
|
417
|
+
targetHours: "\u76EE\u6807\u5DE5\u65F6",
|
|
418
|
+
remainingHours: "\u5269\u4F59\u5DE5\u65F6",
|
|
419
|
+
isFilled: "\u662F\u5426\u586B\u6EE1",
|
|
420
|
+
projectBreakdown: "\u9879\u76EE\u5206\u5E03",
|
|
421
|
+
project_summary: "\u9879\u76EE\u6C47\u603B",
|
|
422
|
+
// 版本 (Version) 相关字段
|
|
423
|
+
effective_date: "\u6709\u6548\u65E5\u671F",
|
|
424
|
+
sharing: "\u5171\u4EAB\u8303\u56F4",
|
|
425
|
+
wiki_page_title: "Wiki\u9875\u9762",
|
|
426
|
+
// 自定义字段相关
|
|
427
|
+
custom_field: "\u81EA\u5B9A\u4E49\u5B57\u6BB5",
|
|
428
|
+
identify: "\u6807\u8BC6",
|
|
429
|
+
// 选项/配置相关
|
|
430
|
+
options: "\u9009\u9879",
|
|
431
|
+
activities: "\u6D3B\u52A8\u7C7B\u578B\u5217\u8868",
|
|
432
|
+
trackers: "\u8DDF\u8E2A\u5668\u5217\u8868",
|
|
433
|
+
statuses: "\u72B6\u6001\u5217\u8868",
|
|
434
|
+
priorities: "\u4F18\u5148\u7EA7\u5217\u8868",
|
|
435
|
+
versions: "\u7248\u672C\u5217\u8868",
|
|
436
|
+
users: "\u7528\u6237\u5217\u8868",
|
|
437
|
+
projects: "\u9879\u76EE\u5217\u8868",
|
|
438
|
+
// 其他字段
|
|
439
|
+
notes: "\u8BF4\u660E",
|
|
440
|
+
is_pending_version: "\u662F\u5426\u5F85\u5B9A\u7248\u672C",
|
|
441
|
+
count: "\u6570\u91CF",
|
|
442
|
+
category: "\u5206\u7C7B",
|
|
443
|
+
category_id: "\u5206\u7C7B\u7F16\u53F7"
|
|
444
|
+
};
|
|
445
|
+
var keyMappingEnabled = true;
|
|
446
|
+
function setKeyMappingEnabled(enabled) {
|
|
447
|
+
keyMappingEnabled = enabled;
|
|
448
|
+
}
|
|
449
|
+
function isKeyMappingEnabled() {
|
|
450
|
+
return keyMappingEnabled;
|
|
451
|
+
}
|
|
452
|
+
function collectKeysRecursive(obj, keys) {
|
|
453
|
+
if (obj === null || obj === void 0) return;
|
|
454
|
+
if (typeof obj !== "object") return;
|
|
455
|
+
if (Array.isArray(obj)) {
|
|
456
|
+
for (const item of obj) {
|
|
457
|
+
collectKeysRecursive(item, keys);
|
|
458
|
+
}
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
462
|
+
keys.add(key);
|
|
463
|
+
collectKeysRecursive(value, keys);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
function collectUsedKeys(obj) {
|
|
467
|
+
const keys = /* @__PURE__ */ new Set();
|
|
468
|
+
collectKeysRecursive(obj, keys);
|
|
469
|
+
return Array.from(keys);
|
|
470
|
+
}
|
|
471
|
+
function generateKeyMapping(obj) {
|
|
472
|
+
const usedKeys = collectUsedKeys(obj);
|
|
473
|
+
const mapping = {};
|
|
474
|
+
for (const key of usedKeys) {
|
|
475
|
+
if (KEY_MAP[key]) {
|
|
476
|
+
mapping[key] = KEY_MAP[key];
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return mapping;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// src/utils/output.ts
|
|
337
483
|
function generateOutputPath(command, identifier) {
|
|
338
484
|
const now = /* @__PURE__ */ new Date();
|
|
339
485
|
const timestamp = now.getFullYear().toString() + (now.getMonth() + 1).toString().padStart(2, "0") + now.getDate().toString().padStart(2, "0") + now.getHours().toString().padStart(2, "0") + now.getMinutes().toString().padStart(2, "0") + now.getSeconds().toString().padStart(2, "0");
|
|
340
486
|
return `/tmp/pm-cli_${command}_${identifier}_${timestamp}.json`;
|
|
341
487
|
}
|
|
342
|
-
function
|
|
488
|
+
function addKeyMappingToResult(result) {
|
|
489
|
+
if (!isKeyMappingEnabled()) {
|
|
490
|
+
return result;
|
|
491
|
+
}
|
|
492
|
+
const mapping = generateKeyMapping(result);
|
|
493
|
+
return {
|
|
494
|
+
...result,
|
|
495
|
+
_key_mapping: mapping
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
function outputToFile(data, filePath, pretty = false) {
|
|
343
499
|
const result = {
|
|
344
500
|
success: true,
|
|
345
501
|
data
|
|
346
502
|
};
|
|
347
|
-
|
|
503
|
+
const finalResult = addKeyMappingToResult(result);
|
|
504
|
+
writeFileSync2(filePath, JSON.stringify(finalResult, null, pretty ? 2 : 0), "utf-8");
|
|
348
505
|
return filePath;
|
|
349
506
|
}
|
|
350
507
|
function smartOutput(data, options) {
|
|
351
508
|
if (options.stdout) {
|
|
352
|
-
outputSuccess(data);
|
|
509
|
+
outputSuccess(data, options.pretty);
|
|
353
510
|
return void 0;
|
|
354
511
|
}
|
|
355
512
|
const filePath = options.output || generateOutputPath(options.command, options.identifier);
|
|
356
|
-
outputToFile(data, filePath);
|
|
357
|
-
console.log(JSON.stringify({ success: true, output: filePath }, null, 2));
|
|
513
|
+
outputToFile(data, filePath, options.pretty);
|
|
514
|
+
console.log(JSON.stringify({ success: true, output: filePath }, null, options.pretty ? 2 : 0));
|
|
358
515
|
return filePath;
|
|
359
516
|
}
|
|
360
|
-
function outputSuccess(data) {
|
|
517
|
+
function outputSuccess(data, pretty = false) {
|
|
361
518
|
const result = {
|
|
362
519
|
success: true,
|
|
363
520
|
data
|
|
364
521
|
};
|
|
365
|
-
|
|
522
|
+
const finalResult = addKeyMappingToResult(result);
|
|
523
|
+
console.log(JSON.stringify(finalResult, null, pretty ? 2 : 0));
|
|
366
524
|
}
|
|
367
|
-
function outputError(error) {
|
|
525
|
+
function outputError(error, pretty = false) {
|
|
368
526
|
const result = {
|
|
369
527
|
success: false,
|
|
370
528
|
error
|
|
371
529
|
};
|
|
372
|
-
|
|
530
|
+
const finalResult = addKeyMappingToResult(result);
|
|
531
|
+
console.log(JSON.stringify(finalResult, null, pretty ? 2 : 0));
|
|
373
532
|
}
|
|
374
533
|
|
|
375
534
|
// src/commands/test.ts
|
|
376
535
|
function createTestCommand() {
|
|
377
|
-
const testCmd = new Command("test").description("\u6D4B\u8BD5\u7F51\u6613\u6613\u534F\u4F5C\u8FDE\u63A5").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
|
|
536
|
+
const testCmd = new Command("test").description("\u6D4B\u8BD5\u7F51\u6613\u6613\u534F\u4F5C\u8FDE\u63A5").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
378
537
|
const creds = resolveCredentials(options);
|
|
379
538
|
const validation = validateCredentials(creds);
|
|
380
539
|
if (!validation.valid) {
|
|
381
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
540
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
382
541
|
process.exit(1);
|
|
383
542
|
}
|
|
384
543
|
const result = await userService.testConnection(
|
|
@@ -392,9 +551,9 @@ function createTestCommand() {
|
|
|
392
551
|
host: creds.host,
|
|
393
552
|
project: creds.project,
|
|
394
553
|
data: result.data
|
|
395
|
-
});
|
|
554
|
+
}, options.pretty);
|
|
396
555
|
} else {
|
|
397
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u8FDE\u63A5\u5931\u8D25");
|
|
556
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u8FDE\u63A5\u5931\u8D25", options.pretty);
|
|
398
557
|
process.exit(1);
|
|
399
558
|
}
|
|
400
559
|
});
|
|
@@ -405,40 +564,40 @@ function createTestCommand() {
|
|
|
405
564
|
import { Command as Command2 } from "commander";
|
|
406
565
|
function createConfigCommand() {
|
|
407
566
|
const configCmd = new Command2("config").description("\u914D\u7F6E\u7BA1\u7406");
|
|
408
|
-
configCmd.command("set <key> <value>").description("\u8BBE\u7F6E\u914D\u7F6E\u9879").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action((key, value, options) => {
|
|
567
|
+
configCmd.command("set <key> <value>").description("\u8BBE\u7F6E\u914D\u7F6E\u9879").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action((key, value, options) => {
|
|
409
568
|
setConfigValue(key, value, options.config);
|
|
410
569
|
outputSuccess({
|
|
411
570
|
message: `\u914D\u7F6E\u9879 ${key} \u5DF2\u8BBE\u7F6E`,
|
|
412
571
|
key,
|
|
413
572
|
value,
|
|
414
573
|
configPath: getConfigPath(options.config)
|
|
415
|
-
});
|
|
574
|
+
}, options.pretty);
|
|
416
575
|
});
|
|
417
|
-
configCmd.command("get <key>").description("\u83B7\u53D6\u914D\u7F6E\u9879").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action((key, options) => {
|
|
576
|
+
configCmd.command("get <key>").description("\u83B7\u53D6\u914D\u7F6E\u9879").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action((key, options) => {
|
|
418
577
|
const value = getConfigValue(key, options.config);
|
|
419
578
|
if (value !== void 0) {
|
|
420
|
-
outputSuccess({ key, value });
|
|
579
|
+
outputSuccess({ key, value }, options.pretty);
|
|
421
580
|
} else {
|
|
422
|
-
outputError(`\u914D\u7F6E\u9879 ${key} \u4E0D\u5B58\u5728
|
|
581
|
+
outputError(`\u914D\u7F6E\u9879 ${key} \u4E0D\u5B58\u5728`, options.pretty);
|
|
423
582
|
process.exit(1);
|
|
424
583
|
}
|
|
425
584
|
});
|
|
426
|
-
configCmd.command("list").description("\u5217\u51FA\u6240\u6709\u914D\u7F6E").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action((options) => {
|
|
585
|
+
configCmd.command("list").description("\u5217\u51FA\u6240\u6709\u914D\u7F6E").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action((options) => {
|
|
427
586
|
const config = readConfig(options.config);
|
|
428
587
|
outputSuccess({
|
|
429
588
|
configPath: getConfigPath(options.config),
|
|
430
589
|
config
|
|
431
|
-
});
|
|
590
|
+
}, options.pretty);
|
|
432
591
|
});
|
|
433
592
|
const profileCmd = new Command2("profile").description("Profile \u7BA1\u7406");
|
|
434
|
-
profileCmd.command("add <name>").description("\u6DFB\u52A0 profile").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--token <token>", "API Token").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(
|
|
593
|
+
profileCmd.command("add <name>").description("\u6DFB\u52A0 profile").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--token <token>", "API Token").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(
|
|
435
594
|
(name, options) => {
|
|
436
595
|
const profile = {};
|
|
437
596
|
if (options.host) profile.host = options.host;
|
|
438
597
|
if (options.project) profile.project = options.project;
|
|
439
598
|
if (options.token) profile.token = options.token;
|
|
440
599
|
if (Object.keys(profile).length === 0) {
|
|
441
|
-
outputError("\u8BF7\u81F3\u5C11\u6307\u5B9A\u4E00\u4E2A\u914D\u7F6E\u9879 (--host, --project, --token)");
|
|
600
|
+
outputError("\u8BF7\u81F3\u5C11\u6307\u5B9A\u4E00\u4E2A\u914D\u7F6E\u9879 (--host, --project, --token)", options.pretty);
|
|
442
601
|
process.exit(1);
|
|
443
602
|
}
|
|
444
603
|
setProfile(name, profile, options.config);
|
|
@@ -446,19 +605,19 @@ function createConfigCommand() {
|
|
|
446
605
|
message: `Profile ${name} \u5DF2\u6DFB\u52A0`,
|
|
447
606
|
name,
|
|
448
607
|
profile
|
|
449
|
-
});
|
|
608
|
+
}, options.pretty);
|
|
450
609
|
}
|
|
451
610
|
);
|
|
452
|
-
profileCmd.command("remove <name>").description("\u5220\u9664 profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action((name, options) => {
|
|
611
|
+
profileCmd.command("remove <name>").description("\u5220\u9664 profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action((name, options) => {
|
|
453
612
|
const removed = removeProfile(name, options.config);
|
|
454
613
|
if (removed) {
|
|
455
|
-
outputSuccess({ message: `Profile ${name} \u5DF2\u5220\u9664`, name });
|
|
614
|
+
outputSuccess({ message: `Profile ${name} \u5DF2\u5220\u9664`, name }, options.pretty);
|
|
456
615
|
} else {
|
|
457
|
-
outputError(`Profile ${name} \u4E0D\u5B58\u5728
|
|
616
|
+
outputError(`Profile ${name} \u4E0D\u5B58\u5728`, options.pretty);
|
|
458
617
|
process.exit(1);
|
|
459
618
|
}
|
|
460
619
|
});
|
|
461
|
-
profileCmd.command("list").description("\u5217\u51FA\u6240\u6709 profiles").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action((options) => {
|
|
620
|
+
profileCmd.command("list").description("\u5217\u51FA\u6240\u6709 profiles").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action((options) => {
|
|
462
621
|
const profiles = listProfiles(options.config);
|
|
463
622
|
const profileDetails = {};
|
|
464
623
|
for (const name of profiles) {
|
|
@@ -467,7 +626,7 @@ function createConfigCommand() {
|
|
|
467
626
|
outputSuccess({
|
|
468
627
|
profiles: profileDetails,
|
|
469
628
|
count: profiles.length
|
|
470
|
-
});
|
|
629
|
+
}, options.pretty);
|
|
471
630
|
});
|
|
472
631
|
configCmd.addCommand(profileCmd);
|
|
473
632
|
return configCmd;
|
|
@@ -477,6 +636,9 @@ function createConfigCommand() {
|
|
|
477
636
|
import { Command as Command3 } from "commander";
|
|
478
637
|
|
|
479
638
|
// src/services/issue-service.ts
|
|
639
|
+
function sleep(ms) {
|
|
640
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
641
|
+
}
|
|
480
642
|
var IssueService = class {
|
|
481
643
|
/**
|
|
482
644
|
* 获取问题详情
|
|
@@ -560,9 +722,15 @@ var IssueService = class {
|
|
|
560
722
|
return await apiClient.post("filter_query_v6", params);
|
|
561
723
|
}
|
|
562
724
|
/**
|
|
563
|
-
*
|
|
725
|
+
* 递归获取问题及其子单(包含完整详情)
|
|
564
726
|
* @param depth 递归深度,默认 10
|
|
565
727
|
* @param currentLevel 当前层级(内部使用)
|
|
728
|
+
*
|
|
729
|
+
* 实现逻辑:
|
|
730
|
+
* 1. 调用 getIssue(includeChildren=true) 获取当前问题详情
|
|
731
|
+
* 2. API 返回的 children 字段只包含简略信息(status 是字符串,缺少 assigned_to 等)
|
|
732
|
+
* 3. 从 children 中提取子单 ID,递归调用 getIssue 获取每个子单的完整详情
|
|
733
|
+
* 4. 用完整详情替换原始的简略 children
|
|
566
734
|
*/
|
|
567
735
|
async getIssueWithChildren(token, host, project, issueId, depth = 10, currentLevel2 = 0) {
|
|
568
736
|
logger_default.info("\u9012\u5F52\u83B7\u53D6\u95EE\u9898\u8BE6\u60C5", { host, project, issueId, depth, currentLevel: currentLevel2 });
|
|
@@ -575,15 +743,16 @@ var IssueService = class {
|
|
|
575
743
|
if (currentLevel2 >= depth) {
|
|
576
744
|
return { success: true, data: issue };
|
|
577
745
|
}
|
|
578
|
-
const
|
|
579
|
-
if (!
|
|
746
|
+
const rawChildren = issue.children;
|
|
747
|
+
if (!rawChildren || rawChildren.length === 0) {
|
|
580
748
|
return { success: true, data: issue };
|
|
581
749
|
}
|
|
582
|
-
const
|
|
583
|
-
if (
|
|
750
|
+
const directChildrenIds = rawChildren.filter((child) => child.id).map((child) => child.id);
|
|
751
|
+
if (directChildrenIds.length === 0) {
|
|
584
752
|
return { success: true, data: issue };
|
|
585
753
|
}
|
|
586
|
-
|
|
754
|
+
logger_default.info("\u83B7\u53D6\u5B50\u5355\u5B8C\u6574\u8BE6\u60C5", { parentId: issueId, childCount: directChildrenIds.length, level: currentLevel2 });
|
|
755
|
+
const childrenPromises = directChildrenIds.map(
|
|
587
756
|
(childId) => this.getIssueWithChildren(token, host, project, childId, depth, currentLevel2 + 1)
|
|
588
757
|
);
|
|
589
758
|
const childrenResults = await Promise.all(childrenPromises);
|
|
@@ -672,6 +841,285 @@ var IssueService = class {
|
|
|
672
841
|
}
|
|
673
842
|
return result;
|
|
674
843
|
}
|
|
844
|
+
/**
|
|
845
|
+
* 批量获取多个问题详情
|
|
846
|
+
* @param issueIds 问题 ID 数组
|
|
847
|
+
* @param options 可选参数
|
|
848
|
+
* @returns 返回所有问题的详情数组(包含成功和失败的结果)
|
|
849
|
+
*/
|
|
850
|
+
async getMultipleIssues(token, host, project, issueIds, options) {
|
|
851
|
+
const { includeChildren = false, includeRelations = false, depth = 0 } = options || {};
|
|
852
|
+
logger_default.info("\u6279\u91CF\u83B7\u53D6\u95EE\u9898\u8BE6\u60C5", { host, project, count: issueIds.length, issueIds });
|
|
853
|
+
const results = await Promise.all(
|
|
854
|
+
issueIds.map(async (issueId) => {
|
|
855
|
+
try {
|
|
856
|
+
let result;
|
|
857
|
+
if (includeChildren && depth > 0) {
|
|
858
|
+
result = await this.getIssueWithChildren(token, host, project, issueId, depth);
|
|
859
|
+
} else {
|
|
860
|
+
result = await this.getIssue(token, host, project, issueId, includeChildren, includeRelations);
|
|
861
|
+
}
|
|
862
|
+
if (result.success && result.data) {
|
|
863
|
+
return { id: issueId, success: true, data: result.data };
|
|
864
|
+
} else {
|
|
865
|
+
return {
|
|
866
|
+
id: issueId,
|
|
867
|
+
success: false,
|
|
868
|
+
error: result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u5931\u8D25"
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
} catch (error) {
|
|
872
|
+
return {
|
|
873
|
+
id: issueId,
|
|
874
|
+
success: false,
|
|
875
|
+
error: error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
})
|
|
879
|
+
);
|
|
880
|
+
const successCount = results.filter((r) => r.success).length;
|
|
881
|
+
const failCount = results.filter((r) => !r.success).length;
|
|
882
|
+
logger_default.info("\u6279\u91CF\u83B7\u53D6\u5B8C\u6210", { total: issueIds.length, success: successCount, fail: failCount });
|
|
883
|
+
return {
|
|
884
|
+
success: true,
|
|
885
|
+
data: results
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* 同步子单:从源父单复制子单到目标父单
|
|
890
|
+
* @param sourceParentId 源父单 ID
|
|
891
|
+
* @param targetParentId 目标父单 ID
|
|
892
|
+
* @param assignedToMail 新子单的指派人邮箱
|
|
893
|
+
* @param options 选项
|
|
894
|
+
*/
|
|
895
|
+
async syncChildIssues(token, host, project, sourceParentId, targetParentId, assignedToMail, options) {
|
|
896
|
+
const { dryRun = false, depth = 10, skipExisting = true } = options || {};
|
|
897
|
+
logger_default.info("\u5F00\u59CB\u540C\u6B65\u5B50\u5355", {
|
|
898
|
+
sourceParentId,
|
|
899
|
+
targetParentId,
|
|
900
|
+
assignedToMail,
|
|
901
|
+
dryRun,
|
|
902
|
+
depth,
|
|
903
|
+
skipExisting
|
|
904
|
+
});
|
|
905
|
+
const result = {
|
|
906
|
+
totalCreated: 0,
|
|
907
|
+
totalSkipped: 0,
|
|
908
|
+
totalFailed: 0,
|
|
909
|
+
created: [],
|
|
910
|
+
skipped: [],
|
|
911
|
+
failed: []
|
|
912
|
+
};
|
|
913
|
+
try {
|
|
914
|
+
logger_default.info("\u6B63\u5728\u83B7\u53D6\u6E90\u7236\u5355\u7684\u5B50\u5355\u6811\u5F62\u7ED3\u6784...");
|
|
915
|
+
const sourceResult = await this.getIssueWithChildren(token, host, project, sourceParentId, depth);
|
|
916
|
+
if (!sourceResult.success || !sourceResult.data) {
|
|
917
|
+
return {
|
|
918
|
+
success: false,
|
|
919
|
+
message: `\u83B7\u53D6\u6E90\u7236\u5355 #${sourceParentId} \u5931\u8D25: ${sourceResult.message || "\u672A\u77E5\u9519\u8BEF"}`
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
const sourceIssue = sourceResult.data;
|
|
923
|
+
logger_default.info("\u6B63\u5728\u83B7\u53D6\u76EE\u6807\u7236\u5355\u4FE1\u606F...");
|
|
924
|
+
const targetResult = await this.getIssue(token, host, project, targetParentId, false, false);
|
|
925
|
+
if (!targetResult.success || !targetResult.data) {
|
|
926
|
+
return {
|
|
927
|
+
success: false,
|
|
928
|
+
message: `\u83B7\u53D6\u76EE\u6807\u7236\u5355 #${targetParentId} \u5931\u8D25: ${targetResult.message || "\u672A\u77E5\u9519\u8BEF"}`
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
const targetParentInfo = targetResult.data;
|
|
932
|
+
let existingTaskNames = /* @__PURE__ */ new Set();
|
|
933
|
+
if (skipExisting) {
|
|
934
|
+
logger_default.info("\u6B63\u5728\u83B7\u53D6\u76EE\u6807\u7236\u5355\u5DF2\u6709\u7684\u5B50\u5355...");
|
|
935
|
+
const existingResult = await this.getIssueWithChildren(token, host, project, targetParentId, depth);
|
|
936
|
+
if (existingResult.success && existingResult.data) {
|
|
937
|
+
existingTaskNames = this.collectAllTaskNames(existingResult.data);
|
|
938
|
+
logger_default.info(`\u76EE\u6807\u7236\u5355\u5DF2\u6709 ${existingTaskNames.size} \u4E2A\u5B50\u5355`);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
logger_default.info("\u5F00\u59CB\u9012\u5F52\u540C\u6B65\u5B50\u5355...");
|
|
942
|
+
await this.syncChildrenRecursive(
|
|
943
|
+
token,
|
|
944
|
+
host,
|
|
945
|
+
project,
|
|
946
|
+
sourceIssue,
|
|
947
|
+
targetParentId,
|
|
948
|
+
targetParentInfo,
|
|
949
|
+
existingTaskNames,
|
|
950
|
+
assignedToMail,
|
|
951
|
+
dryRun,
|
|
952
|
+
result
|
|
953
|
+
);
|
|
954
|
+
logger_default.info("\u540C\u6B65\u5B8C\u6210", {
|
|
955
|
+
totalCreated: result.totalCreated,
|
|
956
|
+
totalSkipped: result.totalSkipped,
|
|
957
|
+
totalFailed: result.totalFailed
|
|
958
|
+
});
|
|
959
|
+
return { success: true, data: result };
|
|
960
|
+
} catch (error) {
|
|
961
|
+
logger_default.error("\u540C\u6B65\u5B50\u5355\u65F6\u53D1\u751F\u9519\u8BEF", error);
|
|
962
|
+
return {
|
|
963
|
+
success: false,
|
|
964
|
+
message: `\u540C\u6B65\u5B50\u5355\u65F6\u53D1\u751F\u9519\u8BEF: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* 收集所有任务名称(递归)
|
|
970
|
+
*/
|
|
971
|
+
collectAllTaskNames(issue) {
|
|
972
|
+
const names = /* @__PURE__ */ new Set();
|
|
973
|
+
if (issue.subject?.trim()) {
|
|
974
|
+
names.add(issue.subject.trim());
|
|
975
|
+
}
|
|
976
|
+
for (const child of issue.children || []) {
|
|
977
|
+
const childNames = this.collectAllTaskNames(child);
|
|
978
|
+
childNames.forEach((name) => names.add(name));
|
|
979
|
+
}
|
|
980
|
+
return names;
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* 递归同步子单
|
|
984
|
+
*/
|
|
985
|
+
async syncChildrenRecursive(token, host, project, sourceNode, targetParentId, targetParentInfo, existingTaskNames, assignedToMail, dryRun, result) {
|
|
986
|
+
for (const child of sourceNode.children || []) {
|
|
987
|
+
const taskName = child.subject?.trim() || "\u672A\u547D\u540D\u4EFB\u52A1";
|
|
988
|
+
const sourceId = child.id;
|
|
989
|
+
if (existingTaskNames.has(taskName)) {
|
|
990
|
+
logger_default.info(`\u23ED\uFE0F \u8DF3\u8FC7\u5DF2\u5B58\u5728\u7684\u4EFB\u52A1: ${taskName}`);
|
|
991
|
+
result.totalSkipped++;
|
|
992
|
+
result.skipped.push({
|
|
993
|
+
sourceId,
|
|
994
|
+
subject: taskName,
|
|
995
|
+
reason: "\u76EE\u6807\u7236\u5355\u4E0B\u5DF2\u5B58\u5728\u540C\u540D\u4EFB\u52A1"
|
|
996
|
+
});
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
const isLeafNode = !child.children || child.children.length === 0;
|
|
1000
|
+
const createParams = {
|
|
1001
|
+
token,
|
|
1002
|
+
host,
|
|
1003
|
+
project,
|
|
1004
|
+
parent_issue_id: targetParentId,
|
|
1005
|
+
subject: taskName,
|
|
1006
|
+
// 继承自目标父单
|
|
1007
|
+
tracker: targetParentInfo.tracker?.name,
|
|
1008
|
+
status: "\u65B0\u5EFA",
|
|
1009
|
+
// 覆盖指派人为"我"
|
|
1010
|
+
assigned_to_mail: assignedToMail,
|
|
1011
|
+
// 只有叶子节点才设置工时
|
|
1012
|
+
estimated_hours: isLeafNode ? child.estimated_hours : void 0,
|
|
1013
|
+
// 保留原任务的优先级
|
|
1014
|
+
priority_id: child.priority?.id
|
|
1015
|
+
};
|
|
1016
|
+
const targetWithVersion = targetParentInfo;
|
|
1017
|
+
if (targetWithVersion.fixed_version?.name) {
|
|
1018
|
+
createParams.version = targetWithVersion.fixed_version.name;
|
|
1019
|
+
}
|
|
1020
|
+
const targetWithCustomFields = targetParentInfo;
|
|
1021
|
+
if (targetWithCustomFields.custom_fields && targetWithCustomFields.custom_fields.length > 0) {
|
|
1022
|
+
const customFieldMap = {};
|
|
1023
|
+
const followsMails = [];
|
|
1024
|
+
for (const field of targetWithCustomFields.custom_fields) {
|
|
1025
|
+
if (field.value !== null && field.value !== void 0 && field.value !== "") {
|
|
1026
|
+
if (field.identify === "IssuesQCFollow") {
|
|
1027
|
+
const followsValue = field.value;
|
|
1028
|
+
if (Array.isArray(followsValue)) {
|
|
1029
|
+
for (const item of followsValue) {
|
|
1030
|
+
if (item.user?.mail) {
|
|
1031
|
+
followsMails.push(item.user.mail);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
} else {
|
|
1036
|
+
customFieldMap[field.id] = field.value;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
if (Object.keys(customFieldMap).length > 0) {
|
|
1041
|
+
createParams.custom_field = JSON.stringify(customFieldMap);
|
|
1042
|
+
}
|
|
1043
|
+
if (followsMails.length > 0) {
|
|
1044
|
+
createParams.follows = followsMails;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
logger_default.info(`\u6B63\u5728\u521B\u5EFA\u5B50\u4EFB\u52A1: ${taskName}`, { sourceId, targetParentId, isLeafNode });
|
|
1048
|
+
if (dryRun) {
|
|
1049
|
+
logger_default.info(`[\u6A21\u62DF] \u5C06\u521B\u5EFA\u5B50\u4EFB\u52A1: ${taskName}`);
|
|
1050
|
+
result.totalCreated++;
|
|
1051
|
+
result.created.push({
|
|
1052
|
+
sourceId,
|
|
1053
|
+
newId: 0,
|
|
1054
|
+
// 模拟模式没有真实 ID
|
|
1055
|
+
subject: taskName,
|
|
1056
|
+
parentId: targetParentId
|
|
1057
|
+
});
|
|
1058
|
+
if (child.children && child.children.length > 0) {
|
|
1059
|
+
await this.syncChildrenRecursive(
|
|
1060
|
+
token,
|
|
1061
|
+
host,
|
|
1062
|
+
project,
|
|
1063
|
+
child,
|
|
1064
|
+
0,
|
|
1065
|
+
// 模拟模式下没有真实的新 ID
|
|
1066
|
+
targetParentInfo,
|
|
1067
|
+
existingTaskNames,
|
|
1068
|
+
assignedToMail,
|
|
1069
|
+
dryRun,
|
|
1070
|
+
result
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
} else {
|
|
1074
|
+
try {
|
|
1075
|
+
const createResult = await this.createIssue(createParams);
|
|
1076
|
+
if (createResult.success && createResult.data) {
|
|
1077
|
+
const newId = createResult.data.id;
|
|
1078
|
+
logger_default.info(`\u2705 \u6210\u529F\u521B\u5EFA\u5B50\u4EFB\u52A1: ID=${newId}, \u6807\u9898=${taskName}`);
|
|
1079
|
+
result.totalCreated++;
|
|
1080
|
+
result.created.push({
|
|
1081
|
+
sourceId,
|
|
1082
|
+
newId,
|
|
1083
|
+
subject: taskName,
|
|
1084
|
+
parentId: targetParentId
|
|
1085
|
+
});
|
|
1086
|
+
if (child.children && child.children.length > 0) {
|
|
1087
|
+
logger_default.info(`\u{1F4C1} \u53D1\u73B0\u5B50\u4EFB\u52A1 ${newId} \u6709 ${child.children.length} \u4E2A\u5B50\u4EFB\u52A1\uFF0C\u7EE7\u7EED\u9012\u5F52\u521B\u5EFA...`);
|
|
1088
|
+
await this.syncChildrenRecursive(
|
|
1089
|
+
token,
|
|
1090
|
+
host,
|
|
1091
|
+
project,
|
|
1092
|
+
child,
|
|
1093
|
+
newId,
|
|
1094
|
+
targetParentInfo,
|
|
1095
|
+
existingTaskNames,
|
|
1096
|
+
assignedToMail,
|
|
1097
|
+
dryRun,
|
|
1098
|
+
result
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
} else {
|
|
1102
|
+
logger_default.error(`\u274C \u521B\u5EFA\u5B50\u4EFB\u52A1\u5931\u8D25: ${taskName}`, createResult.message || createResult.api_error_msg);
|
|
1103
|
+
result.totalFailed++;
|
|
1104
|
+
result.failed.push({
|
|
1105
|
+
sourceId,
|
|
1106
|
+
subject: taskName,
|
|
1107
|
+
error: createResult.message || createResult.api_error_msg || "\u521B\u5EFA\u5931\u8D25"
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
} catch (error) {
|
|
1111
|
+
logger_default.error(`\u274C \u521B\u5EFA\u5B50\u4EFB\u52A1\u65F6\u53D1\u751F\u9519\u8BEF: ${taskName}`, error);
|
|
1112
|
+
result.totalFailed++;
|
|
1113
|
+
result.failed.push({
|
|
1114
|
+
sourceId,
|
|
1115
|
+
subject: taskName,
|
|
1116
|
+
error: error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
await sleep(500);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
675
1123
|
};
|
|
676
1124
|
var issueService = new IssueService();
|
|
677
1125
|
|
|
@@ -703,13 +1151,13 @@ function parsePmLink(url) {
|
|
|
703
1151
|
// src/commands/issue/index.ts
|
|
704
1152
|
function createIssueCommand() {
|
|
705
1153
|
const issueCmd = new Command3("issue").description("\u95EE\u9898\u7BA1\u7406");
|
|
706
|
-
issueCmd.command("get [id]").description("\u83B7\u53D6\u95EE\u9898\u8BE6\u60C5\uFF08\u9ED8\u8BA4\u9012\u5F52\u83B7\u53D6\u5B50\u5355\uFF0C\u8F93\u51FA\u5230\u6587\u4EF6\uFF09").option("--url <url>", "PM \u94FE\u63A5").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--depth <depth>", "\u9012\u5F52\u83B7\u53D6\u5B50\u5355\u7684\u6DF1\u5EA6\uFF080 \u8868\u793A\u4E0D\u83B7\u53D6\u5B50\u5355\uFF09", "10").option("-o, --output <path>", "\u8F93\u51FA JSON \u5230\u6307\u5B9A\u6587\u4EF6\uFF08\u9ED8\u8BA4\u8F93\u51FA\u5230 /tmp\uFF09").option("--stdout", "\u5F3A\u5236\u8F93\u51FA\u5230\u63A7\u5236\u53F0\u800C\u975E\u6587\u4EF6").option("--include-relations", "\u5305\u542B\u5173\u8054\u95EE\u9898").action(async (id, options) => {
|
|
1154
|
+
issueCmd.command("get [id]").description("\u83B7\u53D6\u95EE\u9898\u8BE6\u60C5\uFF08\u9ED8\u8BA4\u9012\u5F52\u83B7\u53D6\u5B50\u5355\uFF0C\u8F93\u51FA\u5230\u6587\u4EF6\uFF09").option("--url <url>", "PM \u94FE\u63A5").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--depth <depth>", "\u9012\u5F52\u83B7\u53D6\u5B50\u5355\u7684\u6DF1\u5EA6\uFF080 \u8868\u793A\u4E0D\u83B7\u53D6\u5B50\u5355\uFF09", "10").option("-o, --output <path>", "\u8F93\u51FA JSON \u5230\u6307\u5B9A\u6587\u4EF6\uFF08\u9ED8\u8BA4\u8F93\u51FA\u5230 /tmp\uFF09").option("--stdout", "\u5F3A\u5236\u8F93\u51FA\u5230\u63A7\u5236\u53F0\u800C\u975E\u6587\u4EF6").option("--include-relations", "\u5305\u542B\u5173\u8054\u95EE\u9898").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (id, options) => {
|
|
707
1155
|
let issueId;
|
|
708
1156
|
let host;
|
|
709
1157
|
if (options.url) {
|
|
710
1158
|
const linkInfo = parsePmLink(options.url);
|
|
711
1159
|
if (!linkInfo) {
|
|
712
|
-
outputError("\u65E0\u6548\u7684 PM \u94FE\u63A5\u683C\u5F0F");
|
|
1160
|
+
outputError("\u65E0\u6548\u7684 PM \u94FE\u63A5\u683C\u5F0F", options.pretty);
|
|
713
1161
|
process.exit(1);
|
|
714
1162
|
}
|
|
715
1163
|
issueId = parseInt(linkInfo.issueId, 10);
|
|
@@ -718,11 +1166,11 @@ function createIssueCommand() {
|
|
|
718
1166
|
const cleanId = id.replace(/^#/, "");
|
|
719
1167
|
issueId = parseInt(cleanId, 10);
|
|
720
1168
|
if (isNaN(issueId)) {
|
|
721
|
-
outputError("\u65E0\u6548\u7684\u95EE\u9898 ID");
|
|
1169
|
+
outputError("\u65E0\u6548\u7684\u95EE\u9898 ID", options.pretty);
|
|
722
1170
|
process.exit(1);
|
|
723
1171
|
}
|
|
724
1172
|
} else {
|
|
725
|
-
outputError("\u8BF7\u63D0\u4F9B\u95EE\u9898 ID \u6216 --url \u53C2\u6570");
|
|
1173
|
+
outputError("\u8BF7\u63D0\u4F9B\u95EE\u9898 ID \u6216 --url \u53C2\u6570", options.pretty);
|
|
726
1174
|
process.exit(1);
|
|
727
1175
|
}
|
|
728
1176
|
const creds = resolveCredentials({
|
|
@@ -731,7 +1179,7 @@ function createIssueCommand() {
|
|
|
731
1179
|
});
|
|
732
1180
|
const validation = validateCredentials(creds, ["token", "host"]);
|
|
733
1181
|
if (!validation.valid) {
|
|
734
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
1182
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
735
1183
|
process.exit(1);
|
|
736
1184
|
}
|
|
737
1185
|
const depth = parseInt(options.depth, 10);
|
|
@@ -748,10 +1196,11 @@ function createIssueCommand() {
|
|
|
748
1196
|
stdout: options.stdout,
|
|
749
1197
|
output: options.output,
|
|
750
1198
|
command: "issue-get",
|
|
751
|
-
identifier: issueId.toString()
|
|
1199
|
+
identifier: issueId.toString(),
|
|
1200
|
+
pretty: options.pretty
|
|
752
1201
|
});
|
|
753
1202
|
} else {
|
|
754
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u95EE\u9898\u5931\u8D25");
|
|
1203
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u95EE\u9898\u5931\u8D25", options.pretty);
|
|
755
1204
|
process.exit(1);
|
|
756
1205
|
}
|
|
757
1206
|
} else {
|
|
@@ -768,19 +1217,20 @@ function createIssueCommand() {
|
|
|
768
1217
|
stdout: options.stdout,
|
|
769
1218
|
output: options.output,
|
|
770
1219
|
command: "issue-get",
|
|
771
|
-
identifier: issueId.toString()
|
|
1220
|
+
identifier: issueId.toString(),
|
|
1221
|
+
pretty: options.pretty
|
|
772
1222
|
});
|
|
773
1223
|
} else {
|
|
774
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u95EE\u9898\u5931\u8D25");
|
|
1224
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u95EE\u9898\u5931\u8D25", options.pretty);
|
|
775
1225
|
process.exit(1);
|
|
776
1226
|
}
|
|
777
1227
|
}
|
|
778
1228
|
});
|
|
779
|
-
issueCmd.command("create").description("\u521B\u5EFA\u95EE\u9898").requiredOption("--subject <subject>", "\u95EE\u9898\u6807\u9898").option("--description <description>", "\u95EE\u9898\u63CF\u8FF0").option("--tracker-id <id>", "\u8DDF\u8E2A\u5668 ID").option("--priority-id <id>", "\u4F18\u5148\u7EA7 ID").option("--assigned-to-id <id>", "\u6307\u6D3E\u4EBA ID").option("--parent-id <id>", "\u7236\u95EE\u9898 ID").option("--start-date <date>", "\u5F00\u59CB\u65E5\u671F").option("--due-date <date>", "\u622A\u6B62\u65E5\u671F").option("--estimated-hours <hours>", "\u9884\u4F30\u5DE5\u65F6").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
|
|
1229
|
+
issueCmd.command("create").description("\u521B\u5EFA\u95EE\u9898").requiredOption("--subject <subject>", "\u95EE\u9898\u6807\u9898").option("--description <description>", "\u95EE\u9898\u63CF\u8FF0").option("--tracker <tracker>", "\u8DDF\u8E2A\u5668\u540D\u79F0").option("--tracker-id <id>", "\u8DDF\u8E2A\u5668 ID").option("--priority-id <id>", "\u4F18\u5148\u7EA7 ID").option("--assigned-to-mail <email>", "\u6307\u6D3E\u4EBA\u90AE\u7BB1").option("--assigned-to-id <id>", "\u6307\u6D3E\u4EBA ID").option("--parent-id <id>", "\u7236\u95EE\u9898 ID\uFF08\u5B50\u5355\u4F1A\u7EE7\u627F\u7236\u5355\u7684 tracker\u3001version\u3001assigned_to \u7B49\u4FE1\u606F\uFF09").option("--version <version>", "\u76EE\u6807\u7248\u672C\u540D\u79F0").option("--start-date <date>", "\u5F00\u59CB\u65E5\u671F").option("--due-date <date>", "\u622A\u6B62\u65E5\u671F").option("--estimated-hours <hours>", "\u9884\u4F30\u5DE5\u65F6").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
780
1230
|
const creds = resolveCredentials(options);
|
|
781
1231
|
const validation = validateCredentials(creds);
|
|
782
1232
|
if (!validation.valid) {
|
|
783
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
1233
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
784
1234
|
process.exit(1);
|
|
785
1235
|
}
|
|
786
1236
|
const params = {
|
|
@@ -789,29 +1239,89 @@ function createIssueCommand() {
|
|
|
789
1239
|
project: creds.project,
|
|
790
1240
|
subject: options.subject
|
|
791
1241
|
};
|
|
1242
|
+
if (options.parentId) {
|
|
1243
|
+
const parentId = parseInt(options.parentId, 10);
|
|
1244
|
+
const parentResult = await issueService.getIssue(
|
|
1245
|
+
creds.token,
|
|
1246
|
+
creds.host,
|
|
1247
|
+
creds.project || "",
|
|
1248
|
+
parentId,
|
|
1249
|
+
false,
|
|
1250
|
+
false
|
|
1251
|
+
);
|
|
1252
|
+
if (parentResult.success && parentResult.data) {
|
|
1253
|
+
const parent = parentResult.data;
|
|
1254
|
+
if (!options.tracker && !options.trackerId && parent.tracker?.name) {
|
|
1255
|
+
params.tracker = parent.tracker.name;
|
|
1256
|
+
}
|
|
1257
|
+
if (!options.assignedToMail && !options.assignedToId && parent.assigned_to?.mail) {
|
|
1258
|
+
params.assigned_to_mail = parent.assigned_to.mail;
|
|
1259
|
+
}
|
|
1260
|
+
if (!options.version && parent.fixed_version?.name) {
|
|
1261
|
+
params.version = parent.fixed_version.name;
|
|
1262
|
+
}
|
|
1263
|
+
if (!options.priorityId && parent.priority?.id) {
|
|
1264
|
+
params.priority_id = parent.priority.id;
|
|
1265
|
+
}
|
|
1266
|
+
params.status = "\u65B0\u5EFA";
|
|
1267
|
+
if (parent.custom_fields && parent.custom_fields.length > 0) {
|
|
1268
|
+
const customFieldMap = {};
|
|
1269
|
+
const followsMails = [];
|
|
1270
|
+
for (const field of parent.custom_fields) {
|
|
1271
|
+
if (field.value !== null && field.value !== void 0 && field.value !== "") {
|
|
1272
|
+
const fieldWithIdentify = field;
|
|
1273
|
+
if (fieldWithIdentify.identify === "IssuesQCFollow") {
|
|
1274
|
+
const followsValue = field.value;
|
|
1275
|
+
if (Array.isArray(followsValue)) {
|
|
1276
|
+
for (const item of followsValue) {
|
|
1277
|
+
if (item.user?.mail) {
|
|
1278
|
+
followsMails.push(item.user.mail);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
} else {
|
|
1283
|
+
customFieldMap[field.id] = field.value;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
if (Object.keys(customFieldMap).length > 0) {
|
|
1288
|
+
params.custom_field = JSON.stringify(customFieldMap);
|
|
1289
|
+
}
|
|
1290
|
+
if (followsMails.length > 0) {
|
|
1291
|
+
params.follows = followsMails;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
} else {
|
|
1295
|
+
outputError(`\u65E0\u6CD5\u83B7\u53D6\u7236\u5355 #${parentId} \u7684\u4FE1\u606F: ${parentResult.message || "\u672A\u77E5\u9519\u8BEF"}`, options.pretty);
|
|
1296
|
+
process.exit(1);
|
|
1297
|
+
}
|
|
1298
|
+
params.parent_issue_id = parentId;
|
|
1299
|
+
}
|
|
792
1300
|
if (options.description) params.description = options.description;
|
|
1301
|
+
if (options.tracker) params.tracker = options.tracker;
|
|
793
1302
|
if (options.trackerId) params.tracker_id = parseInt(options.trackerId, 10);
|
|
794
1303
|
if (options.priorityId) params.priority_id = parseInt(options.priorityId, 10);
|
|
1304
|
+
if (options.assignedToMail) params.assigned_to_mail = options.assignedToMail;
|
|
795
1305
|
if (options.assignedToId) params.assigned_to_id = parseInt(options.assignedToId, 10);
|
|
796
|
-
if (options.
|
|
1306
|
+
if (options.version) params.version = options.version;
|
|
797
1307
|
if (options.startDate) params.start_date = options.startDate;
|
|
798
1308
|
if (options.dueDate) params.due_date = options.dueDate;
|
|
799
1309
|
if (options.estimatedHours) params.estimated_hours = parseFloat(options.estimatedHours);
|
|
800
1310
|
const result = await issueService.createIssue(params);
|
|
801
1311
|
if (result.success && result.data) {
|
|
802
|
-
outputSuccess(result.data);
|
|
1312
|
+
outputSuccess(result.data, options.pretty);
|
|
803
1313
|
} else {
|
|
804
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u521B\u5EFA\u95EE\u9898\u5931\u8D25");
|
|
1314
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u521B\u5EFA\u95EE\u9898\u5931\u8D25", options.pretty);
|
|
805
1315
|
process.exit(1);
|
|
806
1316
|
}
|
|
807
1317
|
});
|
|
808
|
-
issueCmd.command("update <id>").description("\u66F4\u65B0\u95EE\u9898").option("--subject <subject>", "\u95EE\u9898\u6807\u9898").option("--description <description>", "\u95EE\u9898\u63CF\u8FF0").option("--status <status>", "\u72B6\u6001\u540D\u79F0 (\u5982: \u65B0\u5EFA\u3001\u5F00\u53D1\u4E2D\u3001\u5DF2\u89E3\u51B3)").option("--tracker <tracker>", "\u8DDF\u8E2A\u6807\u7B7E\u540D\u79F0 (\u5982: BUG\u3001\u4EFB\u52A1)").option("--version <version>", "\u76EE\u6807\u7248\u672C\u540D\u79F0").option("--assigned-to-mail <email>", "\u6307\u6D3E\u4EBA\u90AE\u7BB1").option("--notes <notes>", "\u66F4\u65B0\u8BF4\u660E/\u5907\u6CE8").option("--start-date <date>", "\u5F00\u59CB\u65E5\u671F (\u683C\u5F0F: YYYY-MM-DD)").option("--due-date <date>", "\u622A\u6B62\u65E5\u671F (\u683C\u5F0F: YYYY-MM-DD)").option("--estimated-hours <hours>", "\u9884\u4F30\u5DE5\u65F6").option("--follows <email>", "\u8DDF\u8FDBQA\u90AE\u7BB1").option("--url <url>", "PM \u94FE\u63A5 (\u81EA\u52A8\u89E3\u6790 host \u548C issue_id)").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (id, options) => {
|
|
1318
|
+
issueCmd.command("update <id>").description("\u66F4\u65B0\u95EE\u9898").option("--subject <subject>", "\u95EE\u9898\u6807\u9898").option("--description <description>", "\u95EE\u9898\u63CF\u8FF0").option("--status <status>", "\u72B6\u6001\u540D\u79F0 (\u5982: \u65B0\u5EFA\u3001\u5F00\u53D1\u4E2D\u3001\u5DF2\u89E3\u51B3)").option("--tracker <tracker>", "\u8DDF\u8E2A\u6807\u7B7E\u540D\u79F0 (\u5982: BUG\u3001\u4EFB\u52A1)").option("--version <version>", "\u76EE\u6807\u7248\u672C\u540D\u79F0").option("--assigned-to-mail <email>", "\u6307\u6D3E\u4EBA\u90AE\u7BB1").option("--notes <notes>", "\u66F4\u65B0\u8BF4\u660E/\u5907\u6CE8").option("--start-date <date>", "\u5F00\u59CB\u65E5\u671F (\u683C\u5F0F: YYYY-MM-DD)").option("--due-date <date>", "\u622A\u6B62\u65E5\u671F (\u683C\u5F0F: YYYY-MM-DD)").option("--estimated-hours <hours>", "\u9884\u4F30\u5DE5\u65F6").option("--follows <email>", "\u8DDF\u8FDBQA\u90AE\u7BB1").option("--custom-field <json>", `\u81EA\u5B9A\u4E49\u5B57\u6BB5 (JSON\u683C\u5F0F\uFF0C\u5982: '{"123":"value"}'\uFF0C\u652F\u6301\u4F20\u5165\u5B57\u6BB5\u540D\u6216\u5B57\u6BB5ID)`).option("--url <url>", "PM \u94FE\u63A5 (\u81EA\u52A8\u89E3\u6790 host \u548C issue_id)").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (id, options) => {
|
|
809
1319
|
let issueId;
|
|
810
1320
|
let urlHost;
|
|
811
1321
|
if (options.url) {
|
|
812
1322
|
const linkInfo = parsePmLink(options.url);
|
|
813
1323
|
if (!linkInfo) {
|
|
814
|
-
outputError("\u65E0\u6548\u7684 PM \u94FE\u63A5\u683C\u5F0F");
|
|
1324
|
+
outputError("\u65E0\u6548\u7684 PM \u94FE\u63A5\u683C\u5F0F", options.pretty);
|
|
815
1325
|
process.exit(1);
|
|
816
1326
|
}
|
|
817
1327
|
issueId = parseInt(linkInfo.issueId, 10);
|
|
@@ -819,7 +1329,7 @@ function createIssueCommand() {
|
|
|
819
1329
|
} else {
|
|
820
1330
|
issueId = parseInt(id.replace(/^#/, ""), 10);
|
|
821
1331
|
if (isNaN(issueId)) {
|
|
822
|
-
outputError("\u65E0\u6548\u7684\u95EE\u9898 ID");
|
|
1332
|
+
outputError("\u65E0\u6548\u7684\u95EE\u9898 ID", options.pretty);
|
|
823
1333
|
process.exit(1);
|
|
824
1334
|
}
|
|
825
1335
|
}
|
|
@@ -829,7 +1339,7 @@ function createIssueCommand() {
|
|
|
829
1339
|
});
|
|
830
1340
|
const validation = validateCredentials(creds);
|
|
831
1341
|
if (!validation.valid) {
|
|
832
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
1342
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
833
1343
|
process.exit(1);
|
|
834
1344
|
}
|
|
835
1345
|
const params = {
|
|
@@ -849,19 +1359,66 @@ function createIssueCommand() {
|
|
|
849
1359
|
if (options.dueDate) params.due_date = options.dueDate;
|
|
850
1360
|
if (options.estimatedHours) params.estimated_hours = parseFloat(options.estimatedHours);
|
|
851
1361
|
if (options.follows) params.follows = options.follows;
|
|
1362
|
+
if (options.customField) {
|
|
1363
|
+
try {
|
|
1364
|
+
const customFieldData = JSON.parse(options.customField);
|
|
1365
|
+
const hasNonNumericKey = Object.keys(customFieldData).some((key) => isNaN(Number(key)));
|
|
1366
|
+
if (hasNonNumericKey) {
|
|
1367
|
+
const fieldOptionsResult = await issueService.getIssueFieldOptions(
|
|
1368
|
+
creds.token,
|
|
1369
|
+
creds.host,
|
|
1370
|
+
creds.project
|
|
1371
|
+
);
|
|
1372
|
+
if (fieldOptionsResult.success && fieldOptionsResult.data) {
|
|
1373
|
+
const fieldOptions = fieldOptionsResult.data;
|
|
1374
|
+
if (fieldOptions.data?.custom_fields) {
|
|
1375
|
+
const fieldMap = /* @__PURE__ */ new Map();
|
|
1376
|
+
for (const field of fieldOptions.data.custom_fields) {
|
|
1377
|
+
fieldMap.set(field.name, field.id);
|
|
1378
|
+
}
|
|
1379
|
+
const convertedData = {};
|
|
1380
|
+
for (const [key, value] of Object.entries(customFieldData)) {
|
|
1381
|
+
const fieldId = isNaN(Number(key)) ? fieldMap.get(key) : Number(key);
|
|
1382
|
+
if (fieldId !== void 0) {
|
|
1383
|
+
convertedData[fieldId] = value;
|
|
1384
|
+
} else {
|
|
1385
|
+
outputError(`\u672A\u627E\u5230\u81EA\u5B9A\u4E49\u5B57\u6BB5: ${key}`, options.pretty);
|
|
1386
|
+
process.exit(1);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
params.custom_field = JSON.stringify(convertedData);
|
|
1390
|
+
} else {
|
|
1391
|
+
outputError("\u65E0\u6CD5\u83B7\u53D6\u81EA\u5B9A\u4E49\u5B57\u6BB5\u5217\u8868", options.pretty);
|
|
1392
|
+
process.exit(1);
|
|
1393
|
+
}
|
|
1394
|
+
} else {
|
|
1395
|
+
outputError("\u83B7\u53D6\u81EA\u5B9A\u4E49\u5B57\u6BB5\u5217\u8868\u5931\u8D25", options.pretty);
|
|
1396
|
+
process.exit(1);
|
|
1397
|
+
}
|
|
1398
|
+
} else {
|
|
1399
|
+
params.custom_field = JSON.stringify(customFieldData);
|
|
1400
|
+
}
|
|
1401
|
+
} catch (error) {
|
|
1402
|
+
outputError(
|
|
1403
|
+
`\u81EA\u5B9A\u4E49\u5B57\u6BB5 JSON \u683C\u5F0F\u9519\u8BEF: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`,
|
|
1404
|
+
options.pretty
|
|
1405
|
+
);
|
|
1406
|
+
process.exit(1);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
852
1409
|
const result = await issueService.updateIssue(params);
|
|
853
1410
|
if (result.success && result.data) {
|
|
854
|
-
outputSuccess(result.data);
|
|
1411
|
+
outputSuccess(result.data, options.pretty);
|
|
855
1412
|
} else {
|
|
856
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u66F4\u65B0\u95EE\u9898\u5931\u8D25");
|
|
1413
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u66F4\u65B0\u95EE\u9898\u5931\u8D25", options.pretty);
|
|
857
1414
|
process.exit(1);
|
|
858
1415
|
}
|
|
859
1416
|
});
|
|
860
|
-
issueCmd.command("query").description("\u81EA\u5B9A\u4E49\u67E5\u8BE2").requiredOption("--query-id <id>", "\u67E5\u8BE2 ID").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236").option("--offset <offset>", "\u504F\u79FB\u91CF").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
|
|
1417
|
+
issueCmd.command("query").description("\u81EA\u5B9A\u4E49\u67E5\u8BE2").requiredOption("--query-id <id>", "\u67E5\u8BE2 ID").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236").option("--offset <offset>", "\u504F\u79FB\u91CF").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
861
1418
|
const creds = resolveCredentials(options);
|
|
862
1419
|
const validation = validateCredentials(creds);
|
|
863
1420
|
if (!validation.valid) {
|
|
864
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
1421
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
865
1422
|
process.exit(1);
|
|
866
1423
|
}
|
|
867
1424
|
const queryId = parseInt(options.queryId, 10);
|
|
@@ -876,17 +1433,17 @@ function createIssueCommand() {
|
|
|
876
1433
|
offset
|
|
877
1434
|
);
|
|
878
1435
|
if (result.success && result.data) {
|
|
879
|
-
outputSuccess(result.data);
|
|
1436
|
+
outputSuccess(result.data, options.pretty);
|
|
880
1437
|
} else {
|
|
881
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5931\u8D25");
|
|
1438
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5931\u8D25", options.pretty);
|
|
882
1439
|
process.exit(1);
|
|
883
1440
|
}
|
|
884
1441
|
});
|
|
885
|
-
issueCmd.command("filter").description("V6 \u8FC7\u6EE4\u5668\u67E5\u8BE2").option("--mode <mode>", "\u67E5\u8BE2\u6A21\u5F0F (normal/simple/advanced)", "normal").option("--status <status>", "\u72B6\u6001\u8FC7\u6EE4").option("--tracker <tracker>", "\u8DDF\u8E2A\u5668\u8FC7\u6EE4").option("--assigned-to <user>", "\u6307\u6D3E\u4EBA\u8FC7\u6EE4").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236").option("--offset <offset>", "\u504F\u79FB\u91CF").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
|
|
1442
|
+
issueCmd.command("filter").description("V6 \u8FC7\u6EE4\u5668\u67E5\u8BE2").option("--mode <mode>", "\u67E5\u8BE2\u6A21\u5F0F (normal/simple/advanced)", "normal").option("--status <status>", "\u72B6\u6001\u8FC7\u6EE4").option("--tracker <tracker>", "\u8DDF\u8E2A\u5668\u8FC7\u6EE4").option("--assigned-to <user>", "\u6307\u6D3E\u4EBA\u8FC7\u6EE4").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236").option("--offset <offset>", "\u504F\u79FB\u91CF").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
886
1443
|
const creds = resolveCredentials(options);
|
|
887
1444
|
const validation = validateCredentials(creds);
|
|
888
1445
|
if (!validation.valid) {
|
|
889
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
1446
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
890
1447
|
process.exit(1);
|
|
891
1448
|
}
|
|
892
1449
|
const filterParams = {};
|
|
@@ -903,17 +1460,17 @@ function createIssueCommand() {
|
|
|
903
1460
|
filterParams
|
|
904
1461
|
);
|
|
905
1462
|
if (result.success && result.data) {
|
|
906
|
-
outputSuccess(result.data);
|
|
1463
|
+
outputSuccess(result.data, options.pretty);
|
|
907
1464
|
} else {
|
|
908
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5931\u8D25");
|
|
1465
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5931\u8D25", options.pretty);
|
|
909
1466
|
process.exit(1);
|
|
910
1467
|
}
|
|
911
1468
|
});
|
|
912
|
-
issueCmd.command("field-options").description("\u83B7\u53D6\u95EE\u9898\u5B57\u6BB5\u9009\u9879").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
|
|
1469
|
+
issueCmd.command("field-options").description("\u83B7\u53D6\u95EE\u9898\u5B57\u6BB5\u9009\u9879").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
913
1470
|
const creds = resolveCredentials(options);
|
|
914
1471
|
const validation = validateCredentials(creds);
|
|
915
1472
|
if (!validation.valid) {
|
|
916
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
1473
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
917
1474
|
process.exit(1);
|
|
918
1475
|
}
|
|
919
1476
|
const result = await issueService.getIssueFieldOptions(
|
|
@@ -922,19 +1479,19 @@ function createIssueCommand() {
|
|
|
922
1479
|
creds.project
|
|
923
1480
|
);
|
|
924
1481
|
if (result.success) {
|
|
925
|
-
outputSuccess(result.data);
|
|
1482
|
+
outputSuccess(result.data, options.pretty);
|
|
926
1483
|
} else {
|
|
927
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u5B57\u6BB5\u9009\u9879\u5931\u8D25");
|
|
1484
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u5B57\u6BB5\u9009\u9879\u5931\u8D25", options.pretty);
|
|
928
1485
|
process.exit(1);
|
|
929
1486
|
}
|
|
930
1487
|
});
|
|
931
|
-
issueCmd.command("children <id>").description("\u67E5\u8BE2\u5B50\u4EFB\u52A1\uFF08\u652F\u6301\u6309\u8D1F\u8D23\u4EBA\u8FC7\u6EE4\uFF09").option("--url <url>", "PM \u94FE\u63A5").option("--assigned-to <name>", "\u8D1F\u8D23\u4EBA\u59D3\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF09").option("--assigned-to-id <id>", "\u8D1F\u8D23\u4EBA ID").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236", "100").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (id, options) => {
|
|
1488
|
+
issueCmd.command("children <id>").description("\u67E5\u8BE2\u5B50\u4EFB\u52A1\uFF08\u652F\u6301\u6309\u8D1F\u8D23\u4EBA\u8FC7\u6EE4\uFF09").option("--url <url>", "PM \u94FE\u63A5").option("--assigned-to <name>", "\u8D1F\u8D23\u4EBA\u59D3\u540D\uFF08\u652F\u6301\u6A21\u7CCA\u5339\u914D\uFF09").option("--assigned-to-id <id>", "\u8D1F\u8D23\u4EBA ID").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236", "100").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (id, options) => {
|
|
932
1489
|
let issueId;
|
|
933
1490
|
let host;
|
|
934
1491
|
if (options.url) {
|
|
935
1492
|
const linkInfo = parsePmLink(options.url);
|
|
936
1493
|
if (!linkInfo) {
|
|
937
|
-
outputError("\u65E0\u6548\u7684 PM \u94FE\u63A5\u683C\u5F0F");
|
|
1494
|
+
outputError("\u65E0\u6548\u7684 PM \u94FE\u63A5\u683C\u5F0F", options.pretty);
|
|
938
1495
|
process.exit(1);
|
|
939
1496
|
}
|
|
940
1497
|
issueId = parseInt(linkInfo.issueId, 10);
|
|
@@ -943,7 +1500,7 @@ function createIssueCommand() {
|
|
|
943
1500
|
const cleanId = id.replace(/^#/, "");
|
|
944
1501
|
issueId = parseInt(cleanId, 10);
|
|
945
1502
|
if (isNaN(issueId)) {
|
|
946
|
-
outputError("\u65E0\u6548\u7684\u95EE\u9898 ID");
|
|
1503
|
+
outputError("\u65E0\u6548\u7684\u95EE\u9898 ID", options.pretty);
|
|
947
1504
|
process.exit(1);
|
|
948
1505
|
}
|
|
949
1506
|
}
|
|
@@ -953,7 +1510,7 @@ function createIssueCommand() {
|
|
|
953
1510
|
});
|
|
954
1511
|
const validation = validateCredentials(creds);
|
|
955
1512
|
if (!validation.valid) {
|
|
956
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
1513
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
957
1514
|
process.exit(1);
|
|
958
1515
|
}
|
|
959
1516
|
let assignedToId;
|
|
@@ -972,11 +1529,11 @@ function createIssueCommand() {
|
|
|
972
1529
|
if (matchedUser) {
|
|
973
1530
|
assignedToId = matchedUser.id;
|
|
974
1531
|
} else {
|
|
975
|
-
outputError(`\u672A\u627E\u5230\u5339\u914D\u7684\u7528\u6237: ${options.assignedTo}
|
|
1532
|
+
outputError(`\u672A\u627E\u5230\u5339\u914D\u7684\u7528\u6237: ${options.assignedTo}`, options.pretty);
|
|
976
1533
|
process.exit(1);
|
|
977
1534
|
}
|
|
978
1535
|
} else {
|
|
979
|
-
outputError("\u83B7\u53D6\u7528\u6237\u5217\u8868\u5931\u8D25");
|
|
1536
|
+
outputError("\u83B7\u53D6\u7528\u6237\u5217\u8868\u5931\u8D25", options.pretty);
|
|
980
1537
|
process.exit(1);
|
|
981
1538
|
}
|
|
982
1539
|
} else if (options.assignedToId) {
|
|
@@ -997,12 +1554,174 @@ function createIssueCommand() {
|
|
|
997
1554
|
outputSuccess({
|
|
998
1555
|
total: data.data.list.length,
|
|
999
1556
|
issues: data.data.list
|
|
1000
|
-
});
|
|
1557
|
+
}, options.pretty);
|
|
1001
1558
|
} else {
|
|
1002
|
-
outputSuccess(result.data);
|
|
1559
|
+
outputSuccess(result.data, options.pretty);
|
|
1560
|
+
}
|
|
1561
|
+
} else {
|
|
1562
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5B50\u4EFB\u52A1\u5931\u8D25", options.pretty);
|
|
1563
|
+
process.exit(1);
|
|
1564
|
+
}
|
|
1565
|
+
});
|
|
1566
|
+
issueCmd.command("mget [ids...]").description("\u6279\u91CF\u83B7\u53D6\u591A\u4E2A\u95EE\u9898\u8BE6\u60C5").option("--ids <ids>", "\u9017\u53F7\u5206\u9694\u7684\u95EE\u9898 ID \u5217\u8868").option("--depth <depth>", "\u9012\u5F52\u83B7\u53D6\u5B50\u5355\u7684\u6DF1\u5EA6\uFF080 \u8868\u793A\u4E0D\u83B7\u53D6\u5B50\u5355\uFF09", "0").option("--include-relations", "\u5305\u542B\u5173\u8054\u95EE\u9898").option("-o, --output <path>", "\u8F93\u51FA JSON \u5230\u6307\u5B9A\u6587\u4EF6\uFF08\u9ED8\u8BA4\u8F93\u51FA\u5230 /tmp\uFF09").option("--stdout", "\u5F3A\u5236\u8F93\u51FA\u5230\u63A7\u5236\u53F0\u800C\u975E\u6587\u4EF6").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (ids, options) => {
|
|
1567
|
+
let issueIds = [];
|
|
1568
|
+
if (ids && ids.length > 0) {
|
|
1569
|
+
const parsedIds = ids.map((id) => parseInt(id.replace(/^#/, ""), 10)).filter((id) => !isNaN(id));
|
|
1570
|
+
issueIds.push(...parsedIds);
|
|
1571
|
+
}
|
|
1572
|
+
if (options.ids) {
|
|
1573
|
+
const idsFromOption = options.ids.split(",").map((id) => parseInt(id.trim().replace(/^#/, ""), 10)).filter((id) => !isNaN(id));
|
|
1574
|
+
issueIds.push(...idsFromOption);
|
|
1575
|
+
}
|
|
1576
|
+
issueIds = [...new Set(issueIds)];
|
|
1577
|
+
if (issueIds.length === 0) {
|
|
1578
|
+
outputError("\u8BF7\u63D0\u4F9B\u81F3\u5C11\u4E00\u4E2A\u95EE\u9898 ID", options.pretty);
|
|
1579
|
+
process.exit(1);
|
|
1580
|
+
}
|
|
1581
|
+
const creds = resolveCredentials(options);
|
|
1582
|
+
const validation = validateCredentials(creds, ["token", "host"]);
|
|
1583
|
+
if (!validation.valid) {
|
|
1584
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
1585
|
+
process.exit(1);
|
|
1586
|
+
}
|
|
1587
|
+
const depth = parseInt(options.depth, 10);
|
|
1588
|
+
const includeChildren = depth > 0;
|
|
1589
|
+
const includeRelations = options.includeRelations;
|
|
1590
|
+
const result = await issueService.getMultipleIssues(
|
|
1591
|
+
creds.token,
|
|
1592
|
+
creds.host,
|
|
1593
|
+
creds.project || "",
|
|
1594
|
+
issueIds,
|
|
1595
|
+
{ includeChildren, includeRelations, depth }
|
|
1596
|
+
);
|
|
1597
|
+
if (result.success && result.data) {
|
|
1598
|
+
const successItems = result.data.filter((item) => item.success);
|
|
1599
|
+
const failedItems = result.data.filter((item) => !item.success);
|
|
1600
|
+
const output = {
|
|
1601
|
+
success: true,
|
|
1602
|
+
summary: {
|
|
1603
|
+
total: issueIds.length,
|
|
1604
|
+
success: successItems.length,
|
|
1605
|
+
failed: failedItems.length
|
|
1606
|
+
},
|
|
1607
|
+
issues: successItems.map((item) => item.data),
|
|
1608
|
+
errors: failedItems.length > 0 ? failedItems.map((item) => ({ id: item.id, error: item.error })) : void 0
|
|
1609
|
+
};
|
|
1610
|
+
smartOutput(output, {
|
|
1611
|
+
stdout: options.stdout,
|
|
1612
|
+
output: options.output,
|
|
1613
|
+
command: "issue-mget",
|
|
1614
|
+
identifier: issueIds.join("_"),
|
|
1615
|
+
pretty: options.pretty
|
|
1616
|
+
});
|
|
1617
|
+
} else {
|
|
1618
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u6279\u91CF\u83B7\u53D6\u95EE\u9898\u5931\u8D25", options.pretty);
|
|
1619
|
+
process.exit(1);
|
|
1620
|
+
}
|
|
1621
|
+
});
|
|
1622
|
+
issueCmd.command("sync").description("\u540C\u6B65\u5B50\u5355\uFF1A\u4ECE\u6E90\u7236\u5355\u590D\u5236\u5B50\u5355\u5230\u76EE\u6807\u7236\u5355").option("--from <id>", "\u6E90\u7236\u5355 ID").option("--to <id>", "\u76EE\u6807\u7236\u5355 ID").option("--from-url <url>", "\u6E90\u7236\u5355 PM \u94FE\u63A5").option("--to-url <url>", "\u76EE\u6807\u7236\u5355 PM \u94FE\u63A5").option("--assigned-to-mail <email>", "\u6307\u6D3E\u4EBA\u90AE\u7BB1\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u7684 userMail\uFF09").option("--depth <depth>", "\u9012\u5F52\u6DF1\u5EA6", "10").option("--dry-run", "\u6A21\u62DF\u8FD0\u884C\uFF0C\u4E0D\u5B9E\u9645\u521B\u5EFA").option("--no-skip-existing", "\u4E0D\u8DF3\u8FC7\u540C\u540D\u4EFB\u52A1").option("-o, --output <path>", "\u8F93\u51FA JSON \u5230\u6307\u5B9A\u6587\u4EF6").option("--stdout", "\u5F3A\u5236\u8F93\u51FA\u5230\u63A7\u5236\u53F0\u800C\u975E\u6587\u4EF6").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
1623
|
+
let sourceId;
|
|
1624
|
+
let targetId;
|
|
1625
|
+
let sourceHost;
|
|
1626
|
+
let targetHost;
|
|
1627
|
+
if (options.fromUrl) {
|
|
1628
|
+
const linkInfo = parsePmLink(options.fromUrl);
|
|
1629
|
+
if (!linkInfo) {
|
|
1630
|
+
outputError("\u65E0\u6548\u7684\u6E90\u7236\u5355 PM \u94FE\u63A5\u683C\u5F0F", options.pretty);
|
|
1631
|
+
process.exit(1);
|
|
1632
|
+
}
|
|
1633
|
+
sourceId = parseInt(linkInfo.issueId, 10);
|
|
1634
|
+
sourceHost = linkInfo.host;
|
|
1635
|
+
} else if (options.from) {
|
|
1636
|
+
const cleanId = options.from.replace(/^#/, "");
|
|
1637
|
+
sourceId = parseInt(cleanId, 10);
|
|
1638
|
+
if (isNaN(sourceId)) {
|
|
1639
|
+
outputError("\u65E0\u6548\u7684\u6E90\u7236\u5355 ID", options.pretty);
|
|
1640
|
+
process.exit(1);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
if (options.toUrl) {
|
|
1644
|
+
const linkInfo = parsePmLink(options.toUrl);
|
|
1645
|
+
if (!linkInfo) {
|
|
1646
|
+
outputError("\u65E0\u6548\u7684\u76EE\u6807\u7236\u5355 PM \u94FE\u63A5\u683C\u5F0F", options.pretty);
|
|
1647
|
+
process.exit(1);
|
|
1648
|
+
}
|
|
1649
|
+
targetId = parseInt(linkInfo.issueId, 10);
|
|
1650
|
+
targetHost = linkInfo.host;
|
|
1651
|
+
} else if (options.to) {
|
|
1652
|
+
const cleanId = options.to.replace(/^#/, "");
|
|
1653
|
+
targetId = parseInt(cleanId, 10);
|
|
1654
|
+
if (isNaN(targetId)) {
|
|
1655
|
+
outputError("\u65E0\u6548\u7684\u76EE\u6807\u7236\u5355 ID", options.pretty);
|
|
1656
|
+
process.exit(1);
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
if (!sourceId) {
|
|
1660
|
+
outputError("\u8BF7\u63D0\u4F9B\u6E90\u7236\u5355 ID\uFF08--from\uFF09\u6216 PM \u94FE\u63A5\uFF08--from-url\uFF09", options.pretty);
|
|
1661
|
+
process.exit(1);
|
|
1662
|
+
}
|
|
1663
|
+
if (!targetId) {
|
|
1664
|
+
outputError("\u8BF7\u63D0\u4F9B\u76EE\u6807\u7236\u5355 ID\uFF08--to\uFF09\u6216 PM \u94FE\u63A5\uFF08--to-url\uFF09", options.pretty);
|
|
1665
|
+
process.exit(1);
|
|
1666
|
+
}
|
|
1667
|
+
const creds = resolveCredentials({
|
|
1668
|
+
...options,
|
|
1669
|
+
host: sourceHost || targetHost || options.host
|
|
1670
|
+
});
|
|
1671
|
+
const validation = validateCredentials(creds, ["token", "host"]);
|
|
1672
|
+
if (!validation.valid) {
|
|
1673
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
1674
|
+
process.exit(1);
|
|
1675
|
+
}
|
|
1676
|
+
let assignedToMail = options.assignedToMail;
|
|
1677
|
+
if (!assignedToMail) {
|
|
1678
|
+
const config = readConfig(options.config);
|
|
1679
|
+
assignedToMail = config.default?.userMail;
|
|
1680
|
+
if (options.profile && config.profiles[options.profile]?.userMail) {
|
|
1681
|
+
assignedToMail = config.profiles[options.profile].userMail;
|
|
1003
1682
|
}
|
|
1683
|
+
}
|
|
1684
|
+
if (!assignedToMail) {
|
|
1685
|
+
outputError("\u8BF7\u63D0\u4F9B\u6307\u6D3E\u4EBA\u90AE\u7BB1\uFF08--assigned-to-mail\uFF09\u6216\u914D\u7F6E userMail\uFF08pm-cli config set user-mail xxx\uFF09", options.pretty);
|
|
1686
|
+
process.exit(1);
|
|
1687
|
+
}
|
|
1688
|
+
const depth = parseInt(options.depth, 10);
|
|
1689
|
+
const dryRun = options.dryRun || false;
|
|
1690
|
+
const skipExisting = options.skipExisting !== false;
|
|
1691
|
+
const result = await issueService.syncChildIssues(
|
|
1692
|
+
creds.token,
|
|
1693
|
+
creds.host,
|
|
1694
|
+
creds.project || "",
|
|
1695
|
+
sourceId,
|
|
1696
|
+
targetId,
|
|
1697
|
+
assignedToMail,
|
|
1698
|
+
{ dryRun, depth, skipExisting }
|
|
1699
|
+
);
|
|
1700
|
+
if (result.success && result.data) {
|
|
1701
|
+
const output = {
|
|
1702
|
+
success: true,
|
|
1703
|
+
dryRun,
|
|
1704
|
+
sourceParentId: sourceId,
|
|
1705
|
+
targetParentId: targetId,
|
|
1706
|
+
assignedToMail,
|
|
1707
|
+
summary: {
|
|
1708
|
+
totalCreated: result.data.totalCreated,
|
|
1709
|
+
totalSkipped: result.data.totalSkipped,
|
|
1710
|
+
totalFailed: result.data.totalFailed
|
|
1711
|
+
},
|
|
1712
|
+
created: result.data.created,
|
|
1713
|
+
skipped: result.data.skipped.length > 0 ? result.data.skipped : void 0,
|
|
1714
|
+
failed: result.data.failed.length > 0 ? result.data.failed : void 0
|
|
1715
|
+
};
|
|
1716
|
+
smartOutput(output, {
|
|
1717
|
+
stdout: options.stdout,
|
|
1718
|
+
output: options.output,
|
|
1719
|
+
command: "issue-sync",
|
|
1720
|
+
identifier: `${sourceId}_to_${targetId}`,
|
|
1721
|
+
pretty: options.pretty
|
|
1722
|
+
});
|
|
1004
1723
|
} else {
|
|
1005
|
-
outputError(result.message || result.msg || result.api_error_msg || "\
|
|
1724
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u540C\u6B65\u5B50\u5355\u5931\u8D25", options.pretty);
|
|
1006
1725
|
process.exit(1);
|
|
1007
1726
|
}
|
|
1008
1727
|
});
|
|
@@ -1036,18 +1755,22 @@ var TimeEntryService = class {
|
|
|
1036
1755
|
return await apiClient.get("query_time_entries", requestParams);
|
|
1037
1756
|
}
|
|
1038
1757
|
/**
|
|
1039
|
-
*
|
|
1758
|
+
* 获取工时条目选项(活动类型列表等)
|
|
1759
|
+
* 使用 time_entry GET API
|
|
1040
1760
|
*/
|
|
1041
|
-
async getTimeEntryOptions(token, host, project) {
|
|
1042
|
-
logger_default.info("\u83B7\u53D6\u5DE5\u65F6\u6761\u76EE\u9009\u9879", { host, project });
|
|
1043
|
-
|
|
1761
|
+
async getTimeEntryOptions(token, host, project, issueId) {
|
|
1762
|
+
logger_default.info("\u83B7\u53D6\u5DE5\u65F6\u6761\u76EE\u9009\u9879", { host, project, issueId });
|
|
1763
|
+
const params = { token, host, project };
|
|
1764
|
+
if (issueId) params.issue_id = issueId;
|
|
1765
|
+
return await apiClient.get("time_entry", params);
|
|
1044
1766
|
}
|
|
1045
1767
|
/**
|
|
1046
1768
|
* 创建工时条目
|
|
1769
|
+
* 使用 time_entry API(非 save_time_entry)
|
|
1047
1770
|
*/
|
|
1048
1771
|
async createTimeEntry(params) {
|
|
1049
1772
|
logger_default.info("\u521B\u5EFA\u5DE5\u65F6\u6761\u76EE", { params });
|
|
1050
|
-
return await apiClient.post("
|
|
1773
|
+
return await apiClient.post("time_entry", params);
|
|
1051
1774
|
}
|
|
1052
1775
|
/**
|
|
1053
1776
|
* 更新工时条目
|
|
@@ -1059,13 +1782,13 @@ var TimeEntryService = class {
|
|
|
1059
1782
|
/**
|
|
1060
1783
|
* 删除工时条目
|
|
1061
1784
|
*/
|
|
1062
|
-
async deleteTimeEntry(token, host,
|
|
1063
|
-
logger_default.info("\u5220\u9664\u5DE5\u65F6\u6761\u76EE", { host,
|
|
1785
|
+
async deleteTimeEntry(token, host, timeEntryId) {
|
|
1786
|
+
logger_default.info("\u5220\u9664\u5DE5\u65F6\u6761\u76EE", { host, timeEntryId });
|
|
1064
1787
|
return await apiClient.get("delete_time_entry", {
|
|
1065
1788
|
token,
|
|
1066
1789
|
host,
|
|
1067
|
-
|
|
1068
|
-
time_entry_id
|
|
1790
|
+
id: timeEntryId
|
|
1791
|
+
// 使用 id 参数,非 time_entry_id
|
|
1069
1792
|
});
|
|
1070
1793
|
}
|
|
1071
1794
|
};
|
|
@@ -1104,19 +1827,19 @@ function getWeekDateRange(week) {
|
|
|
1104
1827
|
}
|
|
1105
1828
|
function createTimeCommand() {
|
|
1106
1829
|
const timeCmd = new Command4("time").description("\u5DE5\u65F6\u7BA1\u7406");
|
|
1107
|
-
timeCmd.command("list").description("\u67E5\u8BE2\u5DE5\u65F6\u6761\u76EE").option("--from <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)").option("--to <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)").option("--user-id <id>", "\u7528\u6237 ID").option("--activity-id <id>", "\u6D3B\u52A8\u7C7B\u578B ID").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236").option("--offset <offset>", "\u504F\u79FB\u91CF").option("--all-projects", "\u67E5\u8BE2\u6240\u6709\u9879\u76EE\u7684\u5DE5\u65F6").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
|
|
1830
|
+
timeCmd.command("list").description("\u67E5\u8BE2\u5DE5\u65F6\u6761\u76EE").option("--from <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)").option("--to <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)").option("--user-id <id>", "\u7528\u6237 ID").option("--activity-id <id>", "\u6D3B\u52A8\u7C7B\u578B ID").option("--limit <limit>", "\u8FD4\u56DE\u6570\u91CF\u9650\u5236").option("--offset <offset>", "\u504F\u79FB\u91CF").option("--all-projects", "\u67E5\u8BE2\u6240\u6709\u9879\u76EE\u7684\u5DE5\u65F6").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
1108
1831
|
const creds = resolveCredentials(options);
|
|
1109
1832
|
const requiredFields = options.allProjects ? ["token", "host"] : ["token", "host", "project"];
|
|
1110
1833
|
const validation = validateCredentials(creds, requiredFields);
|
|
1111
1834
|
if (!validation.valid) {
|
|
1112
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
1835
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
1113
1836
|
process.exit(1);
|
|
1114
1837
|
}
|
|
1115
1838
|
if (options.allProjects) {
|
|
1116
1839
|
logger_default.info("\u67E5\u8BE2\u6240\u6709\u9879\u76EE\u7684\u5DE5\u65F6...");
|
|
1117
1840
|
const projectsResult = await userService.getProjects(creds.token, creds.host);
|
|
1118
1841
|
if (!projectsResult.success || !projectsResult.data) {
|
|
1119
|
-
outputError(projectsResult.message || projectsResult.msg || projectsResult.api_error_msg || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25");
|
|
1842
|
+
outputError(projectsResult.message || projectsResult.msg || projectsResult.api_error_msg || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25", options.pretty);
|
|
1120
1843
|
process.exit(1);
|
|
1121
1844
|
}
|
|
1122
1845
|
const projects = projectsResult.data;
|
|
@@ -1172,28 +1895,42 @@ function createTimeCommand() {
|
|
|
1172
1895
|
offset: options.offset ? parseInt(options.offset, 10) : void 0
|
|
1173
1896
|
});
|
|
1174
1897
|
if (result.success && result.data) {
|
|
1175
|
-
outputSuccess(result.data);
|
|
1898
|
+
outputSuccess(result.data, options.pretty);
|
|
1176
1899
|
} else {
|
|
1177
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5DE5\u65F6\u5931\u8D25");
|
|
1900
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u67E5\u8BE2\u5DE5\u65F6\u5931\u8D25", options.pretty);
|
|
1178
1901
|
process.exit(1);
|
|
1179
1902
|
}
|
|
1180
1903
|
}
|
|
1181
1904
|
});
|
|
1182
|
-
timeCmd.command("create").description("\u521B\u5EFA\u5DE5\u65F6\u6761\u76EE").requiredOption("--issue <id>", "\u95EE\u9898 ID").requiredOption("--days <days>", "\u5DE5\u65F6\uFF08\u5929\uFF09").
|
|
1905
|
+
timeCmd.command("create").description("\u521B\u5EFA\u5DE5\u65F6\u6761\u76EE").requiredOption("--issue <id>", "\u95EE\u9898 ID").requiredOption("--days <days>", "\u5DE5\u65F6\uFF08\u5929\uFF09").requiredOption("--activity <id>", "\u6D3B\u52A8\u7C7B\u578B ID\uFF08\u4F7F\u7528 pm-cli time options \u83B7\u53D6\u53EF\u7528\u503C\uFF09").option("--date <date>", "\u65E5\u671F (YYYY-MM-DD)\uFF0C\u9ED8\u8BA4\u4ECA\u5929").option("--comments <comments>", "\u5907\u6CE8").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
1183
1906
|
const creds = resolveCredentials(options);
|
|
1184
1907
|
const validation = validateCredentials(creds);
|
|
1185
1908
|
if (!validation.valid) {
|
|
1186
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
1909
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
1910
|
+
process.exit(1);
|
|
1911
|
+
}
|
|
1912
|
+
let userId;
|
|
1913
|
+
const configUserId = getConfigValue("userId", options.config);
|
|
1914
|
+
if (configUserId) {
|
|
1915
|
+
userId = parseInt(configUserId, 10);
|
|
1916
|
+
}
|
|
1917
|
+
if (!userId) {
|
|
1918
|
+
outputError("\u7F3A\u5C11\u7528\u6237 ID\uFF0C\u8BF7\u5148\u8BBE\u7F6E: pm-cli config set user-id <\u60A8\u7684\u7528\u6237ID>", options.pretty);
|
|
1187
1919
|
process.exit(1);
|
|
1188
1920
|
}
|
|
1189
1921
|
const issueId = parseInt(options.issue.replace(/^#/, ""), 10);
|
|
1190
1922
|
if (isNaN(issueId)) {
|
|
1191
|
-
outputError("\u65E0\u6548\u7684\u95EE\u9898 ID");
|
|
1923
|
+
outputError("\u65E0\u6548\u7684\u95EE\u9898 ID", options.pretty);
|
|
1192
1924
|
process.exit(1);
|
|
1193
1925
|
}
|
|
1194
1926
|
const days = parseFloat(options.days);
|
|
1195
1927
|
if (isNaN(days) || days <= 0) {
|
|
1196
|
-
outputError("\u65E0\u6548\u7684\u5DE5\u65F6\u6570");
|
|
1928
|
+
outputError("\u65E0\u6548\u7684\u5DE5\u65F6\u6570", options.pretty);
|
|
1929
|
+
process.exit(1);
|
|
1930
|
+
}
|
|
1931
|
+
const activityId = parseInt(options.activity, 10);
|
|
1932
|
+
if (isNaN(activityId)) {
|
|
1933
|
+
outputError("\u65E0\u6548\u7684\u6D3B\u52A8\u7C7B\u578B ID\uFF0C\u4F7F\u7528 pm-cli time options \u83B7\u53D6\u53EF\u7528\u503C", options.pretty);
|
|
1197
1934
|
process.exit(1);
|
|
1198
1935
|
}
|
|
1199
1936
|
const params = {
|
|
@@ -1203,97 +1940,133 @@ function createTimeCommand() {
|
|
|
1203
1940
|
issue_id: issueId,
|
|
1204
1941
|
hours: days,
|
|
1205
1942
|
// API 参数名为 hours,但单位是天
|
|
1206
|
-
spent_on: options.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
1943
|
+
spent_on: options.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
1944
|
+
user_id: userId,
|
|
1945
|
+
// 用户 ID(必填)
|
|
1946
|
+
activity_id: activityId,
|
|
1947
|
+
// 活动类型 ID(必填)
|
|
1948
|
+
bulk_create: "true"
|
|
1949
|
+
// 固定为 true
|
|
1207
1950
|
};
|
|
1208
|
-
if (options.activity) params.activity_id = parseInt(options.activity, 10);
|
|
1209
1951
|
if (options.comments) params.comments = options.comments;
|
|
1210
1952
|
const result = await timeEntryService.createTimeEntry(params);
|
|
1211
1953
|
if (result.success && result.data) {
|
|
1212
|
-
outputSuccess(result.data);
|
|
1954
|
+
outputSuccess(result.data, options.pretty);
|
|
1213
1955
|
} else {
|
|
1214
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u521B\u5EFA\u5DE5\u65F6\u5931\u8D25");
|
|
1956
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u521B\u5EFA\u5DE5\u65F6\u5931\u8D25", options.pretty);
|
|
1215
1957
|
process.exit(1);
|
|
1216
1958
|
}
|
|
1217
1959
|
});
|
|
1218
|
-
timeCmd.command("update <id>").description("\u66F4\u65B0\u5DE5\u65F6\u6761\u76EE").
|
|
1960
|
+
timeCmd.command("update <id>").description("\u66F4\u65B0\u5DE5\u65F6\u6761\u76EE").requiredOption("--issue <id>", "\u95EE\u9898 ID\uFF08\u5FC5\u586B\uFF09").requiredOption("--days <days>", "\u5DE5\u65F6\uFF08\u5929\uFF09\uFF08\u5FC5\u586B\uFF09").requiredOption("--activity <id>", "\u6D3B\u52A8\u7C7B\u578B ID\uFF08\u5FC5\u586B\uFF09").requiredOption("--date <date>", "\u65E5\u671F (YYYY-MM-DD)\uFF08\u5FC5\u586B\uFF09").option("--comments <comments>", "\u5907\u6CE8").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (id, options) => {
|
|
1219
1961
|
const creds = resolveCredentials(options);
|
|
1220
1962
|
const validation = validateCredentials(creds);
|
|
1221
1963
|
if (!validation.valid) {
|
|
1222
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
1964
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
1965
|
+
process.exit(1);
|
|
1966
|
+
}
|
|
1967
|
+
let userId;
|
|
1968
|
+
const configUserId = getConfigValue("userId", options.config);
|
|
1969
|
+
if (configUserId) {
|
|
1970
|
+
userId = parseInt(configUserId, 10);
|
|
1971
|
+
}
|
|
1972
|
+
if (!userId) {
|
|
1973
|
+
outputError("\u7F3A\u5C11\u7528\u6237 ID\uFF0C\u8BF7\u5148\u8BBE\u7F6E: pm-cli config set user-id <\u60A8\u7684\u7528\u6237ID>", options.pretty);
|
|
1223
1974
|
process.exit(1);
|
|
1224
1975
|
}
|
|
1225
1976
|
const timeEntryId = parseInt(id, 10);
|
|
1226
1977
|
if (isNaN(timeEntryId)) {
|
|
1227
|
-
outputError("\u65E0\u6548\u7684\u5DE5\u65F6\u6761\u76EE ID");
|
|
1978
|
+
outputError("\u65E0\u6548\u7684\u5DE5\u65F6\u6761\u76EE ID", options.pretty);
|
|
1979
|
+
process.exit(1);
|
|
1980
|
+
}
|
|
1981
|
+
const issueId = parseInt(options.issue.replace(/^#/, ""), 10);
|
|
1982
|
+
if (isNaN(issueId)) {
|
|
1983
|
+
outputError("\u65E0\u6548\u7684\u95EE\u9898 ID", options.pretty);
|
|
1228
1984
|
process.exit(1);
|
|
1229
1985
|
}
|
|
1230
1986
|
const params = {
|
|
1231
1987
|
token: creds.token,
|
|
1232
1988
|
host: creds.host,
|
|
1233
1989
|
project: creds.project,
|
|
1234
|
-
|
|
1990
|
+
id: timeEntryId,
|
|
1991
|
+
// 工时 ID
|
|
1992
|
+
issue_id: issueId,
|
|
1993
|
+
// 问题 ID(必填)
|
|
1994
|
+
hours: parseFloat(options.days),
|
|
1995
|
+
// 工时(必填)
|
|
1996
|
+
activity_id: parseInt(options.activity, 10),
|
|
1997
|
+
// 活动类型(必填)
|
|
1998
|
+
spent_on: options.date,
|
|
1999
|
+
// 日期(必填)
|
|
2000
|
+
user_id: userId,
|
|
2001
|
+
// 用户 ID(必填)
|
|
2002
|
+
updated_by: userId
|
|
2003
|
+
// 操作用户 ID(必填)
|
|
1235
2004
|
};
|
|
1236
|
-
if (options.days) params.hours = parseFloat(options.days);
|
|
1237
|
-
if (options.activity) params.activity_id = parseInt(options.activity, 10);
|
|
1238
|
-
if (options.date) params.spent_on = options.date;
|
|
1239
2005
|
if (options.comments) params.comments = options.comments;
|
|
1240
2006
|
const result = await timeEntryService.updateTimeEntry(params);
|
|
1241
2007
|
if (result.success && result.data) {
|
|
1242
|
-
outputSuccess(result.data);
|
|
2008
|
+
outputSuccess(result.data, options.pretty);
|
|
1243
2009
|
} else {
|
|
1244
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u66F4\u65B0\u5DE5\u65F6\u5931\u8D25");
|
|
2010
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u66F4\u65B0\u5DE5\u65F6\u5931\u8D25", options.pretty);
|
|
1245
2011
|
process.exit(1);
|
|
1246
2012
|
}
|
|
1247
2013
|
});
|
|
1248
|
-
timeCmd.command("delete <id>").description("\u5220\u9664\u5DE5\u65F6\u6761\u76EE").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (id, options) => {
|
|
2014
|
+
timeCmd.command("delete <id>").description("\u5220\u9664\u5DE5\u65F6\u6761\u76EE").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (id, options) => {
|
|
1249
2015
|
const creds = resolveCredentials(options);
|
|
1250
|
-
const validation = validateCredentials(creds);
|
|
2016
|
+
const validation = validateCredentials(creds, ["token", "host"]);
|
|
1251
2017
|
if (!validation.valid) {
|
|
1252
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
2018
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
1253
2019
|
process.exit(1);
|
|
1254
2020
|
}
|
|
1255
2021
|
const timeEntryId = parseInt(id, 10);
|
|
1256
2022
|
if (isNaN(timeEntryId)) {
|
|
1257
|
-
outputError("\u65E0\u6548\u7684\u5DE5\u65F6\u6761\u76EE ID");
|
|
2023
|
+
outputError("\u65E0\u6548\u7684\u5DE5\u65F6\u6761\u76EE ID", options.pretty);
|
|
1258
2024
|
process.exit(1);
|
|
1259
2025
|
}
|
|
1260
2026
|
const result = await timeEntryService.deleteTimeEntry(
|
|
1261
2027
|
creds.token,
|
|
1262
2028
|
creds.host,
|
|
1263
|
-
creds.project,
|
|
1264
2029
|
timeEntryId
|
|
1265
2030
|
);
|
|
1266
2031
|
if (result.success) {
|
|
1267
|
-
outputSuccess({ message: "\u5DE5\u65F6\u6761\u76EE\u5DF2\u5220\u9664", id: timeEntryId });
|
|
2032
|
+
outputSuccess({ message: "\u5DE5\u65F6\u6761\u76EE\u5DF2\u5220\u9664", id: timeEntryId }, options.pretty);
|
|
1268
2033
|
} else {
|
|
1269
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u5220\u9664\u5DE5\u65F6\u5931\u8D25");
|
|
2034
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u5220\u9664\u5DE5\u65F6\u5931\u8D25", options.pretty);
|
|
1270
2035
|
process.exit(1);
|
|
1271
2036
|
}
|
|
1272
2037
|
});
|
|
1273
|
-
timeCmd.command("options").description("\u83B7\u53D6\u5DE5\u65F6\u6761\u76EE\u9009\u9879").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
|
|
2038
|
+
timeCmd.command("options").description("\u83B7\u53D6\u5DE5\u65F6\u6761\u76EE\u9009\u9879\uFF08\u6D3B\u52A8\u7C7B\u578B\u5217\u8868\u7B49\uFF09").option("--issue <id>", "\u95EE\u9898 ID\uFF08\u53EF\u9009\uFF0C\u7528\u4E8E\u83B7\u53D6\u7279\u5B9A\u95EE\u9898\u7684\u5DE5\u65F6\u9009\u9879\uFF09").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
1274
2039
|
const creds = resolveCredentials(options);
|
|
1275
2040
|
const validation = validateCredentials(creds);
|
|
1276
2041
|
if (!validation.valid) {
|
|
1277
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
2042
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
1278
2043
|
process.exit(1);
|
|
1279
2044
|
}
|
|
2045
|
+
const issueId = options.issue ? parseInt(options.issue.replace(/^#/, ""), 10) : void 0;
|
|
1280
2046
|
const result = await timeEntryService.getTimeEntryOptions(
|
|
1281
2047
|
creds.token,
|
|
1282
2048
|
creds.host,
|
|
1283
|
-
creds.project
|
|
2049
|
+
creds.project,
|
|
2050
|
+
issueId
|
|
1284
2051
|
);
|
|
1285
|
-
if (result.success) {
|
|
1286
|
-
|
|
2052
|
+
if (result.success && result.data) {
|
|
2053
|
+
const data = result.data;
|
|
2054
|
+
if (data.options?.activities) {
|
|
2055
|
+
const activities = data.options.activities.map(([name, id]) => ({ id, name }));
|
|
2056
|
+
outputSuccess({ activities }, options.pretty);
|
|
2057
|
+
} else {
|
|
2058
|
+
outputSuccess(result.data, options.pretty);
|
|
2059
|
+
}
|
|
1287
2060
|
} else {
|
|
1288
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u9009\u9879\u5931\u8D25");
|
|
2061
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u9009\u9879\u5931\u8D25", options.pretty);
|
|
1289
2062
|
process.exit(1);
|
|
1290
2063
|
}
|
|
1291
2064
|
});
|
|
1292
|
-
timeCmd.command("summary").description("\u5DE5\u65F6\u7EDF\u8BA1\u6C47\u603B\uFF08\u67E5\u8BE2\u6307\u5B9A\u65F6\u95F4\u6BB5\u7684\u5DE5\u65F6\u5E76\u8BA1\u7B97\u7F3A\u53E3\uFF09").option("--week <week>", "\u5468\u9009\u62E9: current (\u672C\u5468), last (\u4E0A\u5468), \u6216\u6570\u5B57\u504F\u79FB", "current").option("--from <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)\uFF0C\u4E0E --week \u4E92\u65A5").option("--to <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)\uFF0C\u4E0E --week \u4E92\u65A5").option("--target <days>", "\u76EE\u6807\u5DE5\u65F6\uFF08\u5929\uFF09\uFF0C\u9ED8\u8BA4 5", "5").option("--user-id <id>", "\u7528\u6237 ID\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 user-id\uFF09").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
|
|
2065
|
+
timeCmd.command("summary").description("\u5DE5\u65F6\u7EDF\u8BA1\u6C47\u603B\uFF08\u67E5\u8BE2\u6307\u5B9A\u65F6\u95F4\u6BB5\u7684\u5DE5\u65F6\u5E76\u8BA1\u7B97\u7F3A\u53E3\uFF09").option("--week <week>", "\u5468\u9009\u62E9: current (\u672C\u5468), last (\u4E0A\u5468), \u6216\u6570\u5B57\u504F\u79FB", "current").option("--from <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)\uFF0C\u4E0E --week \u4E92\u65A5").option("--to <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)\uFF0C\u4E0E --week \u4E92\u65A5").option("--target <days>", "\u76EE\u6807\u5DE5\u65F6\uFF08\u5929\uFF09\uFF0C\u9ED8\u8BA4 5", "5").option("--detail", "\u663E\u793A\u5B8C\u6574\u5DE5\u65F6\u6761\u76EE\u8BE6\u60C5").option("--user-id <id>", "\u7528\u6237 ID\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 user-id\uFF09").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
1293
2066
|
const creds = resolveCredentials(options);
|
|
1294
2067
|
const validation = validateCredentials(creds, ["token", "host"]);
|
|
1295
2068
|
if (!validation.valid) {
|
|
1296
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
2069
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
1297
2070
|
process.exit(1);
|
|
1298
2071
|
}
|
|
1299
2072
|
let fromDate;
|
|
@@ -1324,13 +2097,14 @@ function createTimeCommand() {
|
|
|
1324
2097
|
}
|
|
1325
2098
|
const projectsResult = await userService.getProjects(creds.token, creds.host);
|
|
1326
2099
|
if (!projectsResult.success || !projectsResult.data) {
|
|
1327
|
-
outputError(projectsResult.message || projectsResult.msg || projectsResult.api_error_msg || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25");
|
|
2100
|
+
outputError(projectsResult.message || projectsResult.msg || projectsResult.api_error_msg || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25", options.pretty);
|
|
1328
2101
|
process.exit(1);
|
|
1329
2102
|
}
|
|
1330
2103
|
const projects = projectsResult.data;
|
|
1331
2104
|
const projectNames = Object.values(projects).map((p) => p.name);
|
|
1332
2105
|
logger_default.info(`\u67E5\u8BE2 ${projectNames.length} \u4E2A\u9879\u76EE...`);
|
|
1333
2106
|
const projectSummary = [];
|
|
2107
|
+
const allTimeEntries = [];
|
|
1334
2108
|
let totalHours = 0;
|
|
1335
2109
|
for (const projectName of projectNames) {
|
|
1336
2110
|
const result = await timeEntryService.queryTimeEntries({
|
|
@@ -1353,13 +2127,14 @@ function createTimeCommand() {
|
|
|
1353
2127
|
});
|
|
1354
2128
|
totalHours += projectHours;
|
|
1355
2129
|
}
|
|
2130
|
+
allTimeEntries.push(...data.time_entries);
|
|
1356
2131
|
}
|
|
1357
2132
|
}
|
|
1358
2133
|
}
|
|
1359
2134
|
const targetHours = parseFloat(options.target) || 5;
|
|
1360
2135
|
const remainingHours = Math.max(0, targetHours - totalHours);
|
|
1361
2136
|
totalHours = Math.round(totalHours * 100) / 100;
|
|
1362
|
-
|
|
2137
|
+
const output = {
|
|
1363
2138
|
period: periodLabel,
|
|
1364
2139
|
userId: userId || "\u672A\u6307\u5B9A",
|
|
1365
2140
|
totalHours,
|
|
@@ -1367,7 +2142,15 @@ function createTimeCommand() {
|
|
|
1367
2142
|
remainingHours: Math.round(remainingHours * 100) / 100,
|
|
1368
2143
|
isFilled: remainingHours === 0,
|
|
1369
2144
|
projectBreakdown: projectSummary.sort((a, b) => b.hours - a.hours)
|
|
1370
|
-
}
|
|
2145
|
+
};
|
|
2146
|
+
if (options.detail) {
|
|
2147
|
+
output.timeEntries = allTimeEntries.sort((a, b) => {
|
|
2148
|
+
const dateA = a.spent_on || "";
|
|
2149
|
+
const dateB = b.spent_on || "";
|
|
2150
|
+
return dateA.localeCompare(dateB);
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
outputSuccess(output, options.pretty);
|
|
1371
2154
|
});
|
|
1372
2155
|
return timeCmd;
|
|
1373
2156
|
}
|
|
@@ -1376,26 +2159,26 @@ function createTimeCommand() {
|
|
|
1376
2159
|
import { Command as Command5 } from "commander";
|
|
1377
2160
|
function createProjectCommand() {
|
|
1378
2161
|
const projectCmd = new Command5("project").description("\u9879\u76EE\u7BA1\u7406");
|
|
1379
|
-
projectCmd.command("list").description("\u83B7\u53D6\u9879\u76EE\u5217\u8868").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
|
|
2162
|
+
projectCmd.command("list").description("\u83B7\u53D6\u9879\u76EE\u5217\u8868").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
1380
2163
|
const creds = resolveCredentials(options);
|
|
1381
2164
|
const validation = validateCredentials(creds, ["token", "host"]);
|
|
1382
2165
|
if (!validation.valid) {
|
|
1383
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
2166
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
1384
2167
|
process.exit(1);
|
|
1385
2168
|
}
|
|
1386
2169
|
const result = await userService.getProjects(creds.token, creds.host);
|
|
1387
2170
|
if (result.success && result.data) {
|
|
1388
|
-
outputSuccess(result.data);
|
|
2171
|
+
outputSuccess(result.data, options.pretty);
|
|
1389
2172
|
} else {
|
|
1390
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25");
|
|
2173
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25", options.pretty);
|
|
1391
2174
|
process.exit(1);
|
|
1392
2175
|
}
|
|
1393
2176
|
});
|
|
1394
|
-
projectCmd.command("users").description("\u83B7\u53D6\u9879\u76EE\u7528\u6237\u5217\u8868").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
|
|
2177
|
+
projectCmd.command("users").description("\u83B7\u53D6\u9879\u76EE\u7528\u6237\u5217\u8868").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--project <project>", "\u9879\u76EE\u540D\u79F0").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
1395
2178
|
const creds = resolveCredentials(options);
|
|
1396
2179
|
const validation = validateCredentials(creds);
|
|
1397
2180
|
if (!validation.valid) {
|
|
1398
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
2181
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
1399
2182
|
process.exit(1);
|
|
1400
2183
|
}
|
|
1401
2184
|
const result = await userService.getProjectUsers(
|
|
@@ -1404,24 +2187,24 @@ function createProjectCommand() {
|
|
|
1404
2187
|
creds.project
|
|
1405
2188
|
);
|
|
1406
2189
|
if (result.success && result.data) {
|
|
1407
|
-
outputSuccess(result.data);
|
|
2190
|
+
outputSuccess(result.data, options.pretty);
|
|
1408
2191
|
} else {
|
|
1409
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u7528\u6237\u5217\u8868\u5931\u8D25");
|
|
2192
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u7528\u6237\u5217\u8868\u5931\u8D25", options.pretty);
|
|
1410
2193
|
process.exit(1);
|
|
1411
2194
|
}
|
|
1412
2195
|
});
|
|
1413
|
-
projectCmd.command("info").description("\u83B7\u53D6\u4E3B\u673A\u4FE1\u606F").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(async (options) => {
|
|
2196
|
+
projectCmd.command("info").description("\u83B7\u53D6\u4E3B\u673A\u4FE1\u606F").option("--token <token>", "API Token").option("--host <host>", "PM \u4E3B\u673A\u5730\u5740").option("--profile <name>", "\u4F7F\u7528\u914D\u7F6E profile").option("--config <path>", "\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09").action(async (options) => {
|
|
1414
2197
|
const creds = resolveCredentials(options);
|
|
1415
2198
|
const validation = validateCredentials(creds, ["token", "host"]);
|
|
1416
2199
|
if (!validation.valid) {
|
|
1417
|
-
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}
|
|
2200
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
1418
2201
|
process.exit(1);
|
|
1419
2202
|
}
|
|
1420
2203
|
const result = await userService.getHostInfo(creds.token, creds.host);
|
|
1421
2204
|
if (result.success) {
|
|
1422
|
-
outputSuccess(result.data);
|
|
2205
|
+
outputSuccess(result.data, options.pretty);
|
|
1423
2206
|
} else {
|
|
1424
|
-
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u4E3B\u673A\u4FE1\u606F\u5931\u8D25");
|
|
2207
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u83B7\u53D6\u4E3B\u673A\u4FE1\u606F\u5931\u8D25", options.pretty);
|
|
1425
2208
|
process.exit(1);
|
|
1426
2209
|
}
|
|
1427
2210
|
});
|
|
@@ -1429,8 +2212,15 @@ function createProjectCommand() {
|
|
|
1429
2212
|
}
|
|
1430
2213
|
|
|
1431
2214
|
// src/index.ts
|
|
2215
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
2216
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
2217
|
+
import { fileURLToPath } from "url";
|
|
2218
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
2219
|
+
var __dirname = dirname2(__filename);
|
|
2220
|
+
var packageJson = JSON.parse(readFileSync2(join2(__dirname, "../package.json"), "utf-8"));
|
|
2221
|
+
var version = packageJson.version;
|
|
1432
2222
|
var program = new Command6();
|
|
1433
|
-
program.name("pm-cli").description("\u7F51\u6613\u6613\u534F\u4F5C (PM NetEase) \u547D\u4EE4\u884C\u5DE5\u5177").version(
|
|
2223
|
+
program.name("pm-cli").description("\u7F51\u6613\u6613\u534F\u4F5C (PM NetEase) \u547D\u4EE4\u884C\u5DE5\u5177").version(version).option("--verbose", "\u8BE6\u7EC6\u8F93\u51FA").option("--debug", "\u8C03\u8BD5\u6A21\u5F0F").option("--no-mapping", "\u4E0D\u8F93\u51FA key \u6620\u5C04\u8868\uFF08\u9ED8\u8BA4\u8F93\u51FA\uFF09").hook("preAction", (thisCommand) => {
|
|
1434
2224
|
const opts = thisCommand.opts();
|
|
1435
2225
|
if (opts.debug) {
|
|
1436
2226
|
logger_default.setLevel("debug");
|
|
@@ -1439,6 +2229,9 @@ program.name("pm-cli").description("\u7F51\u6613\u6613\u534F\u4F5C (PM NetEase)
|
|
|
1439
2229
|
} else {
|
|
1440
2230
|
logger_default.setLevel("silent");
|
|
1441
2231
|
}
|
|
2232
|
+
if (opts.mapping === false) {
|
|
2233
|
+
setKeyMappingEnabled(false);
|
|
2234
|
+
}
|
|
1442
2235
|
});
|
|
1443
2236
|
program.addCommand(createTestCommand());
|
|
1444
2237
|
program.addCommand(createConfigCommand());
|