@hangox/pm-cli 0.2.6 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +707 -19
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -562,10 +562,217 @@ function createTestCommand() {
|
|
|
562
562
|
|
|
563
563
|
// src/commands/config/index.ts
|
|
564
564
|
import { Command as Command2 } from "commander";
|
|
565
|
+
|
|
566
|
+
// src/telemetry/index.ts
|
|
567
|
+
import * as os3 from "os";
|
|
568
|
+
|
|
569
|
+
// src/telemetry/client.ts
|
|
570
|
+
import { PostHog } from "posthog-node";
|
|
571
|
+
import * as os2 from "os";
|
|
572
|
+
import * as crypto2 from "crypto";
|
|
573
|
+
import * as fs2 from "fs";
|
|
574
|
+
import * as path2 from "path";
|
|
575
|
+
|
|
576
|
+
// src/telemetry/sanitizer.ts
|
|
577
|
+
import * as crypto from "crypto";
|
|
578
|
+
function hashString(str) {
|
|
579
|
+
return crypto.createHash("sha256").update(str).digest("hex").slice(0, 16);
|
|
580
|
+
}
|
|
581
|
+
function classifyError(error) {
|
|
582
|
+
if (error instanceof Error) {
|
|
583
|
+
const message = error.message.toLowerCase();
|
|
584
|
+
const name = error.name.toLowerCase();
|
|
585
|
+
if (message.includes("econnrefused") || message.includes("enotfound") || message.includes("etimedout") || message.includes("network") || message.includes("fetch failed") || name.includes("fetch")) {
|
|
586
|
+
return "network";
|
|
587
|
+
}
|
|
588
|
+
if (message.includes("401") || message.includes("403") || message.includes("unauthorized") || message.includes("forbidden") || message.includes("token") || message.includes("authentication")) {
|
|
589
|
+
return "auth";
|
|
590
|
+
}
|
|
591
|
+
if (message.includes("validation") || message.includes("invalid") || message.includes("required") || message.includes("missing")) {
|
|
592
|
+
return "validation";
|
|
593
|
+
}
|
|
594
|
+
if (message.includes("400") || message.includes("404") || message.includes("500") || message.includes("502") || message.includes("503")) {
|
|
595
|
+
return "api";
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return "unknown";
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// src/telemetry/client.ts
|
|
602
|
+
var POSTHOG_KEY = "phc_GrxYD6adJvJyDkSSUwWhhD4hThh3EOSJ4Q7xKkNSfdl";
|
|
603
|
+
var POSTHOG_HOST = "https://us.i.posthog.com";
|
|
604
|
+
var CONFIG_DIR = path2.join(os2.homedir(), ".config", "pm-cli");
|
|
605
|
+
var TELEMETRY_ID_FILE = path2.join(CONFIG_DIR, ".telemetry-id");
|
|
606
|
+
var client = null;
|
|
607
|
+
var cliVersion = "unknown";
|
|
608
|
+
function getOrCreateDeviceId() {
|
|
609
|
+
try {
|
|
610
|
+
if (fs2.existsSync(TELEMETRY_ID_FILE)) {
|
|
611
|
+
return fs2.readFileSync(TELEMETRY_ID_FILE, "utf-8").trim();
|
|
612
|
+
}
|
|
613
|
+
if (!fs2.existsSync(CONFIG_DIR)) {
|
|
614
|
+
fs2.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
615
|
+
}
|
|
616
|
+
const id = `device_${crypto2.randomUUID()}`;
|
|
617
|
+
fs2.writeFileSync(TELEMETRY_ID_FILE, id);
|
|
618
|
+
return id;
|
|
619
|
+
} catch {
|
|
620
|
+
return `temp_${crypto2.randomUUID()}`;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function getDistinctId() {
|
|
624
|
+
const userMail = getConfigValue("userMail");
|
|
625
|
+
if (userMail) {
|
|
626
|
+
return `user_${hashString(userMail)}`;
|
|
627
|
+
}
|
|
628
|
+
return getOrCreateDeviceId();
|
|
629
|
+
}
|
|
630
|
+
function initTelemetry(version2) {
|
|
631
|
+
cliVersion = version2;
|
|
632
|
+
try {
|
|
633
|
+
client = new PostHog(POSTHOG_KEY, {
|
|
634
|
+
host: POSTHOG_HOST,
|
|
635
|
+
flushAt: 1,
|
|
636
|
+
// CLI 单次执行,立即发送
|
|
637
|
+
flushInterval: 0
|
|
638
|
+
// 禁用定时刷新
|
|
639
|
+
});
|
|
640
|
+
logger_default.debug("\u9065\u6D4B\u5DF2\u521D\u59CB\u5316");
|
|
641
|
+
} catch (error) {
|
|
642
|
+
logger_default.debug("\u9065\u6D4B\u521D\u59CB\u5316\u5931\u8D25:", error);
|
|
643
|
+
client = null;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
async function shutdownTelemetry() {
|
|
647
|
+
if (client) {
|
|
648
|
+
try {
|
|
649
|
+
await client.shutdown();
|
|
650
|
+
logger_default.debug("\u9065\u6D4B\u5DF2\u5173\u95ED");
|
|
651
|
+
} catch (error) {
|
|
652
|
+
logger_default.debug("\u9065\u6D4B\u5173\u95ED\u5931\u8D25:", error);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
function getCommonProperties() {
|
|
657
|
+
return {
|
|
658
|
+
cli_version: cliVersion,
|
|
659
|
+
node_version: process.version,
|
|
660
|
+
os: process.platform,
|
|
661
|
+
os_version: os2.release(),
|
|
662
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function capture(event, properties) {
|
|
666
|
+
if (!client) {
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
try {
|
|
670
|
+
client.capture({
|
|
671
|
+
distinctId: getDistinctId(),
|
|
672
|
+
event,
|
|
673
|
+
properties: {
|
|
674
|
+
...getCommonProperties(),
|
|
675
|
+
...properties
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
logger_default.debug(`\u9065\u6D4B\u4E8B\u4EF6: ${event}`, properties);
|
|
679
|
+
} catch (error) {
|
|
680
|
+
logger_default.debug("\u9065\u6D4B\u4E8B\u4EF6\u53D1\u9001\u5931\u8D25:", error);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// src/telemetry/events.ts
|
|
685
|
+
var EventNames = {
|
|
686
|
+
SESSION_STARTED: "cli_session_started",
|
|
687
|
+
COMMAND_EXECUTED: "command_executed",
|
|
688
|
+
ERROR_OCCURRED: "error_occurred",
|
|
689
|
+
CONFIG_CHANGED: "config_changed",
|
|
690
|
+
MILESTONE_REACHED: "milestone_reached",
|
|
691
|
+
PERFORMANCE_METRIC: "performance_metric"
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
// src/telemetry/index.ts
|
|
695
|
+
var currentContext = null;
|
|
696
|
+
function startCommandTracking(command, subcommand, extraProps) {
|
|
697
|
+
currentContext = {
|
|
698
|
+
command,
|
|
699
|
+
subcommand,
|
|
700
|
+
startTime: Date.now(),
|
|
701
|
+
extraProps
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
function endCommandTracking(extraProps) {
|
|
705
|
+
if (!currentContext) return;
|
|
706
|
+
const duration = Date.now() - currentContext.startTime;
|
|
707
|
+
const properties = {
|
|
708
|
+
command: currentContext.command,
|
|
709
|
+
subcommand: currentContext.subcommand,
|
|
710
|
+
success: true,
|
|
711
|
+
duration_ms: duration,
|
|
712
|
+
...currentContext.extraProps,
|
|
713
|
+
...extraProps
|
|
714
|
+
};
|
|
715
|
+
capture(EventNames.COMMAND_EXECUTED, properties);
|
|
716
|
+
currentContext = null;
|
|
717
|
+
}
|
|
718
|
+
function failCommandTracking(error, extraProps) {
|
|
719
|
+
if (!currentContext) return;
|
|
720
|
+
const duration = Date.now() - currentContext.startTime;
|
|
721
|
+
const errorType = classifyError(error);
|
|
722
|
+
const properties = {
|
|
723
|
+
command: currentContext.command,
|
|
724
|
+
subcommand: currentContext.subcommand,
|
|
725
|
+
success: false,
|
|
726
|
+
duration_ms: duration,
|
|
727
|
+
error_type: errorType,
|
|
728
|
+
...currentContext.extraProps,
|
|
729
|
+
...extraProps
|
|
730
|
+
};
|
|
731
|
+
capture(EventNames.COMMAND_EXECUTED, properties);
|
|
732
|
+
currentContext = null;
|
|
733
|
+
}
|
|
734
|
+
function isCI() {
|
|
735
|
+
return !!(process.env.CI || process.env.CONTINUOUS_INTEGRATION || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.JENKINS_URL || process.env.TRAVIS);
|
|
736
|
+
}
|
|
737
|
+
function getTerminal() {
|
|
738
|
+
return process.env.TERM_PROGRAM || process.env.TERMINAL || "unknown";
|
|
739
|
+
}
|
|
740
|
+
function getShell() {
|
|
741
|
+
const shell = process.env.SHELL || "";
|
|
742
|
+
const shellName = shell.split("/").pop() || "unknown";
|
|
743
|
+
return shellName;
|
|
744
|
+
}
|
|
745
|
+
function trackSessionStarted() {
|
|
746
|
+
const properties = {
|
|
747
|
+
cli_version: "",
|
|
748
|
+
// 由 client 自动填充
|
|
749
|
+
node_version: process.version,
|
|
750
|
+
os: process.platform,
|
|
751
|
+
os_version: os3.release(),
|
|
752
|
+
shell: getShell(),
|
|
753
|
+
is_ci: isCI(),
|
|
754
|
+
terminal: getTerminal()
|
|
755
|
+
};
|
|
756
|
+
capture(EventNames.SESSION_STARTED, properties);
|
|
757
|
+
}
|
|
758
|
+
function trackConfigChanged(key, action, isProfile = false, profileCount) {
|
|
759
|
+
const sensitiveKeys = ["token", "default-token"];
|
|
760
|
+
const safeKey = sensitiveKeys.includes(key.toLowerCase()) ? "[redacted]" : key;
|
|
761
|
+
const properties = {
|
|
762
|
+
key: safeKey,
|
|
763
|
+
action,
|
|
764
|
+
is_profile: isProfile,
|
|
765
|
+
profile_count: profileCount
|
|
766
|
+
};
|
|
767
|
+
capture(EventNames.CONFIG_CHANGED, properties);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// src/commands/config/index.ts
|
|
565
771
|
function createConfigCommand() {
|
|
566
772
|
const configCmd = new Command2("config").description("\u914D\u7F6E\u7BA1\u7406");
|
|
567
773
|
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) => {
|
|
568
774
|
setConfigValue(key, value, options.config);
|
|
775
|
+
trackConfigChanged(key, "set", false);
|
|
569
776
|
outputSuccess({
|
|
570
777
|
message: `\u914D\u7F6E\u9879 ${key} \u5DF2\u8BBE\u7F6E`,
|
|
571
778
|
key,
|
|
@@ -601,6 +808,8 @@ function createConfigCommand() {
|
|
|
601
808
|
process.exit(1);
|
|
602
809
|
}
|
|
603
810
|
setProfile(name, profile, options.config);
|
|
811
|
+
const profileCount = listProfiles(options.config).length;
|
|
812
|
+
trackConfigChanged(name, "set", true, profileCount);
|
|
604
813
|
outputSuccess({
|
|
605
814
|
message: `Profile ${name} \u5DF2\u6DFB\u52A0`,
|
|
606
815
|
name,
|
|
@@ -611,6 +820,8 @@ function createConfigCommand() {
|
|
|
611
820
|
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) => {
|
|
612
821
|
const removed = removeProfile(name, options.config);
|
|
613
822
|
if (removed) {
|
|
823
|
+
const profileCount = listProfiles(options.config).length;
|
|
824
|
+
trackConfigChanged(name, "delete", true, profileCount);
|
|
614
825
|
outputSuccess({ message: `Profile ${name} \u5DF2\u5220\u9664`, name }, options.pretty);
|
|
615
826
|
} else {
|
|
616
827
|
outputError(`Profile ${name} \u4E0D\u5B58\u5728`, options.pretty);
|
|
@@ -634,6 +845,98 @@ function createConfigCommand() {
|
|
|
634
845
|
|
|
635
846
|
// src/commands/issue/index.ts
|
|
636
847
|
import { Command as Command3 } from "commander";
|
|
848
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
849
|
+
|
|
850
|
+
// src/utils/markdown-parser.ts
|
|
851
|
+
function extractHours(text) {
|
|
852
|
+
const daysRegex = /[((][^))]*?([\d.]+)d[^))]*[))]/;
|
|
853
|
+
const daysMatch = text.match(daysRegex);
|
|
854
|
+
if (daysMatch) {
|
|
855
|
+
const value = parseFloat(daysMatch[1]);
|
|
856
|
+
return isNaN(value) ? void 0 : value;
|
|
857
|
+
}
|
|
858
|
+
const chineseDaysRegex = /[((]([\d.]+)天[))]/;
|
|
859
|
+
const chineseDaysMatch = text.match(chineseDaysRegex);
|
|
860
|
+
if (chineseDaysMatch) {
|
|
861
|
+
const value = parseFloat(chineseDaysMatch[1]);
|
|
862
|
+
return isNaN(value) ? void 0 : value;
|
|
863
|
+
}
|
|
864
|
+
const hoursRegex = /[((]([\d.]+)[))]/;
|
|
865
|
+
const hoursMatch = text.match(hoursRegex);
|
|
866
|
+
if (hoursMatch) {
|
|
867
|
+
const value = parseFloat(hoursMatch[1]);
|
|
868
|
+
return isNaN(value) ? void 0 : value;
|
|
869
|
+
}
|
|
870
|
+
return void 0;
|
|
871
|
+
}
|
|
872
|
+
function parseMarkdownLine(line) {
|
|
873
|
+
const leadingSpaces = line.match(/^(\s*)/)?.[1].length || 0;
|
|
874
|
+
const level = Math.floor(leadingSpaces / 2);
|
|
875
|
+
const cleanLine = line.trimStart().replace(/^[*-]\s*/, "").trim();
|
|
876
|
+
const estimatedHours = extractHours(cleanLine);
|
|
877
|
+
const subject = cleanLine.replace(/\s*[((][^))]*[))]/g, "").trim();
|
|
878
|
+
return { level, subject, estimatedHours };
|
|
879
|
+
}
|
|
880
|
+
function parseMarkdownToNodes(markdown) {
|
|
881
|
+
const lines = markdown.trim().split("\n").filter((line) => line.trim().length > 0).filter((line) => /^\s*[*-]/.test(line));
|
|
882
|
+
const rootNodes = [];
|
|
883
|
+
const nodeStack = [];
|
|
884
|
+
for (const line of lines) {
|
|
885
|
+
const { level, subject, estimatedHours } = parseMarkdownLine(line);
|
|
886
|
+
if (!subject) {
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
const node = {
|
|
890
|
+
subject,
|
|
891
|
+
estimatedHours,
|
|
892
|
+
children: []
|
|
893
|
+
};
|
|
894
|
+
while (nodeStack.length > 0 && nodeStack[nodeStack.length - 1].level >= level) {
|
|
895
|
+
nodeStack.pop();
|
|
896
|
+
}
|
|
897
|
+
if (nodeStack.length === 0) {
|
|
898
|
+
rootNodes.push(node);
|
|
899
|
+
} else {
|
|
900
|
+
nodeStack[nodeStack.length - 1].node.children.push(node);
|
|
901
|
+
}
|
|
902
|
+
nodeStack.push({ level, node });
|
|
903
|
+
}
|
|
904
|
+
return rootNodes;
|
|
905
|
+
}
|
|
906
|
+
function countTasks(nodes) {
|
|
907
|
+
let count = 0;
|
|
908
|
+
for (const node of nodes) {
|
|
909
|
+
count += 1;
|
|
910
|
+
count += countTasks(node.children);
|
|
911
|
+
}
|
|
912
|
+
return count;
|
|
913
|
+
}
|
|
914
|
+
function sumEstimatedHours(nodes) {
|
|
915
|
+
let total = 0;
|
|
916
|
+
for (const node of nodes) {
|
|
917
|
+
if (node.estimatedHours) {
|
|
918
|
+
total += node.estimatedHours;
|
|
919
|
+
}
|
|
920
|
+
total += sumEstimatedHours(node.children);
|
|
921
|
+
}
|
|
922
|
+
return total;
|
|
923
|
+
}
|
|
924
|
+
function formatTaskTree(nodes, prefix = "", _isLast = true, showAssignee = false, assigneeName) {
|
|
925
|
+
const lines = [];
|
|
926
|
+
nodes.forEach((node, index) => {
|
|
927
|
+
const isLastNode = index === nodes.length - 1;
|
|
928
|
+
const connector = prefix === "" ? "* " : isLastNode ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
929
|
+
const hoursStr = node.estimatedHours !== void 0 ? ` (${node.estimatedHours}d)` : "";
|
|
930
|
+
const assigneeStr = showAssignee && assigneeName ? ` \u2192 ${assigneeName}` : "";
|
|
931
|
+
lines.push(`${prefix}${connector}${node.subject}${hoursStr}${assigneeStr}`);
|
|
932
|
+
if (node.children.length > 0) {
|
|
933
|
+
const childPrefix = prefix === "" ? " " : prefix + (isLastNode ? " " : "\u2502 ");
|
|
934
|
+
const childLines = formatTaskTree(node.children, childPrefix, isLastNode, showAssignee, assigneeName);
|
|
935
|
+
lines.push(childLines);
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
return lines.join("\n");
|
|
939
|
+
}
|
|
637
940
|
|
|
638
941
|
// src/services/issue-service.ts
|
|
639
942
|
function sleep(ms) {
|
|
@@ -903,6 +1206,7 @@ var IssueService = class {
|
|
|
903
1206
|
skipExisting
|
|
904
1207
|
});
|
|
905
1208
|
const result = {
|
|
1209
|
+
totalTasks: 0,
|
|
906
1210
|
totalCreated: 0,
|
|
907
1211
|
totalSkipped: 0,
|
|
908
1212
|
totalFailed: 0,
|
|
@@ -920,6 +1224,8 @@ var IssueService = class {
|
|
|
920
1224
|
};
|
|
921
1225
|
}
|
|
922
1226
|
const sourceIssue = sourceResult.data;
|
|
1227
|
+
result.totalTasks = this.countAllChildren(sourceIssue);
|
|
1228
|
+
logger_default.info(`\u6E90\u7236\u5355\u5171\u6709 ${result.totalTasks} \u4E2A\u5B50\u4EFB\u52A1`);
|
|
923
1229
|
logger_default.info("\u6B63\u5728\u83B7\u53D6\u76EE\u6807\u7236\u5355\u4FE1\u606F...");
|
|
924
1230
|
const targetResult = await this.getIssue(token, host, project, targetParentId, false, false);
|
|
925
1231
|
if (!targetResult.success || !targetResult.data) {
|
|
@@ -965,6 +1271,17 @@ var IssueService = class {
|
|
|
965
1271
|
};
|
|
966
1272
|
}
|
|
967
1273
|
}
|
|
1274
|
+
/**
|
|
1275
|
+
* 计算所有子任务数量(递归)
|
|
1276
|
+
*/
|
|
1277
|
+
countAllChildren(issue) {
|
|
1278
|
+
let count = 0;
|
|
1279
|
+
for (const child of issue.children || []) {
|
|
1280
|
+
count++;
|
|
1281
|
+
count += this.countAllChildren(child);
|
|
1282
|
+
}
|
|
1283
|
+
return count;
|
|
1284
|
+
}
|
|
968
1285
|
/**
|
|
969
1286
|
* 收集所有任务名称(递归)
|
|
970
1287
|
*/
|
|
@@ -994,6 +1311,8 @@ var IssueService = class {
|
|
|
994
1311
|
subject: taskName,
|
|
995
1312
|
reason: "\u76EE\u6807\u7236\u5355\u4E0B\u5DF2\u5B58\u5728\u540C\u540D\u4EFB\u52A1"
|
|
996
1313
|
});
|
|
1314
|
+
const progress = result.totalCreated + result.totalSkipped + result.totalFailed;
|
|
1315
|
+
console.error(`[${progress}/${result.totalTasks}] \u23ED\uFE0F \u8DF3\u8FC7: #${sourceId} ${taskName} (\u5DF2\u5B58\u5728)`);
|
|
997
1316
|
continue;
|
|
998
1317
|
}
|
|
999
1318
|
const isLeafNode = !child.children || child.children.length === 0;
|
|
@@ -1083,6 +1402,8 @@ var IssueService = class {
|
|
|
1083
1402
|
subject: taskName,
|
|
1084
1403
|
parentId: targetParentId
|
|
1085
1404
|
});
|
|
1405
|
+
const progress = result.totalCreated + result.totalSkipped + result.totalFailed;
|
|
1406
|
+
console.error(`[${progress}/${result.totalTasks}] \u2705 \u540C\u6B65\u6210\u529F: #${newId} \u2190 #${sourceId} ${taskName}`);
|
|
1086
1407
|
if (child.children && child.children.length > 0) {
|
|
1087
1408
|
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
1409
|
await this.syncChildrenRecursive(
|
|
@@ -1099,27 +1420,241 @@ var IssueService = class {
|
|
|
1099
1420
|
);
|
|
1100
1421
|
}
|
|
1101
1422
|
} else {
|
|
1102
|
-
|
|
1423
|
+
const errorMsg = createResult.message || createResult.api_error_msg || "\u521B\u5EFA\u5931\u8D25";
|
|
1424
|
+
logger_default.error(`\u274C \u521B\u5EFA\u5B50\u4EFB\u52A1\u5931\u8D25: ${taskName}`, errorMsg);
|
|
1103
1425
|
result.totalFailed++;
|
|
1104
1426
|
result.failed.push({
|
|
1105
1427
|
sourceId,
|
|
1106
1428
|
subject: taskName,
|
|
1107
|
-
error:
|
|
1429
|
+
error: errorMsg
|
|
1108
1430
|
});
|
|
1431
|
+
const progress = result.totalCreated + result.totalSkipped + result.totalFailed;
|
|
1432
|
+
console.error(`[${progress}/${result.totalTasks}] \u274C \u540C\u6B65\u5931\u8D25: #${sourceId} ${taskName} - ${errorMsg}`);
|
|
1109
1433
|
}
|
|
1110
1434
|
} catch (error) {
|
|
1435
|
+
const errorMsg = error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF";
|
|
1111
1436
|
logger_default.error(`\u274C \u521B\u5EFA\u5B50\u4EFB\u52A1\u65F6\u53D1\u751F\u9519\u8BEF: ${taskName}`, error);
|
|
1112
1437
|
result.totalFailed++;
|
|
1113
1438
|
result.failed.push({
|
|
1114
1439
|
sourceId,
|
|
1115
1440
|
subject: taskName,
|
|
1116
|
-
error:
|
|
1441
|
+
error: errorMsg
|
|
1117
1442
|
});
|
|
1443
|
+
const progress = result.totalCreated + result.totalSkipped + result.totalFailed;
|
|
1444
|
+
console.error(`[${progress}/${result.totalTasks}] \u274C \u540C\u6B65\u5931\u8D25: #${sourceId} ${taskName} - ${errorMsg}`);
|
|
1118
1445
|
}
|
|
1119
1446
|
await sleep(500);
|
|
1120
1447
|
}
|
|
1121
1448
|
}
|
|
1122
1449
|
}
|
|
1450
|
+
/**
|
|
1451
|
+
* 从 Markdown 批量创建子单
|
|
1452
|
+
* @param parentId 父单 ID
|
|
1453
|
+
* @param markdown Markdown 文本
|
|
1454
|
+
* @param assignedToMail 指派人邮箱
|
|
1455
|
+
* @param options 选项
|
|
1456
|
+
*/
|
|
1457
|
+
async batchCreateFromMarkdown(token, host, project, parentId, markdown, assignedToMail, options) {
|
|
1458
|
+
const { dryRun = false, interval = 5e3 } = options || {};
|
|
1459
|
+
logger_default.info("\u5F00\u59CB\u6279\u91CF\u521B\u5EFA\u5B50\u5355", {
|
|
1460
|
+
parentId,
|
|
1461
|
+
assignedToMail,
|
|
1462
|
+
dryRun,
|
|
1463
|
+
interval
|
|
1464
|
+
});
|
|
1465
|
+
const result = {
|
|
1466
|
+
totalCreated: 0,
|
|
1467
|
+
totalFailed: 0,
|
|
1468
|
+
totalTasks: 0,
|
|
1469
|
+
totalEstimatedHours: 0,
|
|
1470
|
+
created: [],
|
|
1471
|
+
failed: []
|
|
1472
|
+
};
|
|
1473
|
+
try {
|
|
1474
|
+
const taskNodes = parseMarkdownToNodes(markdown);
|
|
1475
|
+
if (taskNodes.length === 0) {
|
|
1476
|
+
return {
|
|
1477
|
+
success: false,
|
|
1478
|
+
message: "\u6CA1\u6709\u89E3\u6790\u5230\u4EFB\u4F55\u4EFB\u52A1\uFF0C\u8BF7\u68C0\u67E5 Markdown \u683C\u5F0F"
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
result.totalTasks = countTasks(taskNodes);
|
|
1482
|
+
result.totalEstimatedHours = sumEstimatedHours(taskNodes);
|
|
1483
|
+
logger_default.info("Markdown \u89E3\u6790\u5B8C\u6210", {
|
|
1484
|
+
totalTasks: result.totalTasks,
|
|
1485
|
+
totalEstimatedHours: result.totalEstimatedHours
|
|
1486
|
+
});
|
|
1487
|
+
logger_default.info("\u6B63\u5728\u83B7\u53D6\u7236\u5355\u4FE1\u606F...");
|
|
1488
|
+
const parentResult = await this.getIssue(token, host, project, parentId, false, false);
|
|
1489
|
+
if (!parentResult.success || !parentResult.data) {
|
|
1490
|
+
return {
|
|
1491
|
+
success: false,
|
|
1492
|
+
message: `\u83B7\u53D6\u7236\u5355 #${parentId} \u5931\u8D25: ${parentResult.message || "\u672A\u77E5\u9519\u8BEF"}`
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
const parentInfo = parentResult.data;
|
|
1496
|
+
logger_default.info("\u5F00\u59CB\u9012\u5F52\u521B\u5EFA\u5B50\u5355...");
|
|
1497
|
+
await this.batchCreateRecursive(
|
|
1498
|
+
token,
|
|
1499
|
+
host,
|
|
1500
|
+
project,
|
|
1501
|
+
taskNodes,
|
|
1502
|
+
parentId,
|
|
1503
|
+
parentInfo,
|
|
1504
|
+
assignedToMail,
|
|
1505
|
+
dryRun,
|
|
1506
|
+
interval,
|
|
1507
|
+
result
|
|
1508
|
+
);
|
|
1509
|
+
logger_default.info("\u6279\u91CF\u521B\u5EFA\u5B8C\u6210", {
|
|
1510
|
+
totalCreated: result.totalCreated,
|
|
1511
|
+
totalFailed: result.totalFailed
|
|
1512
|
+
});
|
|
1513
|
+
return { success: true, data: result };
|
|
1514
|
+
} catch (error) {
|
|
1515
|
+
logger_default.error("\u6279\u91CF\u521B\u5EFA\u5B50\u5355\u65F6\u53D1\u751F\u9519\u8BEF", error);
|
|
1516
|
+
return {
|
|
1517
|
+
success: false,
|
|
1518
|
+
message: `\u6279\u91CF\u521B\u5EFA\u5B50\u5355\u65F6\u53D1\u751F\u9519\u8BEF: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* 递归批量创建子单
|
|
1524
|
+
*/
|
|
1525
|
+
async batchCreateRecursive(token, host, project, taskNodes, targetParentId, parentInfo, assignedToMail, dryRun, interval, result) {
|
|
1526
|
+
for (const node of taskNodes) {
|
|
1527
|
+
const taskName = node.subject;
|
|
1528
|
+
const isLeafNode = node.children.length === 0;
|
|
1529
|
+
const createParams = {
|
|
1530
|
+
token,
|
|
1531
|
+
host,
|
|
1532
|
+
project,
|
|
1533
|
+
parent_issue_id: targetParentId,
|
|
1534
|
+
subject: taskName,
|
|
1535
|
+
// 继承自父单
|
|
1536
|
+
tracker: parentInfo.tracker?.name,
|
|
1537
|
+
status: "\u65B0\u5EFA",
|
|
1538
|
+
// 指派人
|
|
1539
|
+
assigned_to_mail: assignedToMail,
|
|
1540
|
+
// 只有叶子节点才设置工时
|
|
1541
|
+
estimated_hours: isLeafNode ? node.estimatedHours : void 0
|
|
1542
|
+
};
|
|
1543
|
+
const parentWithVersion = parentInfo;
|
|
1544
|
+
if (parentWithVersion.fixed_version?.name) {
|
|
1545
|
+
createParams.version = parentWithVersion.fixed_version.name;
|
|
1546
|
+
}
|
|
1547
|
+
const parentWithCustomFields = parentInfo;
|
|
1548
|
+
if (parentWithCustomFields.custom_fields && parentWithCustomFields.custom_fields.length > 0) {
|
|
1549
|
+
const customFieldMap = {};
|
|
1550
|
+
const followsMails = [];
|
|
1551
|
+
for (const field of parentWithCustomFields.custom_fields) {
|
|
1552
|
+
if (field.value !== null && field.value !== void 0 && field.value !== "") {
|
|
1553
|
+
if (field.identify === "IssuesQCFollow") {
|
|
1554
|
+
const followsValue = field.value;
|
|
1555
|
+
if (Array.isArray(followsValue)) {
|
|
1556
|
+
for (const item of followsValue) {
|
|
1557
|
+
if (item.user?.mail) {
|
|
1558
|
+
followsMails.push(item.user.mail);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
} else {
|
|
1563
|
+
customFieldMap[field.id] = field.value;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
if (Object.keys(customFieldMap).length > 0) {
|
|
1568
|
+
createParams.custom_field = JSON.stringify(customFieldMap);
|
|
1569
|
+
}
|
|
1570
|
+
if (followsMails.length > 0) {
|
|
1571
|
+
createParams.follows = followsMails;
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
logger_default.info(`\u6B63\u5728\u521B\u5EFA\u5B50\u4EFB\u52A1: ${taskName}`, { targetParentId, isLeafNode, estimatedHours: node.estimatedHours });
|
|
1575
|
+
if (dryRun) {
|
|
1576
|
+
logger_default.info(`[\u6A21\u62DF] \u5C06\u521B\u5EFA\u5B50\u4EFB\u52A1: ${taskName}`);
|
|
1577
|
+
result.totalCreated++;
|
|
1578
|
+
result.created.push({
|
|
1579
|
+
newId: 0,
|
|
1580
|
+
// 模拟模式没有真实 ID
|
|
1581
|
+
subject: taskName,
|
|
1582
|
+
parentId: targetParentId,
|
|
1583
|
+
estimatedHours: node.estimatedHours
|
|
1584
|
+
});
|
|
1585
|
+
if (node.children.length > 0) {
|
|
1586
|
+
await this.batchCreateRecursive(
|
|
1587
|
+
token,
|
|
1588
|
+
host,
|
|
1589
|
+
project,
|
|
1590
|
+
node.children,
|
|
1591
|
+
0,
|
|
1592
|
+
// 模拟模式下没有真实的新 ID
|
|
1593
|
+
parentInfo,
|
|
1594
|
+
assignedToMail,
|
|
1595
|
+
dryRun,
|
|
1596
|
+
interval,
|
|
1597
|
+
result
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1600
|
+
} else {
|
|
1601
|
+
try {
|
|
1602
|
+
const createResult = await this.createIssue(createParams);
|
|
1603
|
+
if (createResult.success && createResult.data) {
|
|
1604
|
+
const newId = createResult.data.id;
|
|
1605
|
+
logger_default.info(`\u2705 \u6210\u529F\u521B\u5EFA\u5B50\u4EFB\u52A1: ID=${newId}, \u6807\u9898=${taskName}`);
|
|
1606
|
+
result.totalCreated++;
|
|
1607
|
+
result.created.push({
|
|
1608
|
+
newId,
|
|
1609
|
+
subject: taskName,
|
|
1610
|
+
parentId: targetParentId,
|
|
1611
|
+
estimatedHours: node.estimatedHours
|
|
1612
|
+
});
|
|
1613
|
+
const progress = result.totalCreated + result.totalFailed;
|
|
1614
|
+
console.error(`[${progress}/${result.totalTasks}] \u2705 \u521B\u5EFA\u6210\u529F: #${newId} ${taskName}`);
|
|
1615
|
+
if (node.children.length > 0) {
|
|
1616
|
+
logger_default.info(`\u{1F4C1} \u53D1\u73B0\u5B50\u4EFB\u52A1 ${newId} \u6709 ${node.children.length} \u4E2A\u5B50\u4EFB\u52A1\uFF0C\u7EE7\u7EED\u9012\u5F52\u521B\u5EFA...`);
|
|
1617
|
+
await this.batchCreateRecursive(
|
|
1618
|
+
token,
|
|
1619
|
+
host,
|
|
1620
|
+
project,
|
|
1621
|
+
node.children,
|
|
1622
|
+
newId,
|
|
1623
|
+
parentInfo,
|
|
1624
|
+
assignedToMail,
|
|
1625
|
+
dryRun,
|
|
1626
|
+
interval,
|
|
1627
|
+
result
|
|
1628
|
+
);
|
|
1629
|
+
}
|
|
1630
|
+
} else {
|
|
1631
|
+
const errorMsg = createResult.message || createResult.api_error_msg || "\u521B\u5EFA\u5931\u8D25";
|
|
1632
|
+
logger_default.error(`\u274C \u521B\u5EFA\u5B50\u4EFB\u52A1\u5931\u8D25: ${taskName}`, errorMsg);
|
|
1633
|
+
result.totalFailed++;
|
|
1634
|
+
result.failed.push({
|
|
1635
|
+
subject: taskName,
|
|
1636
|
+
parentId: targetParentId,
|
|
1637
|
+
error: errorMsg
|
|
1638
|
+
});
|
|
1639
|
+
const progress = result.totalCreated + result.totalFailed;
|
|
1640
|
+
console.error(`[${progress}/${result.totalTasks}] \u274C \u521B\u5EFA\u5931\u8D25: ${taskName} - ${errorMsg}`);
|
|
1641
|
+
}
|
|
1642
|
+
} catch (error) {
|
|
1643
|
+
const errorMsg = error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF";
|
|
1644
|
+
logger_default.error(`\u274C \u521B\u5EFA\u5B50\u4EFB\u52A1\u65F6\u53D1\u751F\u9519\u8BEF: ${taskName}`, error);
|
|
1645
|
+
result.totalFailed++;
|
|
1646
|
+
result.failed.push({
|
|
1647
|
+
subject: taskName,
|
|
1648
|
+
parentId: targetParentId,
|
|
1649
|
+
error: errorMsg
|
|
1650
|
+
});
|
|
1651
|
+
const progress = result.totalCreated + result.totalFailed;
|
|
1652
|
+
console.error(`[${progress}/${result.totalTasks}] \u274C \u521B\u5EFA\u5931\u8D25: ${taskName} - ${errorMsg}`);
|
|
1653
|
+
}
|
|
1654
|
+
await sleep(interval);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1123
1658
|
};
|
|
1124
1659
|
var issueService = new IssueService();
|
|
1125
1660
|
|
|
@@ -1149,8 +1684,8 @@ function parsePmLink(url) {
|
|
|
1149
1684
|
}
|
|
1150
1685
|
|
|
1151
1686
|
// src/utils/issue-exporter.ts
|
|
1152
|
-
import { writeFileSync as
|
|
1153
|
-
import { join as
|
|
1687
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, statSync } from "fs";
|
|
1688
|
+
import { join as join3 } from "path";
|
|
1154
1689
|
|
|
1155
1690
|
// src/utils/preset-extractor.ts
|
|
1156
1691
|
var SUMMARY_CUSTOM_FIELD_IDS = [84];
|
|
@@ -1265,27 +1800,27 @@ function generateExportDir(command, identifier) {
|
|
|
1265
1800
|
async function exportIssuePresets(issue, exportDir, options = {}) {
|
|
1266
1801
|
const { depth = -1, pretty = false } = options;
|
|
1267
1802
|
const indent = pretty ? 2 : 0;
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1803
|
+
mkdirSync3(exportDir, { recursive: true });
|
|
1804
|
+
mkdirSync3(join3(exportDir, "standard"), { recursive: true });
|
|
1805
|
+
mkdirSync3(join3(exportDir, "complete"), { recursive: true });
|
|
1271
1806
|
const resultFiles = [];
|
|
1272
1807
|
const summaryTree = generateSummaryTree(issue, 0, depth);
|
|
1273
|
-
const summaryPath =
|
|
1274
|
-
|
|
1808
|
+
const summaryPath = join3(exportDir, "tree.summary.json");
|
|
1809
|
+
writeFileSync4(summaryPath, JSON.stringify(summaryTree, null, indent), "utf-8");
|
|
1275
1810
|
const summarySize = statSync(summaryPath).size;
|
|
1276
1811
|
resultFiles.push({ path: "tree.summary.json", size: summarySize, type: "tree" });
|
|
1277
1812
|
const allIssues = flattenIssues(issue);
|
|
1278
1813
|
for (const iss of allIssues) {
|
|
1279
1814
|
const standardData = extractStandardFields(iss);
|
|
1280
|
-
const standardPath =
|
|
1281
|
-
|
|
1815
|
+
const standardPath = join3(exportDir, "standard", `${iss.id}.json`);
|
|
1816
|
+
writeFileSync4(standardPath, JSON.stringify(standardData, null, indent), "utf-8");
|
|
1282
1817
|
const standardSize = statSync(standardPath).size;
|
|
1283
1818
|
resultFiles.push({ path: `standard/${iss.id}.json`, size: standardSize, type: "standard" });
|
|
1284
1819
|
}
|
|
1285
1820
|
for (const iss of allIssues) {
|
|
1286
1821
|
const completeData = extractCompleteFields(iss);
|
|
1287
|
-
const completePath =
|
|
1288
|
-
|
|
1822
|
+
const completePath = join3(exportDir, "complete", `${iss.id}.json`);
|
|
1823
|
+
writeFileSync4(completePath, JSON.stringify(completeData, null, indent), "utf-8");
|
|
1289
1824
|
const completeSize = statSync(completePath).size;
|
|
1290
1825
|
resultFiles.push({ path: `complete/${iss.id}.json`, size: completeSize, type: "complete" });
|
|
1291
1826
|
}
|
|
@@ -1936,6 +2471,117 @@ function createIssueCommand() {
|
|
|
1936
2471
|
process.exit(1);
|
|
1937
2472
|
}
|
|
1938
2473
|
});
|
|
2474
|
+
issueCmd.command("batch-create").description("\u4ECE Markdown \u6279\u91CF\u521B\u5EFA\u5B50\u5355").option("--parent-id <id>", "\u7236\u5355 ID").option("--parent-url <url>", "\u7236\u5355 PM \u94FE\u63A5").option("--markdown <file>", "Markdown \u6587\u4EF6\u8DEF\u5F84").option("--stdin", "\u4ECE\u6807\u51C6\u8F93\u5165\u8BFB\u53D6 Markdown").option("--assigned-to-mail <email>", "\u6307\u6D3E\u4EBA\u90AE\u7BB1\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u7684 userMail\uFF09").option("--interval <ms>", "\u521B\u5EFA\u95F4\u9694\uFF08\u6BEB\u79D2\uFF09", "5000").option("--dry-run", "\u6A21\u62DF\u8FD0\u884C\uFF0C\u4E0D\u5B9E\u9645\u521B\u5EFA").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) => {
|
|
2475
|
+
let parentId;
|
|
2476
|
+
let parentHost;
|
|
2477
|
+
if (options.parentUrl) {
|
|
2478
|
+
const linkInfo = parsePmLink(options.parentUrl);
|
|
2479
|
+
if (!linkInfo) {
|
|
2480
|
+
outputError("\u65E0\u6548\u7684\u7236\u5355 PM \u94FE\u63A5\u683C\u5F0F", options.pretty);
|
|
2481
|
+
process.exit(1);
|
|
2482
|
+
}
|
|
2483
|
+
parentId = parseInt(linkInfo.issueId, 10);
|
|
2484
|
+
parentHost = linkInfo.host;
|
|
2485
|
+
} else if (options.parentId) {
|
|
2486
|
+
const cleanId = options.parentId.replace(/^#/, "");
|
|
2487
|
+
parentId = parseInt(cleanId, 10);
|
|
2488
|
+
if (isNaN(parentId)) {
|
|
2489
|
+
outputError("\u65E0\u6548\u7684\u7236\u5355 ID", options.pretty);
|
|
2490
|
+
process.exit(1);
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
if (!parentId) {
|
|
2494
|
+
outputError("\u8BF7\u63D0\u4F9B\u7236\u5355 ID\uFF08--parent-id\uFF09\u6216 PM \u94FE\u63A5\uFF08--parent-url\uFF09", options.pretty);
|
|
2495
|
+
process.exit(1);
|
|
2496
|
+
}
|
|
2497
|
+
let markdown;
|
|
2498
|
+
if (options.stdin) {
|
|
2499
|
+
const chunks = [];
|
|
2500
|
+
for await (const chunk of process.stdin) {
|
|
2501
|
+
chunks.push(chunk);
|
|
2502
|
+
}
|
|
2503
|
+
markdown = Buffer.concat(chunks).toString("utf-8");
|
|
2504
|
+
} else if (options.markdown) {
|
|
2505
|
+
try {
|
|
2506
|
+
markdown = readFileSync3(options.markdown, "utf-8");
|
|
2507
|
+
} catch {
|
|
2508
|
+
outputError(`\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25: ${options.markdown}`, options.pretty);
|
|
2509
|
+
process.exit(1);
|
|
2510
|
+
}
|
|
2511
|
+
} else {
|
|
2512
|
+
outputError("\u8BF7\u63D0\u4F9B Markdown \u6587\u4EF6\u8DEF\u5F84\uFF08--markdown\uFF09\u6216\u4F7F\u7528\u6807\u51C6\u8F93\u5165\uFF08--stdin\uFF09", options.pretty);
|
|
2513
|
+
process.exit(1);
|
|
2514
|
+
}
|
|
2515
|
+
if (!markdown.trim()) {
|
|
2516
|
+
outputError("Markdown \u5185\u5BB9\u4E3A\u7A7A", options.pretty);
|
|
2517
|
+
process.exit(1);
|
|
2518
|
+
}
|
|
2519
|
+
const taskNodes = parseMarkdownToNodes(markdown);
|
|
2520
|
+
if (taskNodes.length === 0) {
|
|
2521
|
+
outputError("\u6CA1\u6709\u89E3\u6790\u5230\u4EFB\u4F55\u4EFB\u52A1\uFF0C\u8BF7\u68C0\u67E5 Markdown \u683C\u5F0F", options.pretty);
|
|
2522
|
+
process.exit(1);
|
|
2523
|
+
}
|
|
2524
|
+
const creds = resolveCredentials({
|
|
2525
|
+
...options,
|
|
2526
|
+
host: parentHost || options.host
|
|
2527
|
+
});
|
|
2528
|
+
const validation = validateCredentials(creds, ["token", "host"]);
|
|
2529
|
+
if (!validation.valid) {
|
|
2530
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
2531
|
+
process.exit(1);
|
|
2532
|
+
}
|
|
2533
|
+
let assignedToMail = options.assignedToMail;
|
|
2534
|
+
if (!assignedToMail) {
|
|
2535
|
+
const config = readConfig(options.config);
|
|
2536
|
+
assignedToMail = config.default?.userMail;
|
|
2537
|
+
if (options.profile && config.profiles[options.profile]?.userMail) {
|
|
2538
|
+
assignedToMail = config.profiles[options.profile].userMail;
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
if (!assignedToMail) {
|
|
2542
|
+
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);
|
|
2543
|
+
process.exit(1);
|
|
2544
|
+
}
|
|
2545
|
+
const interval = parseInt(options.interval, 10);
|
|
2546
|
+
const dryRun = options.dryRun || false;
|
|
2547
|
+
const result = await issueService.batchCreateFromMarkdown(
|
|
2548
|
+
creds.token,
|
|
2549
|
+
creds.host,
|
|
2550
|
+
creds.project || "",
|
|
2551
|
+
parentId,
|
|
2552
|
+
markdown,
|
|
2553
|
+
assignedToMail,
|
|
2554
|
+
{ dryRun, interval }
|
|
2555
|
+
);
|
|
2556
|
+
if (result.success && result.data) {
|
|
2557
|
+
const output = {
|
|
2558
|
+
success: true,
|
|
2559
|
+
dryRun,
|
|
2560
|
+
parentId,
|
|
2561
|
+
assignedToMail,
|
|
2562
|
+
summary: {
|
|
2563
|
+
totalTasks: result.data.totalTasks,
|
|
2564
|
+
totalEstimatedHours: result.data.totalEstimatedHours,
|
|
2565
|
+
totalCreated: result.data.totalCreated,
|
|
2566
|
+
totalFailed: result.data.totalFailed
|
|
2567
|
+
},
|
|
2568
|
+
// 树形结构预览
|
|
2569
|
+
taskTree: formatTaskTree(taskNodes, "", true, true, assignedToMail.split("@")[0]),
|
|
2570
|
+
created: result.data.created,
|
|
2571
|
+
failed: result.data.failed.length > 0 ? result.data.failed : void 0
|
|
2572
|
+
};
|
|
2573
|
+
smartOutput(output, {
|
|
2574
|
+
stdout: options.stdout,
|
|
2575
|
+
output: options.output,
|
|
2576
|
+
command: "issue-batch-create",
|
|
2577
|
+
identifier: `${parentId}`,
|
|
2578
|
+
pretty: options.pretty
|
|
2579
|
+
});
|
|
2580
|
+
} else {
|
|
2581
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u6279\u91CF\u521B\u5EFA\u5931\u8D25", options.pretty);
|
|
2582
|
+
process.exit(1);
|
|
2583
|
+
}
|
|
2584
|
+
});
|
|
1939
2585
|
return issueCmd;
|
|
1940
2586
|
}
|
|
1941
2587
|
|
|
@@ -2330,6 +2976,9 @@ function createTimeCommand() {
|
|
|
2330
2976
|
const configUserId = getConfigValue("userId", options.config);
|
|
2331
2977
|
if (configUserId) {
|
|
2332
2978
|
userId = parseInt(configUserId, 10);
|
|
2979
|
+
} else {
|
|
2980
|
+
outputError("\u672A\u914D\u7F6E\u7528\u6237 ID\uFF0C\u8BF7\u5148\u8BBE\u7F6E: pm-cli config set user-id <\u60A8\u7684\u7528\u6237ID>", options.pretty);
|
|
2981
|
+
process.exit(1);
|
|
2333
2982
|
}
|
|
2334
2983
|
}
|
|
2335
2984
|
logger_default.info(`\u67E5\u8BE2\u5DE5\u65F6\u7EDF\u8BA1: ${periodLabel}`);
|
|
@@ -2454,15 +3103,32 @@ function createProjectCommand() {
|
|
|
2454
3103
|
}
|
|
2455
3104
|
|
|
2456
3105
|
// src/index.ts
|
|
2457
|
-
import { readFileSync as
|
|
2458
|
-
import { dirname as dirname2, join as
|
|
3106
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
3107
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
2459
3108
|
import { fileURLToPath } from "url";
|
|
2460
3109
|
var __filename = fileURLToPath(import.meta.url);
|
|
2461
3110
|
var __dirname = dirname2(__filename);
|
|
2462
|
-
var packageJson = JSON.parse(
|
|
3111
|
+
var packageJson = JSON.parse(readFileSync4(join4(__dirname, "../package.json"), "utf-8"));
|
|
2463
3112
|
var version = packageJson.version;
|
|
3113
|
+
initTelemetry(version);
|
|
3114
|
+
trackSessionStarted();
|
|
3115
|
+
function extractCommandNames(cmd) {
|
|
3116
|
+
const names = [];
|
|
3117
|
+
let current = cmd;
|
|
3118
|
+
while (current) {
|
|
3119
|
+
const name = current.name();
|
|
3120
|
+
if (name && name !== "pm-cli") {
|
|
3121
|
+
names.unshift(name);
|
|
3122
|
+
}
|
|
3123
|
+
current = current.parent;
|
|
3124
|
+
}
|
|
3125
|
+
return {
|
|
3126
|
+
command: names[0] || "unknown",
|
|
3127
|
+
subcommand: names.slice(1).join(":") || ""
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
2464
3130
|
var program = new Command6();
|
|
2465
|
-
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) => {
|
|
3131
|
+
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, actionCommand) => {
|
|
2466
3132
|
const opts = thisCommand.opts();
|
|
2467
3133
|
if (opts.debug) {
|
|
2468
3134
|
logger_default.setLevel("debug");
|
|
@@ -2474,11 +3140,33 @@ program.name("pm-cli").description("\u7F51\u6613\u6613\u534F\u4F5C (PM NetEase)
|
|
|
2474
3140
|
if (opts.mapping === false) {
|
|
2475
3141
|
setKeyMappingEnabled(false);
|
|
2476
3142
|
}
|
|
3143
|
+
const { command, subcommand } = extractCommandNames(actionCommand);
|
|
3144
|
+
const actionOpts = actionCommand.opts();
|
|
3145
|
+
startCommandTracking(command, subcommand, {
|
|
3146
|
+
has_profile: !!actionOpts.profile,
|
|
3147
|
+
has_url_input: !!actionOpts.url,
|
|
3148
|
+
depth: actionOpts.depth ? parseInt(actionOpts.depth, 10) : void 0,
|
|
3149
|
+
output_mode: actionOpts.stdout ? "stdout" : actionOpts.raw ? "raw" : "file"
|
|
3150
|
+
});
|
|
3151
|
+
}).hook("postAction", () => {
|
|
3152
|
+
endCommandTracking();
|
|
2477
3153
|
});
|
|
2478
3154
|
program.addCommand(createTestCommand());
|
|
2479
3155
|
program.addCommand(createConfigCommand());
|
|
2480
3156
|
program.addCommand(createIssueCommand());
|
|
2481
3157
|
program.addCommand(createTimeCommand());
|
|
2482
3158
|
program.addCommand(createProjectCommand());
|
|
2483
|
-
|
|
3159
|
+
async function main() {
|
|
3160
|
+
try {
|
|
3161
|
+
await program.parseAsync();
|
|
3162
|
+
} catch (error) {
|
|
3163
|
+
failCommandTracking(error);
|
|
3164
|
+
throw error;
|
|
3165
|
+
} finally {
|
|
3166
|
+
await shutdownTelemetry();
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
main().catch(() => {
|
|
3170
|
+
process.exit(1);
|
|
3171
|
+
});
|
|
2484
3172
|
//# sourceMappingURL=index.js.map
|