@hangox/pm-cli 0.2.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +711 -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,92 @@ 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 hoursRegex = /[((]([\d.]+)[))]/;
|
|
859
|
+
const hoursMatch = text.match(hoursRegex);
|
|
860
|
+
if (hoursMatch) {
|
|
861
|
+
const value = parseFloat(hoursMatch[1]);
|
|
862
|
+
return isNaN(value) ? void 0 : value;
|
|
863
|
+
}
|
|
864
|
+
return void 0;
|
|
865
|
+
}
|
|
866
|
+
function parseMarkdownLine(line) {
|
|
867
|
+
const leadingSpaces = line.match(/^(\s*)/)?.[1].length || 0;
|
|
868
|
+
const level = Math.floor(leadingSpaces / 2);
|
|
869
|
+
const cleanLine = line.trimStart().replace(/^[*-]\s*/, "").trim();
|
|
870
|
+
const estimatedHours = extractHours(cleanLine);
|
|
871
|
+
const subject = cleanLine.replace(/\s*[((][^))]*[))]/g, "").trim();
|
|
872
|
+
return { level, subject, estimatedHours };
|
|
873
|
+
}
|
|
874
|
+
function parseMarkdownToNodes(markdown) {
|
|
875
|
+
const lines = markdown.trim().split("\n").filter((line) => line.trim().length > 0).filter((line) => /^\s*[*-]/.test(line));
|
|
876
|
+
const rootNodes = [];
|
|
877
|
+
const nodeStack = [];
|
|
878
|
+
for (const line of lines) {
|
|
879
|
+
const { level, subject, estimatedHours } = parseMarkdownLine(line);
|
|
880
|
+
if (!subject) {
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
const node = {
|
|
884
|
+
subject,
|
|
885
|
+
estimatedHours,
|
|
886
|
+
children: []
|
|
887
|
+
};
|
|
888
|
+
while (nodeStack.length > 0 && nodeStack[nodeStack.length - 1].level >= level) {
|
|
889
|
+
nodeStack.pop();
|
|
890
|
+
}
|
|
891
|
+
if (nodeStack.length === 0) {
|
|
892
|
+
rootNodes.push(node);
|
|
893
|
+
} else {
|
|
894
|
+
nodeStack[nodeStack.length - 1].node.children.push(node);
|
|
895
|
+
}
|
|
896
|
+
nodeStack.push({ level, node });
|
|
897
|
+
}
|
|
898
|
+
return rootNodes;
|
|
899
|
+
}
|
|
900
|
+
function countTasks(nodes) {
|
|
901
|
+
let count = 0;
|
|
902
|
+
for (const node of nodes) {
|
|
903
|
+
count += 1;
|
|
904
|
+
count += countTasks(node.children);
|
|
905
|
+
}
|
|
906
|
+
return count;
|
|
907
|
+
}
|
|
908
|
+
function sumEstimatedHours(nodes) {
|
|
909
|
+
let total = 0;
|
|
910
|
+
for (const node of nodes) {
|
|
911
|
+
if (node.estimatedHours) {
|
|
912
|
+
total += node.estimatedHours;
|
|
913
|
+
}
|
|
914
|
+
total += sumEstimatedHours(node.children);
|
|
915
|
+
}
|
|
916
|
+
return total;
|
|
917
|
+
}
|
|
918
|
+
function formatTaskTree(nodes, prefix = "", _isLast = true, showAssignee = false, assigneeName) {
|
|
919
|
+
const lines = [];
|
|
920
|
+
nodes.forEach((node, index) => {
|
|
921
|
+
const isLastNode = index === nodes.length - 1;
|
|
922
|
+
const connector = prefix === "" ? "* " : isLastNode ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
923
|
+
const hoursStr = node.estimatedHours !== void 0 ? ` (${node.estimatedHours}d)` : "";
|
|
924
|
+
const assigneeStr = showAssignee && assigneeName ? ` \u2192 ${assigneeName}` : "";
|
|
925
|
+
lines.push(`${prefix}${connector}${node.subject}${hoursStr}${assigneeStr}`);
|
|
926
|
+
if (node.children.length > 0) {
|
|
927
|
+
const childPrefix = prefix === "" ? " " : prefix + (isLastNode ? " " : "\u2502 ");
|
|
928
|
+
const childLines = formatTaskTree(node.children, childPrefix, isLastNode, showAssignee, assigneeName);
|
|
929
|
+
lines.push(childLines);
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
return lines.join("\n");
|
|
933
|
+
}
|
|
637
934
|
|
|
638
935
|
// src/services/issue-service.ts
|
|
639
936
|
function sleep(ms) {
|
|
@@ -903,6 +1200,7 @@ var IssueService = class {
|
|
|
903
1200
|
skipExisting
|
|
904
1201
|
});
|
|
905
1202
|
const result = {
|
|
1203
|
+
totalTasks: 0,
|
|
906
1204
|
totalCreated: 0,
|
|
907
1205
|
totalSkipped: 0,
|
|
908
1206
|
totalFailed: 0,
|
|
@@ -920,6 +1218,8 @@ var IssueService = class {
|
|
|
920
1218
|
};
|
|
921
1219
|
}
|
|
922
1220
|
const sourceIssue = sourceResult.data;
|
|
1221
|
+
result.totalTasks = this.countAllChildren(sourceIssue);
|
|
1222
|
+
logger_default.info(`\u6E90\u7236\u5355\u5171\u6709 ${result.totalTasks} \u4E2A\u5B50\u4EFB\u52A1`);
|
|
923
1223
|
logger_default.info("\u6B63\u5728\u83B7\u53D6\u76EE\u6807\u7236\u5355\u4FE1\u606F...");
|
|
924
1224
|
const targetResult = await this.getIssue(token, host, project, targetParentId, false, false);
|
|
925
1225
|
if (!targetResult.success || !targetResult.data) {
|
|
@@ -965,6 +1265,17 @@ var IssueService = class {
|
|
|
965
1265
|
};
|
|
966
1266
|
}
|
|
967
1267
|
}
|
|
1268
|
+
/**
|
|
1269
|
+
* 计算所有子任务数量(递归)
|
|
1270
|
+
*/
|
|
1271
|
+
countAllChildren(issue) {
|
|
1272
|
+
let count = 0;
|
|
1273
|
+
for (const child of issue.children || []) {
|
|
1274
|
+
count++;
|
|
1275
|
+
count += this.countAllChildren(child);
|
|
1276
|
+
}
|
|
1277
|
+
return count;
|
|
1278
|
+
}
|
|
968
1279
|
/**
|
|
969
1280
|
* 收集所有任务名称(递归)
|
|
970
1281
|
*/
|
|
@@ -994,6 +1305,8 @@ var IssueService = class {
|
|
|
994
1305
|
subject: taskName,
|
|
995
1306
|
reason: "\u76EE\u6807\u7236\u5355\u4E0B\u5DF2\u5B58\u5728\u540C\u540D\u4EFB\u52A1"
|
|
996
1307
|
});
|
|
1308
|
+
const progress = result.totalCreated + result.totalSkipped + result.totalFailed;
|
|
1309
|
+
console.error(`[${progress}/${result.totalTasks}] \u23ED\uFE0F \u8DF3\u8FC7: #${sourceId} ${taskName} (\u5DF2\u5B58\u5728)`);
|
|
997
1310
|
continue;
|
|
998
1311
|
}
|
|
999
1312
|
const isLeafNode = !child.children || child.children.length === 0;
|
|
@@ -1083,6 +1396,8 @@ var IssueService = class {
|
|
|
1083
1396
|
subject: taskName,
|
|
1084
1397
|
parentId: targetParentId
|
|
1085
1398
|
});
|
|
1399
|
+
const progress = result.totalCreated + result.totalSkipped + result.totalFailed;
|
|
1400
|
+
console.error(`[${progress}/${result.totalTasks}] \u2705 \u540C\u6B65\u6210\u529F: #${newId} \u2190 #${sourceId} ${taskName}`);
|
|
1086
1401
|
if (child.children && child.children.length > 0) {
|
|
1087
1402
|
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
1403
|
await this.syncChildrenRecursive(
|
|
@@ -1099,27 +1414,241 @@ var IssueService = class {
|
|
|
1099
1414
|
);
|
|
1100
1415
|
}
|
|
1101
1416
|
} else {
|
|
1102
|
-
|
|
1417
|
+
const errorMsg = createResult.message || createResult.api_error_msg || "\u521B\u5EFA\u5931\u8D25";
|
|
1418
|
+
logger_default.error(`\u274C \u521B\u5EFA\u5B50\u4EFB\u52A1\u5931\u8D25: ${taskName}`, errorMsg);
|
|
1103
1419
|
result.totalFailed++;
|
|
1104
1420
|
result.failed.push({
|
|
1105
1421
|
sourceId,
|
|
1106
1422
|
subject: taskName,
|
|
1107
|
-
error:
|
|
1423
|
+
error: errorMsg
|
|
1108
1424
|
});
|
|
1425
|
+
const progress = result.totalCreated + result.totalSkipped + result.totalFailed;
|
|
1426
|
+
console.error(`[${progress}/${result.totalTasks}] \u274C \u540C\u6B65\u5931\u8D25: #${sourceId} ${taskName} - ${errorMsg}`);
|
|
1109
1427
|
}
|
|
1110
1428
|
} catch (error) {
|
|
1429
|
+
const errorMsg = error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF";
|
|
1111
1430
|
logger_default.error(`\u274C \u521B\u5EFA\u5B50\u4EFB\u52A1\u65F6\u53D1\u751F\u9519\u8BEF: ${taskName}`, error);
|
|
1112
1431
|
result.totalFailed++;
|
|
1113
1432
|
result.failed.push({
|
|
1114
1433
|
sourceId,
|
|
1115
1434
|
subject: taskName,
|
|
1116
|
-
error:
|
|
1435
|
+
error: errorMsg
|
|
1117
1436
|
});
|
|
1437
|
+
const progress = result.totalCreated + result.totalSkipped + result.totalFailed;
|
|
1438
|
+
console.error(`[${progress}/${result.totalTasks}] \u274C \u540C\u6B65\u5931\u8D25: #${sourceId} ${taskName} - ${errorMsg}`);
|
|
1118
1439
|
}
|
|
1119
1440
|
await sleep(500);
|
|
1120
1441
|
}
|
|
1121
1442
|
}
|
|
1122
1443
|
}
|
|
1444
|
+
/**
|
|
1445
|
+
* 从 Markdown 批量创建子单
|
|
1446
|
+
* @param parentId 父单 ID
|
|
1447
|
+
* @param markdown Markdown 文本
|
|
1448
|
+
* @param assignedToMail 指派人邮箱
|
|
1449
|
+
* @param options 选项
|
|
1450
|
+
*/
|
|
1451
|
+
async batchCreateFromMarkdown(token, host, project, parentId, markdown, assignedToMail, options) {
|
|
1452
|
+
const { dryRun = false, interval = 5e3 } = options || {};
|
|
1453
|
+
logger_default.info("\u5F00\u59CB\u6279\u91CF\u521B\u5EFA\u5B50\u5355", {
|
|
1454
|
+
parentId,
|
|
1455
|
+
assignedToMail,
|
|
1456
|
+
dryRun,
|
|
1457
|
+
interval
|
|
1458
|
+
});
|
|
1459
|
+
const result = {
|
|
1460
|
+
totalCreated: 0,
|
|
1461
|
+
totalFailed: 0,
|
|
1462
|
+
totalTasks: 0,
|
|
1463
|
+
totalEstimatedHours: 0,
|
|
1464
|
+
created: [],
|
|
1465
|
+
failed: []
|
|
1466
|
+
};
|
|
1467
|
+
try {
|
|
1468
|
+
const taskNodes = parseMarkdownToNodes(markdown);
|
|
1469
|
+
if (taskNodes.length === 0) {
|
|
1470
|
+
return {
|
|
1471
|
+
success: false,
|
|
1472
|
+
message: "\u6CA1\u6709\u89E3\u6790\u5230\u4EFB\u4F55\u4EFB\u52A1\uFF0C\u8BF7\u68C0\u67E5 Markdown \u683C\u5F0F"
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
result.totalTasks = countTasks(taskNodes);
|
|
1476
|
+
result.totalEstimatedHours = sumEstimatedHours(taskNodes);
|
|
1477
|
+
logger_default.info("Markdown \u89E3\u6790\u5B8C\u6210", {
|
|
1478
|
+
totalTasks: result.totalTasks,
|
|
1479
|
+
totalEstimatedHours: result.totalEstimatedHours
|
|
1480
|
+
});
|
|
1481
|
+
logger_default.info("\u6B63\u5728\u83B7\u53D6\u7236\u5355\u4FE1\u606F...");
|
|
1482
|
+
const parentResult = await this.getIssue(token, host, project, parentId, false, false);
|
|
1483
|
+
if (!parentResult.success || !parentResult.data) {
|
|
1484
|
+
return {
|
|
1485
|
+
success: false,
|
|
1486
|
+
message: `\u83B7\u53D6\u7236\u5355 #${parentId} \u5931\u8D25: ${parentResult.message || "\u672A\u77E5\u9519\u8BEF"}`
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
const parentInfo = parentResult.data;
|
|
1490
|
+
logger_default.info("\u5F00\u59CB\u9012\u5F52\u521B\u5EFA\u5B50\u5355...");
|
|
1491
|
+
await this.batchCreateRecursive(
|
|
1492
|
+
token,
|
|
1493
|
+
host,
|
|
1494
|
+
project,
|
|
1495
|
+
taskNodes,
|
|
1496
|
+
parentId,
|
|
1497
|
+
parentInfo,
|
|
1498
|
+
assignedToMail,
|
|
1499
|
+
dryRun,
|
|
1500
|
+
interval,
|
|
1501
|
+
result
|
|
1502
|
+
);
|
|
1503
|
+
logger_default.info("\u6279\u91CF\u521B\u5EFA\u5B8C\u6210", {
|
|
1504
|
+
totalCreated: result.totalCreated,
|
|
1505
|
+
totalFailed: result.totalFailed
|
|
1506
|
+
});
|
|
1507
|
+
return { success: true, data: result };
|
|
1508
|
+
} catch (error) {
|
|
1509
|
+
logger_default.error("\u6279\u91CF\u521B\u5EFA\u5B50\u5355\u65F6\u53D1\u751F\u9519\u8BEF", error);
|
|
1510
|
+
return {
|
|
1511
|
+
success: false,
|
|
1512
|
+
message: `\u6279\u91CF\u521B\u5EFA\u5B50\u5355\u65F6\u53D1\u751F\u9519\u8BEF: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* 递归批量创建子单
|
|
1518
|
+
*/
|
|
1519
|
+
async batchCreateRecursive(token, host, project, taskNodes, targetParentId, parentInfo, assignedToMail, dryRun, interval, result) {
|
|
1520
|
+
for (const node of taskNodes) {
|
|
1521
|
+
const taskName = node.subject;
|
|
1522
|
+
const isLeafNode = node.children.length === 0;
|
|
1523
|
+
const createParams = {
|
|
1524
|
+
token,
|
|
1525
|
+
host,
|
|
1526
|
+
project,
|
|
1527
|
+
parent_issue_id: targetParentId,
|
|
1528
|
+
subject: taskName,
|
|
1529
|
+
// 继承自父单
|
|
1530
|
+
tracker: parentInfo.tracker?.name,
|
|
1531
|
+
status: "\u65B0\u5EFA",
|
|
1532
|
+
// 指派人
|
|
1533
|
+
assigned_to_mail: assignedToMail,
|
|
1534
|
+
// 只有叶子节点才设置工时
|
|
1535
|
+
estimated_hours: isLeafNode ? node.estimatedHours : void 0
|
|
1536
|
+
};
|
|
1537
|
+
const parentWithVersion = parentInfo;
|
|
1538
|
+
if (parentWithVersion.fixed_version?.name) {
|
|
1539
|
+
createParams.version = parentWithVersion.fixed_version.name;
|
|
1540
|
+
}
|
|
1541
|
+
const parentWithCustomFields = parentInfo;
|
|
1542
|
+
if (parentWithCustomFields.custom_fields && parentWithCustomFields.custom_fields.length > 0) {
|
|
1543
|
+
const customFieldMap = {};
|
|
1544
|
+
const followsMails = [];
|
|
1545
|
+
for (const field of parentWithCustomFields.custom_fields) {
|
|
1546
|
+
if (field.value !== null && field.value !== void 0 && field.value !== "") {
|
|
1547
|
+
if (field.identify === "IssuesQCFollow") {
|
|
1548
|
+
const followsValue = field.value;
|
|
1549
|
+
if (Array.isArray(followsValue)) {
|
|
1550
|
+
for (const item of followsValue) {
|
|
1551
|
+
if (item.user?.mail) {
|
|
1552
|
+
followsMails.push(item.user.mail);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
} else {
|
|
1557
|
+
customFieldMap[field.id] = field.value;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
if (Object.keys(customFieldMap).length > 0) {
|
|
1562
|
+
createParams.custom_field = JSON.stringify(customFieldMap);
|
|
1563
|
+
}
|
|
1564
|
+
if (followsMails.length > 0) {
|
|
1565
|
+
createParams.follows = followsMails;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
logger_default.info(`\u6B63\u5728\u521B\u5EFA\u5B50\u4EFB\u52A1: ${taskName}`, { targetParentId, isLeafNode, estimatedHours: node.estimatedHours });
|
|
1569
|
+
if (dryRun) {
|
|
1570
|
+
logger_default.info(`[\u6A21\u62DF] \u5C06\u521B\u5EFA\u5B50\u4EFB\u52A1: ${taskName}`);
|
|
1571
|
+
result.totalCreated++;
|
|
1572
|
+
result.created.push({
|
|
1573
|
+
newId: 0,
|
|
1574
|
+
// 模拟模式没有真实 ID
|
|
1575
|
+
subject: taskName,
|
|
1576
|
+
parentId: targetParentId,
|
|
1577
|
+
estimatedHours: node.estimatedHours
|
|
1578
|
+
});
|
|
1579
|
+
if (node.children.length > 0) {
|
|
1580
|
+
await this.batchCreateRecursive(
|
|
1581
|
+
token,
|
|
1582
|
+
host,
|
|
1583
|
+
project,
|
|
1584
|
+
node.children,
|
|
1585
|
+
0,
|
|
1586
|
+
// 模拟模式下没有真实的新 ID
|
|
1587
|
+
parentInfo,
|
|
1588
|
+
assignedToMail,
|
|
1589
|
+
dryRun,
|
|
1590
|
+
interval,
|
|
1591
|
+
result
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
} else {
|
|
1595
|
+
try {
|
|
1596
|
+
const createResult = await this.createIssue(createParams);
|
|
1597
|
+
if (createResult.success && createResult.data) {
|
|
1598
|
+
const newId = createResult.data.id;
|
|
1599
|
+
logger_default.info(`\u2705 \u6210\u529F\u521B\u5EFA\u5B50\u4EFB\u52A1: ID=${newId}, \u6807\u9898=${taskName}`);
|
|
1600
|
+
result.totalCreated++;
|
|
1601
|
+
result.created.push({
|
|
1602
|
+
newId,
|
|
1603
|
+
subject: taskName,
|
|
1604
|
+
parentId: targetParentId,
|
|
1605
|
+
estimatedHours: node.estimatedHours
|
|
1606
|
+
});
|
|
1607
|
+
const progress = result.totalCreated + result.totalFailed;
|
|
1608
|
+
console.error(`[${progress}/${result.totalTasks}] \u2705 \u521B\u5EFA\u6210\u529F: #${newId} ${taskName}`);
|
|
1609
|
+
if (node.children.length > 0) {
|
|
1610
|
+
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...`);
|
|
1611
|
+
await this.batchCreateRecursive(
|
|
1612
|
+
token,
|
|
1613
|
+
host,
|
|
1614
|
+
project,
|
|
1615
|
+
node.children,
|
|
1616
|
+
newId,
|
|
1617
|
+
parentInfo,
|
|
1618
|
+
assignedToMail,
|
|
1619
|
+
dryRun,
|
|
1620
|
+
interval,
|
|
1621
|
+
result
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1624
|
+
} else {
|
|
1625
|
+
const errorMsg = createResult.message || createResult.api_error_msg || "\u521B\u5EFA\u5931\u8D25";
|
|
1626
|
+
logger_default.error(`\u274C \u521B\u5EFA\u5B50\u4EFB\u52A1\u5931\u8D25: ${taskName}`, errorMsg);
|
|
1627
|
+
result.totalFailed++;
|
|
1628
|
+
result.failed.push({
|
|
1629
|
+
subject: taskName,
|
|
1630
|
+
parentId: targetParentId,
|
|
1631
|
+
error: errorMsg
|
|
1632
|
+
});
|
|
1633
|
+
const progress = result.totalCreated + result.totalFailed;
|
|
1634
|
+
console.error(`[${progress}/${result.totalTasks}] \u274C \u521B\u5EFA\u5931\u8D25: ${taskName} - ${errorMsg}`);
|
|
1635
|
+
}
|
|
1636
|
+
} catch (error) {
|
|
1637
|
+
const errorMsg = error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF";
|
|
1638
|
+
logger_default.error(`\u274C \u521B\u5EFA\u5B50\u4EFB\u52A1\u65F6\u53D1\u751F\u9519\u8BEF: ${taskName}`, error);
|
|
1639
|
+
result.totalFailed++;
|
|
1640
|
+
result.failed.push({
|
|
1641
|
+
subject: taskName,
|
|
1642
|
+
parentId: targetParentId,
|
|
1643
|
+
error: errorMsg
|
|
1644
|
+
});
|
|
1645
|
+
const progress = result.totalCreated + result.totalFailed;
|
|
1646
|
+
console.error(`[${progress}/${result.totalTasks}] \u274C \u521B\u5EFA\u5931\u8D25: ${taskName} - ${errorMsg}`);
|
|
1647
|
+
}
|
|
1648
|
+
await sleep(interval);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1123
1652
|
};
|
|
1124
1653
|
var issueService = new IssueService();
|
|
1125
1654
|
|
|
@@ -1149,8 +1678,8 @@ function parsePmLink(url) {
|
|
|
1149
1678
|
}
|
|
1150
1679
|
|
|
1151
1680
|
// src/utils/issue-exporter.ts
|
|
1152
|
-
import { writeFileSync as
|
|
1153
|
-
import { join as
|
|
1681
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, statSync } from "fs";
|
|
1682
|
+
import { join as join3 } from "path";
|
|
1154
1683
|
|
|
1155
1684
|
// src/utils/preset-extractor.ts
|
|
1156
1685
|
var SUMMARY_CUSTOM_FIELD_IDS = [84];
|
|
@@ -1265,27 +1794,27 @@ function generateExportDir(command, identifier) {
|
|
|
1265
1794
|
async function exportIssuePresets(issue, exportDir, options = {}) {
|
|
1266
1795
|
const { depth = -1, pretty = false } = options;
|
|
1267
1796
|
const indent = pretty ? 2 : 0;
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1797
|
+
mkdirSync3(exportDir, { recursive: true });
|
|
1798
|
+
mkdirSync3(join3(exportDir, "standard"), { recursive: true });
|
|
1799
|
+
mkdirSync3(join3(exportDir, "complete"), { recursive: true });
|
|
1271
1800
|
const resultFiles = [];
|
|
1272
1801
|
const summaryTree = generateSummaryTree(issue, 0, depth);
|
|
1273
|
-
const summaryPath =
|
|
1274
|
-
|
|
1802
|
+
const summaryPath = join3(exportDir, "tree.summary.json");
|
|
1803
|
+
writeFileSync4(summaryPath, JSON.stringify(summaryTree, null, indent), "utf-8");
|
|
1275
1804
|
const summarySize = statSync(summaryPath).size;
|
|
1276
1805
|
resultFiles.push({ path: "tree.summary.json", size: summarySize, type: "tree" });
|
|
1277
1806
|
const allIssues = flattenIssues(issue);
|
|
1278
1807
|
for (const iss of allIssues) {
|
|
1279
1808
|
const standardData = extractStandardFields(iss);
|
|
1280
|
-
const standardPath =
|
|
1281
|
-
|
|
1809
|
+
const standardPath = join3(exportDir, "standard", `${iss.id}.json`);
|
|
1810
|
+
writeFileSync4(standardPath, JSON.stringify(standardData, null, indent), "utf-8");
|
|
1282
1811
|
const standardSize = statSync(standardPath).size;
|
|
1283
1812
|
resultFiles.push({ path: `standard/${iss.id}.json`, size: standardSize, type: "standard" });
|
|
1284
1813
|
}
|
|
1285
1814
|
for (const iss of allIssues) {
|
|
1286
1815
|
const completeData = extractCompleteFields(iss);
|
|
1287
|
-
const completePath =
|
|
1288
|
-
|
|
1816
|
+
const completePath = join3(exportDir, "complete", `${iss.id}.json`);
|
|
1817
|
+
writeFileSync4(completePath, JSON.stringify(completeData, null, indent), "utf-8");
|
|
1289
1818
|
const completeSize = statSync(completePath).size;
|
|
1290
1819
|
resultFiles.push({ path: `complete/${iss.id}.json`, size: completeSize, type: "complete" });
|
|
1291
1820
|
}
|
|
@@ -1936,6 +2465,117 @@ function createIssueCommand() {
|
|
|
1936
2465
|
process.exit(1);
|
|
1937
2466
|
}
|
|
1938
2467
|
});
|
|
2468
|
+
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) => {
|
|
2469
|
+
let parentId;
|
|
2470
|
+
let parentHost;
|
|
2471
|
+
if (options.parentUrl) {
|
|
2472
|
+
const linkInfo = parsePmLink(options.parentUrl);
|
|
2473
|
+
if (!linkInfo) {
|
|
2474
|
+
outputError("\u65E0\u6548\u7684\u7236\u5355 PM \u94FE\u63A5\u683C\u5F0F", options.pretty);
|
|
2475
|
+
process.exit(1);
|
|
2476
|
+
}
|
|
2477
|
+
parentId = parseInt(linkInfo.issueId, 10);
|
|
2478
|
+
parentHost = linkInfo.host;
|
|
2479
|
+
} else if (options.parentId) {
|
|
2480
|
+
const cleanId = options.parentId.replace(/^#/, "");
|
|
2481
|
+
parentId = parseInt(cleanId, 10);
|
|
2482
|
+
if (isNaN(parentId)) {
|
|
2483
|
+
outputError("\u65E0\u6548\u7684\u7236\u5355 ID", options.pretty);
|
|
2484
|
+
process.exit(1);
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
if (!parentId) {
|
|
2488
|
+
outputError("\u8BF7\u63D0\u4F9B\u7236\u5355 ID\uFF08--parent-id\uFF09\u6216 PM \u94FE\u63A5\uFF08--parent-url\uFF09", options.pretty);
|
|
2489
|
+
process.exit(1);
|
|
2490
|
+
}
|
|
2491
|
+
let markdown;
|
|
2492
|
+
if (options.stdin) {
|
|
2493
|
+
const chunks = [];
|
|
2494
|
+
for await (const chunk of process.stdin) {
|
|
2495
|
+
chunks.push(chunk);
|
|
2496
|
+
}
|
|
2497
|
+
markdown = Buffer.concat(chunks).toString("utf-8");
|
|
2498
|
+
} else if (options.markdown) {
|
|
2499
|
+
try {
|
|
2500
|
+
markdown = readFileSync3(options.markdown, "utf-8");
|
|
2501
|
+
} catch {
|
|
2502
|
+
outputError(`\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25: ${options.markdown}`, options.pretty);
|
|
2503
|
+
process.exit(1);
|
|
2504
|
+
}
|
|
2505
|
+
} else {
|
|
2506
|
+
outputError("\u8BF7\u63D0\u4F9B Markdown \u6587\u4EF6\u8DEF\u5F84\uFF08--markdown\uFF09\u6216\u4F7F\u7528\u6807\u51C6\u8F93\u5165\uFF08--stdin\uFF09", options.pretty);
|
|
2507
|
+
process.exit(1);
|
|
2508
|
+
}
|
|
2509
|
+
if (!markdown.trim()) {
|
|
2510
|
+
outputError("Markdown \u5185\u5BB9\u4E3A\u7A7A", options.pretty);
|
|
2511
|
+
process.exit(1);
|
|
2512
|
+
}
|
|
2513
|
+
const taskNodes = parseMarkdownToNodes(markdown);
|
|
2514
|
+
if (taskNodes.length === 0) {
|
|
2515
|
+
outputError("\u6CA1\u6709\u89E3\u6790\u5230\u4EFB\u4F55\u4EFB\u52A1\uFF0C\u8BF7\u68C0\u67E5 Markdown \u683C\u5F0F", options.pretty);
|
|
2516
|
+
process.exit(1);
|
|
2517
|
+
}
|
|
2518
|
+
const creds = resolveCredentials({
|
|
2519
|
+
...options,
|
|
2520
|
+
host: parentHost || options.host
|
|
2521
|
+
});
|
|
2522
|
+
const validation = validateCredentials(creds, ["token", "host"]);
|
|
2523
|
+
if (!validation.valid) {
|
|
2524
|
+
outputError(`\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570: ${validation.missing.join(", ")}`, options.pretty);
|
|
2525
|
+
process.exit(1);
|
|
2526
|
+
}
|
|
2527
|
+
let assignedToMail = options.assignedToMail;
|
|
2528
|
+
if (!assignedToMail) {
|
|
2529
|
+
const config = readConfig(options.config);
|
|
2530
|
+
assignedToMail = config.default?.userMail;
|
|
2531
|
+
if (options.profile && config.profiles[options.profile]?.userMail) {
|
|
2532
|
+
assignedToMail = config.profiles[options.profile].userMail;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
if (!assignedToMail) {
|
|
2536
|
+
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);
|
|
2537
|
+
process.exit(1);
|
|
2538
|
+
}
|
|
2539
|
+
const interval = parseInt(options.interval, 10);
|
|
2540
|
+
const dryRun = options.dryRun || false;
|
|
2541
|
+
const result = await issueService.batchCreateFromMarkdown(
|
|
2542
|
+
creds.token,
|
|
2543
|
+
creds.host,
|
|
2544
|
+
creds.project || "",
|
|
2545
|
+
parentId,
|
|
2546
|
+
markdown,
|
|
2547
|
+
assignedToMail,
|
|
2548
|
+
{ dryRun, interval }
|
|
2549
|
+
);
|
|
2550
|
+
if (result.success && result.data) {
|
|
2551
|
+
const output = {
|
|
2552
|
+
success: true,
|
|
2553
|
+
dryRun,
|
|
2554
|
+
parentId,
|
|
2555
|
+
assignedToMail,
|
|
2556
|
+
summary: {
|
|
2557
|
+
totalTasks: result.data.totalTasks,
|
|
2558
|
+
totalEstimatedHours: result.data.totalEstimatedHours,
|
|
2559
|
+
totalCreated: result.data.totalCreated,
|
|
2560
|
+
totalFailed: result.data.totalFailed
|
|
2561
|
+
},
|
|
2562
|
+
// 树形结构预览
|
|
2563
|
+
taskTree: formatTaskTree(taskNodes, "", true, true, assignedToMail.split("@")[0]),
|
|
2564
|
+
created: result.data.created,
|
|
2565
|
+
failed: result.data.failed.length > 0 ? result.data.failed : void 0
|
|
2566
|
+
};
|
|
2567
|
+
smartOutput(output, {
|
|
2568
|
+
stdout: options.stdout,
|
|
2569
|
+
output: options.output,
|
|
2570
|
+
command: "issue-batch-create",
|
|
2571
|
+
identifier: `${parentId}`,
|
|
2572
|
+
pretty: options.pretty
|
|
2573
|
+
});
|
|
2574
|
+
} else {
|
|
2575
|
+
outputError(result.message || result.msg || result.api_error_msg || "\u6279\u91CF\u521B\u5EFA\u5931\u8D25", options.pretty);
|
|
2576
|
+
process.exit(1);
|
|
2577
|
+
}
|
|
2578
|
+
});
|
|
1939
2579
|
return issueCmd;
|
|
1940
2580
|
}
|
|
1941
2581
|
|
|
@@ -2006,6 +2646,17 @@ var TimeEntryService = class {
|
|
|
2006
2646
|
var timeEntryService = new TimeEntryService();
|
|
2007
2647
|
|
|
2008
2648
|
// src/commands/time/index.ts
|
|
2649
|
+
var WEEKDAY_NAMES = ["\u661F\u671F\u65E5", "\u661F\u671F\u4E00", "\u661F\u671F\u4E8C", "\u661F\u671F\u4E09", "\u661F\u671F\u56DB", "\u661F\u671F\u4E94", "\u661F\u671F\u516D"];
|
|
2650
|
+
function getTodayContext() {
|
|
2651
|
+
const now = /* @__PURE__ */ new Date();
|
|
2652
|
+
const year = now.getFullYear();
|
|
2653
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
2654
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
2655
|
+
return {
|
|
2656
|
+
today: `${year}-${month}-${day}`,
|
|
2657
|
+
todayWeekday: WEEKDAY_NAMES[now.getDay()]
|
|
2658
|
+
};
|
|
2659
|
+
}
|
|
2009
2660
|
function getWeekDateRange(week) {
|
|
2010
2661
|
const now = /* @__PURE__ */ new Date();
|
|
2011
2662
|
let offset = 0;
|
|
@@ -2106,6 +2757,7 @@ function createTimeCommand() {
|
|
|
2106
2757
|
}
|
|
2107
2758
|
const totalHours = allTimeEntries.reduce((sum, e) => sum + (e.hours || 0), 0);
|
|
2108
2759
|
outputSuccess({
|
|
2760
|
+
...getTodayContext(),
|
|
2109
2761
|
total_count: totalCount,
|
|
2110
2762
|
total_hours: Math.round(totalHours * 100) / 100,
|
|
2111
2763
|
project_summary: projectSummary,
|
|
@@ -2364,6 +3016,7 @@ function createTimeCommand() {
|
|
|
2364
3016
|
const remainingHours = Math.max(0, targetHours - totalHours);
|
|
2365
3017
|
totalHours = Math.round(totalHours * 100) / 100;
|
|
2366
3018
|
const output = {
|
|
3019
|
+
...getTodayContext(),
|
|
2367
3020
|
period: periodLabel,
|
|
2368
3021
|
userId: userId || "\u672A\u6307\u5B9A",
|
|
2369
3022
|
totalHours,
|
|
@@ -2441,15 +3094,32 @@ function createProjectCommand() {
|
|
|
2441
3094
|
}
|
|
2442
3095
|
|
|
2443
3096
|
// src/index.ts
|
|
2444
|
-
import { readFileSync as
|
|
2445
|
-
import { dirname as dirname2, join as
|
|
3097
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
3098
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
2446
3099
|
import { fileURLToPath } from "url";
|
|
2447
3100
|
var __filename = fileURLToPath(import.meta.url);
|
|
2448
3101
|
var __dirname = dirname2(__filename);
|
|
2449
|
-
var packageJson = JSON.parse(
|
|
3102
|
+
var packageJson = JSON.parse(readFileSync4(join4(__dirname, "../package.json"), "utf-8"));
|
|
2450
3103
|
var version = packageJson.version;
|
|
3104
|
+
initTelemetry(version);
|
|
3105
|
+
trackSessionStarted();
|
|
3106
|
+
function extractCommandNames(cmd) {
|
|
3107
|
+
const names = [];
|
|
3108
|
+
let current = cmd;
|
|
3109
|
+
while (current) {
|
|
3110
|
+
const name = current.name();
|
|
3111
|
+
if (name && name !== "pm-cli") {
|
|
3112
|
+
names.unshift(name);
|
|
3113
|
+
}
|
|
3114
|
+
current = current.parent;
|
|
3115
|
+
}
|
|
3116
|
+
return {
|
|
3117
|
+
command: names[0] || "unknown",
|
|
3118
|
+
subcommand: names.slice(1).join(":") || ""
|
|
3119
|
+
};
|
|
3120
|
+
}
|
|
2451
3121
|
var program = new Command6();
|
|
2452
|
-
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) => {
|
|
3122
|
+
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) => {
|
|
2453
3123
|
const opts = thisCommand.opts();
|
|
2454
3124
|
if (opts.debug) {
|
|
2455
3125
|
logger_default.setLevel("debug");
|
|
@@ -2461,11 +3131,33 @@ program.name("pm-cli").description("\u7F51\u6613\u6613\u534F\u4F5C (PM NetEase)
|
|
|
2461
3131
|
if (opts.mapping === false) {
|
|
2462
3132
|
setKeyMappingEnabled(false);
|
|
2463
3133
|
}
|
|
3134
|
+
const { command, subcommand } = extractCommandNames(actionCommand);
|
|
3135
|
+
const actionOpts = actionCommand.opts();
|
|
3136
|
+
startCommandTracking(command, subcommand, {
|
|
3137
|
+
has_profile: !!actionOpts.profile,
|
|
3138
|
+
has_url_input: !!actionOpts.url,
|
|
3139
|
+
depth: actionOpts.depth ? parseInt(actionOpts.depth, 10) : void 0,
|
|
3140
|
+
output_mode: actionOpts.stdout ? "stdout" : actionOpts.raw ? "raw" : "file"
|
|
3141
|
+
});
|
|
3142
|
+
}).hook("postAction", () => {
|
|
3143
|
+
endCommandTracking();
|
|
2464
3144
|
});
|
|
2465
3145
|
program.addCommand(createTestCommand());
|
|
2466
3146
|
program.addCommand(createConfigCommand());
|
|
2467
3147
|
program.addCommand(createIssueCommand());
|
|
2468
3148
|
program.addCommand(createTimeCommand());
|
|
2469
3149
|
program.addCommand(createProjectCommand());
|
|
2470
|
-
|
|
3150
|
+
async function main() {
|
|
3151
|
+
try {
|
|
3152
|
+
await program.parseAsync();
|
|
3153
|
+
} catch (error) {
|
|
3154
|
+
failCommandTracking(error);
|
|
3155
|
+
throw error;
|
|
3156
|
+
} finally {
|
|
3157
|
+
await shutdownTelemetry();
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
main().catch(() => {
|
|
3161
|
+
process.exit(1);
|
|
3162
|
+
});
|
|
2471
3163
|
//# sourceMappingURL=index.js.map
|