@hangox/mg-cli 1.0.9 → 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/cli.js CHANGED
@@ -1,7 +1,139 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/index.ts
4
- import { Command as Command12 } from "commander";
4
+ import { Command as Command13 } from "commander";
5
+
6
+ // src/cli/version-check.ts
7
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
8
+ import { homedir } from "os";
9
+ import { join as join2 } from "path";
10
+ import { execSync } from "child_process";
11
+
12
+ // src/shared/version.ts
13
+ import { readFileSync, existsSync } from "fs";
14
+ import { fileURLToPath } from "url";
15
+ import { dirname, join } from "path";
16
+ var cachedVersion = null;
17
+ function getVersion() {
18
+ if (cachedVersion !== null) {
19
+ return cachedVersion;
20
+ }
21
+ try {
22
+ const currentFile = fileURLToPath(import.meta.url);
23
+ const currentDir = dirname(currentFile);
24
+ const packageJsonPaths = [
25
+ join(currentDir, "..", "package.json"),
26
+ // dist/xxx.js -> ../package.json
27
+ join(currentDir, "..", "..", "package.json")
28
+ // src/shared/version.ts -> ../../package.json
29
+ ];
30
+ for (const packageJsonPath of packageJsonPaths) {
31
+ if (existsSync(packageJsonPath)) {
32
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
33
+ if (packageJson.name === "@hangox/mg-cli") {
34
+ cachedVersion = packageJson.version || "0.0.0";
35
+ return cachedVersion;
36
+ }
37
+ }
38
+ }
39
+ cachedVersion = "0.0.0";
40
+ return cachedVersion;
41
+ } catch {
42
+ cachedVersion = "0.0.0";
43
+ return cachedVersion;
44
+ }
45
+ }
46
+ var DEV_VERSION = "9.9.9";
47
+ function isVersionMatch(version1, version2) {
48
+ if (version1 === DEV_VERSION || version2 === DEV_VERSION) {
49
+ return true;
50
+ }
51
+ return version1 === version2;
52
+ }
53
+
54
+ // src/cli/version-check.ts
55
+ var CONFIG_DIR = join2(homedir(), ".config", "mg-cli");
56
+ var CHECK_FILE = join2(CONFIG_DIR, "last-version-check");
57
+ var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
58
+ function readCheckData() {
59
+ try {
60
+ if (!existsSync2(CHECK_FILE)) {
61
+ return null;
62
+ }
63
+ const content = readFileSync2(CHECK_FILE, "utf-8");
64
+ return JSON.parse(content);
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+ function writeCheckData(data) {
70
+ try {
71
+ if (!existsSync2(CONFIG_DIR)) {
72
+ mkdirSync(CONFIG_DIR, { recursive: true });
73
+ }
74
+ writeFileSync(CHECK_FILE, JSON.stringify(data), "utf-8");
75
+ } catch {
76
+ }
77
+ }
78
+ function getRemoteVersion() {
79
+ try {
80
+ const result = execSync("npm view @hangox/mg-cli version", {
81
+ encoding: "utf-8",
82
+ timeout: 5e3,
83
+ stdio: ["pipe", "pipe", "pipe"]
84
+ });
85
+ return result.trim();
86
+ } catch {
87
+ return null;
88
+ }
89
+ }
90
+ function isNewerVersion(local, remote) {
91
+ const localParts = local.split(".").map(Number);
92
+ const remoteParts = remote.split(".").map(Number);
93
+ for (let i = 0; i < 3; i++) {
94
+ const l = localParts[i] || 0;
95
+ const r = remoteParts[i] || 0;
96
+ if (r > l) return true;
97
+ if (r < l) return false;
98
+ }
99
+ return false;
100
+ }
101
+ function checkForUpdates() {
102
+ try {
103
+ const now = Date.now();
104
+ const checkData = readCheckData();
105
+ if (checkData && now - checkData.timestamp < CHECK_INTERVAL) {
106
+ if (checkData.remoteVersion && isNewerVersion(getVersion(), checkData.remoteVersion)) {
107
+ printUpdateNotice(checkData.remoteVersion);
108
+ }
109
+ return;
110
+ }
111
+ const remoteVersion = getRemoteVersion();
112
+ if (remoteVersion) {
113
+ writeCheckData({
114
+ timestamp: now,
115
+ remoteVersion
116
+ });
117
+ if (isNewerVersion(getVersion(), remoteVersion)) {
118
+ printUpdateNotice(remoteVersion);
119
+ }
120
+ } else {
121
+ writeCheckData({
122
+ timestamp: now
123
+ });
124
+ }
125
+ } catch {
126
+ }
127
+ }
128
+ function printUpdateNotice(remoteVersion) {
129
+ const localVersion = getVersion();
130
+ console.log("");
131
+ console.log(`\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`);
132
+ console.log(`\u2502 \u53D1\u73B0\u65B0\u7248\u672C ${remoteVersion}\uFF08\u5F53\u524D ${localVersion}\uFF09`);
133
+ console.log(`\u2502 \u8FD0\u884C npm install -g @hangox/mg-cli \u66F4\u65B0`);
134
+ console.log(`\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518`);
135
+ console.log("");
136
+ }
5
137
 
6
138
  // src/cli/commands/server.ts
7
139
  import { Command } from "commander";
@@ -9,15 +141,15 @@ import { Command } from "commander";
9
141
  // src/server/daemon.ts
10
142
  import { spawn } from "child_process";
11
143
  import { fileURLToPath as fileURLToPath2 } from "url";
12
- import { dirname as dirname4, join as join3 } from "path";
144
+ import { dirname as dirname4, join as join4 } from "path";
13
145
 
14
146
  // src/shared/utils.ts
15
- import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
16
- import { dirname, resolve, isAbsolute } from "path";
147
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync } from "fs";
148
+ import { dirname as dirname2, resolve, isAbsolute } from "path";
17
149
 
18
150
  // src/shared/constants.ts
19
- import { homedir } from "os";
20
- import { join } from "path";
151
+ import { homedir as homedir2 } from "os";
152
+ import { join as join3 } from "path";
21
153
  var IS_DEV_MODE = process.env.MG_DEV_MODE === "true";
22
154
  var PROD_DEFAULT_PORT = 9527;
23
155
  var PROD_PORT_RANGE_START = 9527;
@@ -29,10 +161,10 @@ var DEFAULT_PORT = IS_DEV_MODE ? DEV_DEFAULT_PORT : PROD_DEFAULT_PORT;
29
161
  var PORT_RANGE_START = IS_DEV_MODE ? DEV_PORT_RANGE_START : PROD_PORT_RANGE_START;
30
162
  var PORT_RANGE_END = IS_DEV_MODE ? DEV_PORT_RANGE_END : PROD_PORT_RANGE_END;
31
163
  var PORT_SCAN_TIMEOUT = 500;
32
- var CONFIG_DIR = join(homedir(), ".mg-plugin");
33
- var SERVER_INFO_FILE = join(CONFIG_DIR, "server.json");
34
- var LOG_DIR = join(CONFIG_DIR, "logs");
35
- var SERVER_LOG_FILE = join(LOG_DIR, "server.log");
164
+ var CONFIG_DIR2 = join3(homedir2(), ".mg-plugin");
165
+ var SERVER_INFO_FILE = join3(CONFIG_DIR2, "server.json");
166
+ var LOG_DIR = join3(CONFIG_DIR2, "logs");
167
+ var SERVER_LOG_FILE = join3(LOG_DIR, "server.log");
36
168
  var HEARTBEAT_INTERVAL = 3e4;
37
169
  var HEARTBEAT_TIMEOUT = 9e4;
38
170
  var REQUEST_TIMEOUT = 3e4;
@@ -42,20 +174,20 @@ var MAX_RETRY_COUNT = 3;
42
174
 
43
175
  // src/shared/utils.ts
44
176
  function ensureDir(dir) {
45
- if (!existsSync(dir)) {
46
- mkdirSync(dir, { recursive: true });
177
+ if (!existsSync3(dir)) {
178
+ mkdirSync2(dir, { recursive: true });
47
179
  }
48
180
  }
49
181
  function ensureConfigDir() {
50
- ensureDir(CONFIG_DIR);
182
+ ensureDir(CONFIG_DIR2);
51
183
  ensureDir(LOG_DIR);
52
184
  }
53
185
  function readServerInfo() {
54
186
  try {
55
- if (!existsSync(SERVER_INFO_FILE)) {
187
+ if (!existsSync3(SERVER_INFO_FILE)) {
56
188
  return null;
57
189
  }
58
- const content = readFileSync(SERVER_INFO_FILE, "utf-8");
190
+ const content = readFileSync3(SERVER_INFO_FILE, "utf-8");
59
191
  return JSON.parse(content);
60
192
  } catch {
61
193
  return null;
@@ -63,11 +195,11 @@ function readServerInfo() {
63
195
  }
64
196
  function writeServerInfo(info) {
65
197
  ensureConfigDir();
66
- writeFileSync(SERVER_INFO_FILE, JSON.stringify(info, null, 2), "utf-8");
198
+ writeFileSync2(SERVER_INFO_FILE, JSON.stringify(info, null, 2), "utf-8");
67
199
  }
68
200
  function deleteServerInfo() {
69
201
  try {
70
- if (existsSync(SERVER_INFO_FILE)) {
202
+ if (existsSync3(SERVER_INFO_FILE)) {
71
203
  unlinkSync(SERVER_INFO_FILE);
72
204
  }
73
205
  } catch {
@@ -428,8 +560,8 @@ var MGError = class extends Error {
428
560
  import { WebSocketServer } from "ws";
429
561
 
430
562
  // src/server/logger.ts
431
- import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
432
- import { dirname as dirname2 } from "path";
563
+ import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
564
+ import { dirname as dirname3 } from "path";
433
565
  var levelPriority = {
434
566
  ["DEBUG" /* DEBUG */]: 0,
435
567
  ["INFO" /* INFO */]: 1,
@@ -446,9 +578,9 @@ var Logger = class {
446
578
  minLevel: options.minLevel ?? "INFO" /* INFO */
447
579
  };
448
580
  if (this.options.file) {
449
- const dir = dirname2(this.options.filePath);
450
- if (!existsSync2(dir)) {
451
- mkdirSync2(dir, { recursive: true });
581
+ const dir = dirname3(this.options.filePath);
582
+ if (!existsSync4(dir)) {
583
+ mkdirSync3(dir, { recursive: true });
452
584
  }
453
585
  }
454
586
  }
@@ -889,48 +1021,6 @@ var RequestHandler = class {
889
1021
  }
890
1022
  };
891
1023
 
892
- // src/shared/version.ts
893
- import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
894
- import { fileURLToPath } from "url";
895
- import { dirname as dirname3, join as join2 } from "path";
896
- var cachedVersion = null;
897
- function getVersion() {
898
- if (cachedVersion !== null) {
899
- return cachedVersion;
900
- }
901
- try {
902
- const currentFile = fileURLToPath(import.meta.url);
903
- const currentDir = dirname3(currentFile);
904
- const packageJsonPaths = [
905
- join2(currentDir, "..", "package.json"),
906
- // dist/xxx.js -> ../package.json
907
- join2(currentDir, "..", "..", "package.json")
908
- // src/shared/version.ts -> ../../package.json
909
- ];
910
- for (const packageJsonPath of packageJsonPaths) {
911
- if (existsSync3(packageJsonPath)) {
912
- const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
913
- if (packageJson.name === "@hangox/mg-cli") {
914
- cachedVersion = packageJson.version || "0.0.0";
915
- return cachedVersion;
916
- }
917
- }
918
- }
919
- cachedVersion = "0.0.0";
920
- return cachedVersion;
921
- } catch {
922
- cachedVersion = "0.0.0";
923
- return cachedVersion;
924
- }
925
- }
926
- var DEV_VERSION = "9.9.9";
927
- function isVersionMatch(version1, version2) {
928
- if (version1 === DEV_VERSION || version2 === DEV_VERSION) {
929
- return true;
930
- }
931
- return version1 === version2;
932
- }
933
-
934
1024
  // src/server/websocket-server.ts
935
1025
  var MGServer = class {
936
1026
  wss = null;
@@ -954,7 +1044,7 @@ var MGServer = class {
954
1044
  throw new MGError("E016" /* SERVER_ALREADY_RUNNING */, "Server \u5DF2\u5728\u8FD0\u884C\u4E2D");
955
1045
  }
956
1046
  const port = await this.findAvailablePort();
957
- return new Promise((resolve8, reject) => {
1047
+ return new Promise((resolve9, reject) => {
958
1048
  this.wss = new WebSocketServer({ port });
959
1049
  this.wss.on("listening", () => {
960
1050
  this.port = port;
@@ -962,7 +1052,7 @@ var MGServer = class {
962
1052
  this.startedAt = /* @__PURE__ */ new Date();
963
1053
  this.logger.info(`Server \u542F\u52A8\u6210\u529F\uFF0C\u76D1\u542C\u7AEF\u53E3: ${port}`);
964
1054
  this.connectionManager.startHeartbeatCheck(HEARTBEAT_INTERVAL);
965
- resolve8(port);
1055
+ resolve9(port);
966
1056
  });
967
1057
  this.wss.on("error", (error) => {
968
1058
  this.logger.error("Server \u9519\u8BEF:", error);
@@ -993,14 +1083,14 @@ var MGServer = class {
993
1083
  * 检查端口是否可用
994
1084
  */
995
1085
  isPortAvailable(port) {
996
- return new Promise((resolve8) => {
1086
+ return new Promise((resolve9) => {
997
1087
  const testServer = new WebSocketServer({ port });
998
1088
  testServer.on("listening", () => {
999
1089
  testServer.close();
1000
- resolve8(true);
1090
+ resolve9(true);
1001
1091
  });
1002
1092
  testServer.on("error", () => {
1003
- resolve8(false);
1093
+ resolve9(false);
1004
1094
  });
1005
1095
  });
1006
1096
  }
@@ -1318,12 +1408,12 @@ var MGServer = class {
1318
1408
  this.logger.info("\u6B63\u5728\u505C\u6B62 Server...");
1319
1409
  this.requestHandler.cleanupAll();
1320
1410
  this.connectionManager.closeAll();
1321
- return new Promise((resolve8) => {
1411
+ return new Promise((resolve9) => {
1322
1412
  this.wss.close(() => {
1323
1413
  this.isRunning = false;
1324
1414
  this.wss = null;
1325
1415
  this.logger.info("Server \u5DF2\u505C\u6B62");
1326
- resolve8();
1416
+ resolve9();
1327
1417
  });
1328
1418
  });
1329
1419
  }
@@ -1424,7 +1514,7 @@ async function startServerDaemon(port) {
1424
1514
  ensureConfigDir();
1425
1515
  const currentFile = fileURLToPath2(import.meta.url);
1426
1516
  const currentDir = dirname4(currentFile);
1427
- const serverScript = join3(currentDir, "daemon-runner.js");
1517
+ const serverScript = join4(currentDir, "daemon-runner.js");
1428
1518
  const args = ["--foreground"];
1429
1519
  if (port) {
1430
1520
  args.push("--port", String(port));
@@ -1440,7 +1530,7 @@ async function startServerDaemon(port) {
1440
1530
  child.unref();
1441
1531
  const startTime = Date.now();
1442
1532
  while (Date.now() - startTime < SERVER_START_TIMEOUT) {
1443
- await new Promise((resolve8) => setTimeout(resolve8, 200));
1533
+ await new Promise((resolve9) => setTimeout(resolve9, 200));
1444
1534
  const { running: running2, info: info2 } = isServerRunning();
1445
1535
  if (running2 && info2) {
1446
1536
  return info2;
@@ -1461,7 +1551,7 @@ function stopServer() {
1461
1551
  }
1462
1552
  async function restartServer(port) {
1463
1553
  const { info: oldInfo } = stopServer();
1464
- await new Promise((resolve8) => setTimeout(resolve8, 500));
1554
+ await new Promise((resolve9) => setTimeout(resolve9, 500));
1465
1555
  return startServerDaemon(port || oldInfo?.port);
1466
1556
  }
1467
1557
  function getServerStatus() {
@@ -1534,7 +1624,7 @@ var MGClient = class {
1534
1624
  * 尝试连接指定端口
1535
1625
  */
1536
1626
  tryConnect(port) {
1537
- return new Promise((resolve8, reject) => {
1627
+ return new Promise((resolve9, reject) => {
1538
1628
  const ws = new WebSocket2(`ws://localhost:${port}`);
1539
1629
  const timer = setTimeout(() => {
1540
1630
  ws.close();
@@ -1544,7 +1634,7 @@ var MGClient = class {
1544
1634
  clearTimeout(timer);
1545
1635
  this.ws = ws;
1546
1636
  this.register();
1547
- resolve8();
1637
+ resolve9();
1548
1638
  });
1549
1639
  ws.on("error", (error) => {
1550
1640
  clearTimeout(timer);
@@ -1597,7 +1687,7 @@ var MGClient = class {
1597
1687
  pageUrl,
1598
1688
  timestamp: Date.now()
1599
1689
  };
1600
- return new Promise((resolve8, reject) => {
1690
+ return new Promise((resolve9, reject) => {
1601
1691
  const timer = setTimeout(() => {
1602
1692
  reject(new MGError("E012" /* REQUEST_TIMEOUT */, ErrorMessages["E012" /* REQUEST_TIMEOUT */]));
1603
1693
  }, REQUEST_TIMEOUT);
@@ -1608,7 +1698,7 @@ var MGClient = class {
1608
1698
  clearTimeout(timer);
1609
1699
  this.ws?.off("message", messageHandler);
1610
1700
  if (response.success) {
1611
- resolve8(response.data);
1701
+ resolve9(response.data);
1612
1702
  } else {
1613
1703
  const error = response.error;
1614
1704
  reject(
@@ -1790,9 +1880,9 @@ function createServerCommand() {
1790
1880
 
1791
1881
  // src/cli/commands/get-node-by-id.ts
1792
1882
  import { Command as Command2 } from "commander";
1793
- import { writeFileSync as writeFileSync2 } from "fs";
1883
+ import { writeFileSync as writeFileSync3 } from "fs";
1794
1884
  import { resolve as resolve2, dirname as dirname5 } from "path";
1795
- import { mkdirSync as mkdirSync3 } from "fs";
1885
+ import { mkdirSync as mkdirSync4 } from "fs";
1796
1886
  function createGetNodeByIdCommand() {
1797
1887
  return new Command2("get_node_by_id").description("\u6839\u636E\u8282\u70B9 ID \u83B7\u53D6\u8282\u70B9\u8BE6\u7EC6\u4FE1\u606F\u3002\u6570\u636E\u4FDD\u5B58\u5230\u6307\u5B9A JSON \u6587\u4EF6\uFF0C\u8FD4\u56DE\u6587\u4EF6\u8DEF\u5F84\u548C\u5927\u5C0F\u4FE1\u606F\u3002\u5982\u9700\u901A\u8FC7\u94FE\u63A5\u83B7\u53D6\uFF0C\u8BF7\u4F7F\u7528 get_node_by_link \u547D\u4EE4").requiredOption("--nodeId <id>", "\u8282\u70B9 ID\uFF0C\u683C\u5F0F\u5982 123:456\u3002\u53EF\u4ECE MasterGo \u6D6E\u7A97\u94FE\u63A5\u4E2D\u83B7\u53D6").requiredOption("--output <path>", "\u8F93\u51FA JSON \u6587\u4EF6\u8DEF\u5F84\u3002\u652F\u6301\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u8DEF\u5F84").option("--domain <domain>", "MasterGo \u57DF\u540D\uFF0C\u9ED8\u8BA4 mastergo.netease.com\u3002\u4E0E --fileId \u914D\u5408\u4F7F\u7528", "mastergo.netease.com").option("--fileId <id>", "\u6587\u4EF6 ID\uFF08\u7EAF\u6570\u5B57\uFF09\uFF0C\u4E0E --domain \u914D\u5408\u6307\u5B9A\u76EE\u6807\u9875\u9762").option("--maxDepth <number>", "\u904D\u5386\u6DF1\u5EA6\uFF0C\u9ED8\u8BA4 1\u3002\u589E\u52A0\u6DF1\u5EA6\u4F1A\u663E\u8457\u589E\u52A0\u6570\u636E\u91CF", "1").option("--includeInvisible", "\u5305\u542B\u4E0D\u53EF\u89C1\u8282\u70B9\uFF08visible: false\uFF09\uFF0C\u9ED8\u8BA4\u4E0D\u5305\u542B", false).option("--raw", "\u4FDD\u7559\u539F\u59CB\u6570\u636E\uFF0C\u4E0D\u7CBE\u7B80\u9ED8\u8BA4\u503C\u5B57\u6BB5", false).option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09", false).option("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
1798
1888
  await handleGetNodeById(options);
@@ -1819,9 +1909,9 @@ async function handleGetNodeById(options) {
1819
1909
  const outputData = options.raw ? data : trimNodeDefaults(data);
1820
1910
  const outputPath = resolve2(options.output);
1821
1911
  const outputDir = dirname5(outputPath);
1822
- mkdirSync3(outputDir, { recursive: true });
1912
+ mkdirSync4(outputDir, { recursive: true });
1823
1913
  const jsonContent = JSON.stringify(outputData, null, options.pretty ? 2 : 0);
1824
- writeFileSync2(outputPath, jsonContent, "utf-8");
1914
+ writeFileSync3(outputPath, jsonContent, "utf-8");
1825
1915
  const size = jsonContent.length;
1826
1916
  const sizeKB = (size / 1024).toFixed(2);
1827
1917
  console.log(`\u6587\u4EF6\u8DEF\u5F84: ${outputPath}`);
@@ -1842,9 +1932,9 @@ async function handleGetNodeById(options) {
1842
1932
 
1843
1933
  // src/cli/commands/get-node-by-link.ts
1844
1934
  import { Command as Command3 } from "commander";
1845
- import { writeFileSync as writeFileSync3 } from "fs";
1935
+ import { writeFileSync as writeFileSync4 } from "fs";
1846
1936
  import { resolve as resolve3, dirname as dirname6 } from "path";
1847
- import { mkdirSync as mkdirSync4 } from "fs";
1937
+ import { mkdirSync as mkdirSync5 } from "fs";
1848
1938
  function createGetNodeByLinkCommand() {
1849
1939
  return new Command3("get_node_by_link").description("\u89E3\u6790 mgp:// \u534F\u8BAE\u94FE\u63A5\u5E76\u83B7\u53D6\u8282\u70B9/\u9875\u9762\u4FE1\u606F").requiredOption("--link <url>", "mgp:// \u534F\u8BAE\u94FE\u63A5\uFF08\u652F\u6301 nodeId \u548C pageId\uFF09").requiredOption("--output <path>", "\u8F93\u51FA JSON \u6587\u4EF6\u8DEF\u5F84").option("--maxDepth <number>", "\u904D\u5386\u6DF1\u5EA6", "1").option("--includeInvisible", "\u5305\u542B\u4E0D\u53EF\u89C1\u8282\u70B9", false).option("--raw", "\u4FDD\u7559\u539F\u59CB\u6570\u636E\uFF0C\u4E0D\u7CBE\u7B80\u9ED8\u8BA4\u503C\u5B57\u6BB5", false).option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09", false).option("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
1850
1940
  await handleGetNodeByLink(options);
@@ -1895,9 +1985,9 @@ async function handleGetNodeByLink(options) {
1895
1985
  const outputData = options.raw ? data : trimNodeDefaults(data);
1896
1986
  const outputPath = resolve3(options.output);
1897
1987
  const outputDir = dirname6(outputPath);
1898
- mkdirSync4(outputDir, { recursive: true });
1988
+ mkdirSync5(outputDir, { recursive: true });
1899
1989
  const jsonContent = JSON.stringify(outputData, null, options.pretty ? 2 : 0);
1900
- writeFileSync3(outputPath, jsonContent, "utf-8");
1990
+ writeFileSync4(outputPath, jsonContent, "utf-8");
1901
1991
  const size = jsonContent.length;
1902
1992
  const sizeKB = (size / 1024).toFixed(2);
1903
1993
  console.log(`\u6587\u4EF6\u8DEF\u5F84: ${outputPath}`);
@@ -1930,9 +2020,9 @@ async function handleGetNodeByLink(options) {
1930
2020
 
1931
2021
  // src/cli/commands/get-all-nodes.ts
1932
2022
  import { Command as Command4 } from "commander";
1933
- import { writeFileSync as writeFileSync4 } from "fs";
2023
+ import { writeFileSync as writeFileSync5 } from "fs";
1934
2024
  import { resolve as resolve4, dirname as dirname7 } from "path";
1935
- import { mkdirSync as mkdirSync5 } from "fs";
2025
+ import { mkdirSync as mkdirSync6 } from "fs";
1936
2026
  function createGetAllNodesCommand() {
1937
2027
  return new Command4("get_all_nodes").description("\u83B7\u53D6\u5F53\u524D\u9875\u9762\u7684\u6240\u6709\u8282\u70B9\u6811\u3002\u8B66\u544A\uFF1A\u6DF1\u5EA6\u6BCF\u589E\u52A0 1\uFF0C\u6570\u636E\u91CF\u53EF\u80FD\u5448\u6307\u6570\u7EA7\u589E\u957F\u3002\u5EFA\u8BAE\u4ECE maxDepth=1 \u5F00\u59CB").requiredOption("--output <path>", "\u8F93\u51FA JSON \u6587\u4EF6\u8DEF\u5F84\u3002\u652F\u6301\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u8DEF\u5F84").option("--maxDepth <number>", "\u6700\u5927\u6DF1\u5EA6\uFF0C\u9ED8\u8BA4 1\u3002\u6DF1\u5EA6 2 \u53EF\u80FD\u4EA7\u751F 100KB-500KB\uFF0C\u6DF1\u5EA6 3 \u53EF\u80FD\u8D85\u8FC7 1MB", "1").option("--includeInvisible", "\u5305\u542B\u4E0D\u53EF\u89C1\u8282\u70B9\uFF08visible: false\uFF09\uFF0C\u9ED8\u8BA4\u4E0D\u5305\u542B", false).option("--raw", "\u4FDD\u7559\u539F\u59CB\u6570\u636E\uFF0C\u4E0D\u7CBE\u7B80\u9ED8\u8BA4\u503C\u5B57\u6BB5", false).option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09", false).option("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
1938
2028
  await handleGetAllNodes(options);
@@ -1953,9 +2043,9 @@ async function handleGetAllNodes(options) {
1953
2043
  const outputData = options.raw ? data : Array.isArray(data) ? data.map((node) => trimNodeDefaults(node)) : trimNodeDefaults(data);
1954
2044
  const outputPath = resolve4(options.output);
1955
2045
  const outputDir = dirname7(outputPath);
1956
- mkdirSync5(outputDir, { recursive: true });
2046
+ mkdirSync6(outputDir, { recursive: true });
1957
2047
  const jsonContent = JSON.stringify(outputData, null, options.pretty ? 2 : 0);
1958
- writeFileSync4(outputPath, jsonContent, "utf-8");
2048
+ writeFileSync5(outputPath, jsonContent, "utf-8");
1959
2049
  const size = jsonContent.length;
1960
2050
  const sizeKB = (size / 1024).toFixed(2);
1961
2051
  const nodeCount = Array.isArray(data) ? data.length : 1;
@@ -1977,9 +2067,9 @@ async function handleGetAllNodes(options) {
1977
2067
 
1978
2068
  // src/cli/commands/export-image.ts
1979
2069
  import { Command as Command5 } from "commander";
1980
- import { writeFileSync as writeFileSync5, unlinkSync as unlinkSync2 } from "fs";
2070
+ import { writeFileSync as writeFileSync6, unlinkSync as unlinkSync2 } from "fs";
1981
2071
  import { resolve as resolve5, dirname as dirname8, extname, basename } from "path";
1982
- import { mkdirSync as mkdirSync6 } from "fs";
2072
+ import { mkdirSync as mkdirSync7 } from "fs";
1983
2073
  import { tmpdir } from "os";
1984
2074
  import { vdConvert } from "vd-tool";
1985
2075
  function createExportImageCommand() {
@@ -2069,23 +2159,23 @@ async function handleExportImage(options) {
2069
2159
  console.log("\u8B66\u544A: \u672A\u6307\u5B9A --output\uFF0C\u6587\u4EF6\u5C06\u4FDD\u5B58\u5230\u4E34\u65F6\u76EE\u5F55\uFF0C\u53EF\u80FD\u4F1A\u88AB\u7CFB\u7EDF\u6E05\u7406");
2070
2160
  }
2071
2161
  const outputDir = dirname8(outputPath);
2072
- mkdirSync6(outputDir, { recursive: true });
2162
+ mkdirSync7(outputDir, { recursive: true });
2073
2163
  const buffer = Buffer.from(response.data, "base64");
2074
2164
  let finalPath = outputPath;
2075
2165
  let finalSize = buffer.length;
2076
2166
  if (isVectorFormat) {
2077
2167
  const tempSvgPath = resolve5(tmpdir(), `temp_${Date.now()}.svg`);
2078
- writeFileSync5(tempSvgPath, buffer);
2168
+ writeFileSync6(tempSvgPath, buffer);
2079
2169
  try {
2080
2170
  const vectorOutputDir = dirname8(outputPath);
2081
2171
  const convertedPath = await convertSvgToVector(tempSvgPath, vectorOutputDir);
2082
2172
  const expectedOutputName = basename(tempSvgPath, ".svg") + ".xml";
2083
2173
  const expectedOutputPath = resolve5(vectorOutputDir, expectedOutputName);
2084
2174
  if (expectedOutputPath !== outputPath) {
2085
- const { renameSync, existsSync: existsSync4 } = await import("fs");
2086
- if (existsSync4(expectedOutputPath)) {
2175
+ const { renameSync, existsSync: existsSync5 } = await import("fs");
2176
+ if (existsSync5(expectedOutputPath)) {
2087
2177
  renameSync(expectedOutputPath, outputPath);
2088
- } else if (existsSync4(convertedPath)) {
2178
+ } else if (existsSync5(convertedPath)) {
2089
2179
  renameSync(convertedPath, outputPath);
2090
2180
  }
2091
2181
  }
@@ -2099,7 +2189,7 @@ async function handleExportImage(options) {
2099
2189
  }
2100
2190
  }
2101
2191
  } else {
2102
- writeFileSync5(outputPath, buffer);
2192
+ writeFileSync6(outputPath, buffer);
2103
2193
  }
2104
2194
  const sizeKB = (finalSize / 1024).toFixed(2);
2105
2195
  console.log(`\u6587\u4EF6\u8DEF\u5F84: ${finalPath}`);
@@ -2210,9 +2300,9 @@ async function handleExecuteCode(code, options) {
2210
2300
 
2211
2301
  // src/cli/commands/get-all-pages.ts
2212
2302
  import { Command as Command7 } from "commander";
2213
- import { writeFileSync as writeFileSync6 } from "fs";
2303
+ import { writeFileSync as writeFileSync7 } from "fs";
2214
2304
  import { resolve as resolve6, dirname as dirname9 } from "path";
2215
- import { mkdirSync as mkdirSync7 } from "fs";
2305
+ import { mkdirSync as mkdirSync8 } from "fs";
2216
2306
  import { tmpdir as tmpdir2 } from "os";
2217
2307
  function createGetAllPagesCommand() {
2218
2308
  return new Command7("get_all_pages").description("\u83B7\u53D6 MasterGo \u6587\u6863\u7684\u6240\u6709\u9875\u9762\u4FE1\u606F\u3002\u4E0D\u6307\u5B9A --output \u65F6\u4FDD\u5B58\u5230\u7CFB\u7EDF\u4E34\u65F6\u76EE\u5F55").option("--link <url>", "\u9875\u9762\u94FE\u63A5\u3002\u652F\u6301\u5B8C\u6574 URL \u6216 mgp:// \u534F\u8BAE").option("--fileId <id>", "\u6587\u4EF6 ID\uFF08\u7EAF\u6570\u5B57\uFF09\u3002\u4ECE URL \u4E2D /file/ \u540E\u9762\u7684\u6570\u5B57").option("--domain <domain>", "MasterGo \u57DF\u540D\uFF0C\u9ED8\u8BA4 mastergo.netease.com\u3002\u4E0E --fileId \u914D\u5408\u4F7F\u7528", "mastergo.netease.com").option("--output <path>", "\u8F93\u51FA JSON \u6587\u4EF6\u8DEF\u5F84\u3002\u4E0D\u6307\u5B9A\u5219\u4FDD\u5B58\u5230\u7CFB\u7EDF\u4E34\u65F6\u76EE\u5F55").option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09", false).option("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
@@ -2260,9 +2350,9 @@ async function handleGetAllPages(options) {
2260
2350
  outputPath = resolve6(tmpdir2(), filename);
2261
2351
  }
2262
2352
  const outputDir = dirname9(outputPath);
2263
- mkdirSync7(outputDir, { recursive: true });
2353
+ mkdirSync8(outputDir, { recursive: true });
2264
2354
  const jsonContent = JSON.stringify(data, null, options.pretty ? 2 : 0);
2265
- writeFileSync6(outputPath, jsonContent, "utf-8");
2355
+ writeFileSync7(outputPath, jsonContent, "utf-8");
2266
2356
  const size = jsonContent.length;
2267
2357
  const sizeKB = (size / 1024).toFixed(2);
2268
2358
  console.log(`\u6587\u4EF6\u8DEF\u5F84: ${outputPath}`);
@@ -2280,9 +2370,9 @@ async function handleGetAllPages(options) {
2280
2370
 
2281
2371
  // src/cli/commands/get-node-for-space.ts
2282
2372
  import { Command as Command8 } from "commander";
2283
- import { writeFileSync as writeFileSync7 } from "fs";
2373
+ import { writeFileSync as writeFileSync8 } from "fs";
2284
2374
  import { resolve as resolve7, dirname as dirname10 } from "path";
2285
- import { mkdirSync as mkdirSync8 } from "fs";
2375
+ import { mkdirSync as mkdirSync9 } from "fs";
2286
2376
  function createGetNodeForSpaceCommand() {
2287
2377
  return new Command8("get_node_for_space").description("\u83B7\u53D6\u8282\u70B9\u6216\u9875\u9762\u7684\u7A7A\u95F4\u4F4D\u7F6E\u4FE1\u606F\uFF08id\u3001name\u3001x\u3001y\u3001width\u3001height\uFF09\uFF0C\u7528\u4E8E AI \u7406\u89E3\u5143\u7D20\u5E03\u5C40\u3002\u9ED8\u8BA4\u83B7\u53D6\u6700\u6DF1\u5C42\u7EA7\u3002\u652F\u6301 nodeId \u548C pageId \u94FE\u63A5").option("--nodeId <id>", "\u8282\u70B9 ID\uFF0C\u683C\u5F0F\u5982 123:456\u3002\u4E0E --link \u4E8C\u9009\u4E00").option("--link <url>", "mgp:// \u534F\u8BAE\u94FE\u63A5\uFF08\u652F\u6301 nodeId \u548C pageId\uFF09\u3002\u4E0E --nodeId \u4E8C\u9009\u4E00").requiredOption("--output <path>", "\u8F93\u51FA JSON \u6587\u4EF6\u8DEF\u5F84").option("--domain <domain>", "MasterGo \u57DF\u540D\uFF0C\u9ED8\u8BA4 mastergo.netease.com\u3002\u4E0E --nodeId \u914D\u5408\u4F7F\u7528", "mastergo.netease.com").option("--fileId <id>", "\u6587\u4EF6 ID\uFF08\u7EAF\u6570\u5B57\uFF09\uFF0C\u4E0E --domain \u548C --nodeId \u914D\u5408\u4F7F\u7528").option("--maxDepth <number>", "\u904D\u5386\u6DF1\u5EA6\uFF0C\u9ED8\u8BA4 99\uFF08\u83B7\u53D6\u6700\u6DF1\u5C42\u7EA7\uFF09", "99").option("--includeInvisible", "\u5305\u542B\u4E0D\u53EF\u89C1\u8282\u70B9", false).option("--pretty", "\u683C\u5F0F\u5316\u8F93\u51FA JSON\uFF08\u9ED8\u8BA4\u538B\u7F29\uFF09", false).option("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
2288
2378
  await handleGetNodeForSpace(options);
@@ -2362,9 +2452,9 @@ async function handleGetNodeForSpace(options) {
2362
2452
  const spaceData = extractSpaceInfo(data);
2363
2453
  const outputPath = resolve7(options.output);
2364
2454
  const outputDir = dirname10(outputPath);
2365
- mkdirSync8(outputDir, { recursive: true });
2455
+ mkdirSync9(outputDir, { recursive: true });
2366
2456
  const jsonContent = JSON.stringify(spaceData, null, options.pretty ? 2 : 0);
2367
- writeFileSync7(outputPath, jsonContent, "utf-8");
2457
+ writeFileSync8(outputPath, jsonContent, "utf-8");
2368
2458
  const size = jsonContent.length;
2369
2459
  const sizeKB = (size / 1024).toFixed(2);
2370
2460
  console.log(`\u6587\u4EF6\u8DEF\u5F84: ${outputPath}`);
@@ -2616,8 +2706,374 @@ async function handleNavigateToNode(options) {
2616
2706
  }
2617
2707
  }
2618
2708
 
2709
+ // src/cli/commands/visualize.ts
2710
+ import { Command as Command12 } from "commander";
2711
+ import { writeFileSync as writeFileSync9 } from "fs";
2712
+ import { resolve as resolve8, dirname as dirname11 } from "path";
2713
+ import { mkdirSync as mkdirSync10 } from "fs";
2714
+
2715
+ // src/shared/ascii-renderer.ts
2716
+ var defaultRenderOptions = {
2717
+ showId: true,
2718
+ showSize: true,
2719
+ showPadding: true,
2720
+ showRadius: true,
2721
+ showLayout: true,
2722
+ showGap: true,
2723
+ compact: true
2724
+ };
2725
+ function nodeToRenderNode(node) {
2726
+ const renderNode = {
2727
+ id: node.id,
2728
+ name: node.name,
2729
+ type: node.type,
2730
+ width: Math.round(node.width ?? 0),
2731
+ height: Math.round(node.height ?? 0),
2732
+ x: Math.round(node.x ?? 0),
2733
+ y: Math.round(node.y ?? 0)
2734
+ };
2735
+ const cornerRadius = node.cornerRadius;
2736
+ if (cornerRadius && cornerRadius > 0) {
2737
+ renderNode.cornerRadius = Math.round(cornerRadius);
2738
+ }
2739
+ const paddingTop = node.paddingTop;
2740
+ const paddingRight = node.paddingRight;
2741
+ const paddingBottom = node.paddingBottom;
2742
+ const paddingLeft = node.paddingLeft;
2743
+ if (paddingTop || paddingRight || paddingBottom || paddingLeft) {
2744
+ renderNode.padding = {
2745
+ top: Math.round(paddingTop ?? 0),
2746
+ right: Math.round(paddingRight ?? 0),
2747
+ bottom: Math.round(paddingBottom ?? 0),
2748
+ left: Math.round(paddingLeft ?? 0)
2749
+ };
2750
+ }
2751
+ const flexMode = node.flexMode;
2752
+ if (flexMode === "HORIZONTAL") {
2753
+ renderNode.flexMode = "H";
2754
+ } else if (flexMode === "VERTICAL") {
2755
+ renderNode.flexMode = "V";
2756
+ } else {
2757
+ renderNode.flexMode = "none";
2758
+ }
2759
+ const itemSpacing = node.itemSpacing;
2760
+ if (itemSpacing && itemSpacing > 0) {
2761
+ renderNode.itemSpacing = Math.round(itemSpacing);
2762
+ }
2763
+ if (node.type === "TEXT") {
2764
+ const textStyles = node.textStyles;
2765
+ if (textStyles && textStyles.length > 0) {
2766
+ const firstStyle = textStyles[0];
2767
+ const fontFamily = firstStyle.textStyle?.fontName?.family;
2768
+ const fontSize = firstStyle.textStyle?.fontSize;
2769
+ if (fontFamily && fontSize) {
2770
+ renderNode.font = {
2771
+ family: fontFamily,
2772
+ size: Math.round(fontSize)
2773
+ };
2774
+ }
2775
+ }
2776
+ }
2777
+ if (node.children && node.children.length > 0) {
2778
+ renderNode.children = node.children.map(nodeToRenderNode);
2779
+ }
2780
+ return renderNode;
2781
+ }
2782
+ var CompactRenderer = class {
2783
+ options;
2784
+ treeChars = {
2785
+ branch: "\u251C\u2500 ",
2786
+ lastBranch: "\u2514\u2500 ",
2787
+ vertical: "\u2502 ",
2788
+ space: " "
2789
+ };
2790
+ gapChars = {
2791
+ vertical: "\u2195",
2792
+ horizontal: "\u2194"
2793
+ };
2794
+ constructor(options = {}) {
2795
+ this.options = { ...defaultRenderOptions, ...options, compact: true };
2796
+ }
2797
+ /**
2798
+ * 渲染节点为树形结构
2799
+ */
2800
+ render(node) {
2801
+ const lines = [];
2802
+ lines.push(this.buildLine(node));
2803
+ if (node.children && node.children.length > 0) {
2804
+ this.renderChildren(node, "", lines);
2805
+ }
2806
+ return lines;
2807
+ }
2808
+ renderChildren(parent, prefix, lines) {
2809
+ if (!parent.children) return;
2810
+ const gapSymbol = parent.flexMode === "H" ? this.gapChars.horizontal : this.gapChars.vertical;
2811
+ parent.children.forEach((child, index) => {
2812
+ const isLastChild = index === parent.children.length - 1;
2813
+ const connector = isLastChild ? this.treeChars.lastBranch : this.treeChars.branch;
2814
+ lines.push(prefix + connector + this.buildLine(child));
2815
+ if (child.children && child.children.length > 0) {
2816
+ const childPrefix = prefix + (isLastChild ? this.treeChars.space : this.treeChars.vertical);
2817
+ this.renderChildren(child, childPrefix, lines);
2818
+ }
2819
+ if (this.options.showGap && parent.itemSpacing && parent.itemSpacing > 0 && index < parent.children.length - 1) {
2820
+ const gapLinePrefix = prefix + this.treeChars.vertical;
2821
+ lines.push(gapLinePrefix + ` ${gapSymbol} gap:${parent.itemSpacing}`);
2822
+ }
2823
+ });
2824
+ }
2825
+ buildLine(node) {
2826
+ const parts = [];
2827
+ let typeName = `${node.type} "${node.name}"`;
2828
+ if (this.options.showId) {
2829
+ typeName += ` [${node.id}]`;
2830
+ }
2831
+ parts.push(typeName);
2832
+ if (this.options.showSize) {
2833
+ parts.push(`${node.width}\xD7${node.height}`);
2834
+ }
2835
+ if (this.options.showRadius && node.cornerRadius) {
2836
+ parts.push(`r:${node.cornerRadius}`);
2837
+ }
2838
+ if (this.options.showPadding && node.padding) {
2839
+ const { top, right, bottom, left } = node.padding;
2840
+ if (top === right && right === bottom && bottom === left && top > 0) {
2841
+ parts.push(`p:${top}`);
2842
+ } else if (top > 0 || right > 0 || bottom > 0 || left > 0) {
2843
+ parts.push(`p:${top}/${right}/${bottom}/${left}`);
2844
+ }
2845
+ }
2846
+ if (this.options.showLayout && node.flexMode && node.flexMode !== "none") {
2847
+ parts.push(`flex:${node.flexMode}`);
2848
+ }
2849
+ if (this.options.showGap && node.itemSpacing && node.itemSpacing > 0) {
2850
+ parts.push(`gap:${node.itemSpacing}`);
2851
+ }
2852
+ if (node.font) {
2853
+ parts.push(`font:${node.font.family}/${node.font.size}px`);
2854
+ }
2855
+ parts.push(`@(${node.x},${node.y})`);
2856
+ return parts.join(" ");
2857
+ }
2858
+ };
2859
+ var BoxRenderer = class {
2860
+ options;
2861
+ boxChars = {
2862
+ topLeft: "\u250C",
2863
+ topRight: "\u2510",
2864
+ bottomLeft: "\u2514",
2865
+ bottomRight: "\u2518",
2866
+ horizontal: "\u2500",
2867
+ vertical: "\u2502",
2868
+ teeRight: "\u251C",
2869
+ teeLeft: "\u2524"
2870
+ };
2871
+ gapChars = {
2872
+ vertical: "\u2195",
2873
+ horizontal: "\u2194"
2874
+ };
2875
+ /** 固定总宽度,确保右边框对齐 */
2876
+ totalWidth = 80;
2877
+ constructor(options = {}) {
2878
+ this.options = { ...defaultRenderOptions, ...options, compact: false };
2879
+ }
2880
+ /**
2881
+ * 渲染节点为框图
2882
+ */
2883
+ render(node) {
2884
+ this.totalWidth = Math.max(80, this.calculateMaxWidth(node, 0));
2885
+ return this.renderNode(node, 0);
2886
+ }
2887
+ /**
2888
+ * 计算整棵树需要的最大宽度
2889
+ */
2890
+ calculateMaxWidth(node, indent) {
2891
+ const indentWidth = indent * 4;
2892
+ const title = this.buildTitle(node);
2893
+ const props = this.buildPropsLine(node);
2894
+ const nodeWidth = Math.max(title.length + 6, props.length + 6) + indentWidth;
2895
+ let maxWidth = nodeWidth;
2896
+ if (node.children) {
2897
+ for (const child of node.children) {
2898
+ const childWidth = this.calculateMaxWidth(child, indent + 1);
2899
+ maxWidth = Math.max(maxWidth, childWidth);
2900
+ }
2901
+ }
2902
+ return maxWidth;
2903
+ }
2904
+ renderNode(node, indent) {
2905
+ const lines = [];
2906
+ const indentWidth = indent * 4;
2907
+ const contentWidth = this.totalWidth - indentWidth - 2;
2908
+ const indentStr = " ".repeat(indent);
2909
+ const title = this.buildTitle(node);
2910
+ const props = this.buildPropsLine(node);
2911
+ const titlePadding = contentWidth - title.length - 4;
2912
+ lines.push(
2913
+ indentStr + this.boxChars.topLeft + this.boxChars.horizontal + " " + title + " " + this.boxChars.horizontal.repeat(Math.max(0, titlePadding)) + this.boxChars.topRight
2914
+ );
2915
+ lines.push(
2916
+ indentStr + this.boxChars.vertical + " " + props.padEnd(contentWidth - 2) + this.boxChars.vertical
2917
+ );
2918
+ lines.push(
2919
+ indentStr + this.boxChars.teeRight + this.boxChars.horizontal.repeat(contentWidth) + this.boxChars.teeLeft
2920
+ );
2921
+ if (node.children && node.children.length > 0) {
2922
+ lines.push(
2923
+ indentStr + this.boxChars.vertical + " ".repeat(contentWidth) + this.boxChars.vertical
2924
+ );
2925
+ const gapSymbol = node.flexMode === "H" ? this.gapChars.horizontal : this.gapChars.vertical;
2926
+ node.children.forEach((child, index) => {
2927
+ const childLines = this.renderNode(child, indent + 1);
2928
+ for (const childLine of childLines) {
2929
+ const paddedLine = " " + childLine.padEnd(contentWidth - 2);
2930
+ lines.push(indentStr + this.boxChars.vertical + paddedLine + this.boxChars.vertical);
2931
+ }
2932
+ if (this.options.showGap && node.itemSpacing && node.itemSpacing > 0 && index < node.children.length - 1) {
2933
+ const gapText = `${gapSymbol} gap:${node.itemSpacing}`;
2934
+ const gapPadding = Math.floor((contentWidth - gapText.length) / 2);
2935
+ const gapLine = " ".repeat(gapPadding) + gapText;
2936
+ lines.push(
2937
+ indentStr + this.boxChars.vertical + gapLine.padEnd(contentWidth) + this.boxChars.vertical
2938
+ );
2939
+ } else {
2940
+ lines.push(
2941
+ indentStr + this.boxChars.vertical + " ".repeat(contentWidth) + this.boxChars.vertical
2942
+ );
2943
+ }
2944
+ });
2945
+ }
2946
+ lines.push(
2947
+ indentStr + this.boxChars.bottomLeft + this.boxChars.horizontal.repeat(contentWidth) + this.boxChars.bottomRight
2948
+ );
2949
+ return lines;
2950
+ }
2951
+ buildTitle(node) {
2952
+ let title = `${node.type} "${node.name}"`;
2953
+ if (this.options.showId) {
2954
+ title += ` [${node.id}]`;
2955
+ }
2956
+ return title;
2957
+ }
2958
+ buildPropsLine(node) {
2959
+ const parts = [];
2960
+ if (this.options.showSize) {
2961
+ parts.push(`${node.width}\xD7${node.height}`);
2962
+ }
2963
+ if (this.options.showRadius && node.cornerRadius) {
2964
+ parts.push(`r:${node.cornerRadius}`);
2965
+ }
2966
+ if (this.options.showPadding && node.padding) {
2967
+ const { top, right, bottom, left } = node.padding;
2968
+ if (top === right && right === bottom && bottom === left && top > 0) {
2969
+ parts.push(`p:${top}`);
2970
+ } else if (top > 0 || right > 0 || bottom > 0 || left > 0) {
2971
+ parts.push(`p:${top}/${right}/${bottom}/${left}`);
2972
+ }
2973
+ }
2974
+ if (this.options.showLayout && node.flexMode && node.flexMode !== "none") {
2975
+ parts.push(`flex:${node.flexMode}`);
2976
+ }
2977
+ if (this.options.showGap && node.itemSpacing && node.itemSpacing > 0) {
2978
+ parts.push(`gap:${node.itemSpacing}`);
2979
+ }
2980
+ if (node.font) {
2981
+ parts.push(`font:${node.font.family}/${node.font.size}px`);
2982
+ }
2983
+ parts.push(`pos:(${node.x},${node.y})`);
2984
+ return parts.join(" ");
2985
+ }
2986
+ };
2987
+ function renderAscii(node, options = {}) {
2988
+ const renderNode = nodeToRenderNode(node);
2989
+ const mergedOptions = { ...defaultRenderOptions, ...options };
2990
+ let lines;
2991
+ if (mergedOptions.compact) {
2992
+ const renderer = new CompactRenderer(mergedOptions);
2993
+ lines = renderer.render(renderNode);
2994
+ } else {
2995
+ const renderer = new BoxRenderer(mergedOptions);
2996
+ lines = renderer.render(renderNode);
2997
+ }
2998
+ return lines.join("\n");
2999
+ }
3000
+
3001
+ // src/cli/commands/visualize.ts
3002
+ function createVisualizeCommand() {
3003
+ return new Command12("visualize").description("\u5C06 MasterGo \u8BBE\u8BA1\u7A3F\u8282\u70B9\u6E32\u67D3\u4E3A ASCII \u793A\u610F\u56FE").requiredOption("--link <url>", "mgp:// \u534F\u8BAE\u94FE\u63A5").option("--output <path>", "\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84\uFF08\u4E0D\u6307\u5B9A\u5219\u8F93\u51FA\u5230\u7EC8\u7AEF\uFF09").option("--maxDepth <number>", "\u904D\u5386\u6DF1\u5EA6", "3").option("--no-show-id", "\u9690\u85CF\u8282\u70B9 ID").option("--no-show-size", "\u9690\u85CF\u5C3A\u5BF8").option("--no-show-padding", "\u9690\u85CF padding").option("--no-show-radius", "\u9690\u85CF\u5706\u89D2").option("--no-show-layout", "\u9690\u85CF\u5E03\u5C40\u65B9\u5411").option("--no-show-gap", "\u9690\u85CF gap \u53EF\u89C6\u5316").option("--no-compact", "\u4F7F\u7528\u6846\u56FE\u6A21\u5F0F\uFF08\u4EBA\u7C7B\u9605\u8BFB\u4F18\u5316\uFF09").option("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
3004
+ await handleVisualize(options);
3005
+ });
3006
+ }
3007
+ async function handleVisualize(options) {
3008
+ const parsed = parseMgpLink(options.link);
3009
+ if (!parsed) {
3010
+ console.error(`\u9519\u8BEF [${"E010" /* INVALID_LINK */}]: \u65E0\u6548\u7684 mgp:// \u94FE\u63A5\u683C\u5F0F`);
3011
+ console.error(`\u63D0\u4F9B\u7684\u94FE\u63A5: ${options.link}`);
3012
+ console.error(`\u671F\u671B\u683C\u5F0F:`);
3013
+ console.error(` \u8282\u70B9\u94FE\u63A5: mgp://[mastergo_page_url]?nodeId=[\u8282\u70B9ID]`);
3014
+ console.error(` \u9875\u9762\u94FE\u63A5: mgp://[mastergo_page_url]?pageId=[\u9875\u9762ID]`);
3015
+ process.exit(1);
3016
+ }
3017
+ const { pageUrl, nodeId, pageId } = parsed;
3018
+ const isPageLink = !!pageId;
3019
+ const client = new MGClient({
3020
+ noAutoStart: options.noAutoStart,
3021
+ noRetry: options.noRetry
3022
+ });
3023
+ try {
3024
+ await client.connect();
3025
+ let data;
3026
+ if (isPageLink) {
3027
+ const params = {
3028
+ pageId,
3029
+ maxDepth: parseInt(options.maxDepth || "3", 10),
3030
+ includeInvisible: false
3031
+ };
3032
+ data = await client.requestWithRetry("get_page_by_id" /* GET_PAGE_BY_ID */, params, pageUrl);
3033
+ } else {
3034
+ const params = {
3035
+ nodeId,
3036
+ maxDepth: parseInt(options.maxDepth || "3", 10),
3037
+ includeInvisible: false
3038
+ };
3039
+ data = await client.requestWithRetry("get_node_by_id" /* GET_NODE_BY_ID */, params, pageUrl);
3040
+ }
3041
+ const renderOptions = {
3042
+ showId: options.showId !== false,
3043
+ showSize: options.showSize !== false,
3044
+ showPadding: options.showPadding !== false,
3045
+ showRadius: options.showRadius !== false,
3046
+ showLayout: options.showLayout !== false,
3047
+ showGap: options.showGap !== false,
3048
+ compact: options.compact !== false
3049
+ };
3050
+ const asciiOutput = renderAscii(data, renderOptions);
3051
+ if (options.output) {
3052
+ const outputPath = resolve8(options.output);
3053
+ const outputDir = dirname11(outputPath);
3054
+ mkdirSync10(outputDir, { recursive: true });
3055
+ writeFileSync9(outputPath, asciiOutput, "utf-8");
3056
+ console.log(`ASCII \u793A\u610F\u56FE\u5DF2\u4FDD\u5B58\u5230: ${outputPath}`);
3057
+ console.log(`Link: ${options.link}`);
3058
+ console.log(`\u6A21\u5F0F: ${options.compact ? "\u7D27\u51D1" : "\u6807\u51C6"}`);
3059
+ console.log(`\u904D\u5386\u6DF1\u5EA6: ${options.maxDepth || "3"}`);
3060
+ } else {
3061
+ console.log(asciiOutput);
3062
+ }
3063
+ } catch (error) {
3064
+ if (error instanceof MGError) {
3065
+ console.error(`\u9519\u8BEF [${error.code}]: ${error.message}`);
3066
+ } else {
3067
+ console.error(`\u9519\u8BEF: ${error instanceof Error ? error.message : error}`);
3068
+ }
3069
+ process.exit(1);
3070
+ } finally {
3071
+ client.close();
3072
+ }
3073
+ }
3074
+
2619
3075
  // src/cli/index.ts
2620
- var program = new Command12();
3076
+ var program = new Command13();
2621
3077
  program.name("mg-cli").description("MasterGo CLI \u5DE5\u5177 - \u7528\u4E8E Claude Code \u4E0E MasterGo \u901A\u4FE1").version("1.0.0");
2622
3078
  program.addCommand(createServerCommand());
2623
3079
  program.addCommand(createGetNodeByIdCommand());
@@ -2630,5 +3086,7 @@ program.addCommand(createGetNodeForSpaceCommand());
2630
3086
  program.addCommand(createListExtensionsCommand());
2631
3087
  program.addCommand(createOpenPageCommand());
2632
3088
  program.addCommand(createNavigateToNodeCommand());
3089
+ program.addCommand(createVisualizeCommand());
3090
+ checkForUpdates();
2633
3091
  program.parse();
2634
3092
  //# sourceMappingURL=cli.js.map