@hangox/mg-cli 1.0.8 → 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 Command9 } 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 {
@@ -340,6 +472,12 @@ function extractSpaceInfo(node) {
340
472
  width: roundToOneDecimal(typeof node.width === "number" ? node.width : 0),
341
473
  height: roundToOneDecimal(typeof node.height === "number" ? node.height : 0)
342
474
  };
475
+ if (typeof node.absoluteX === "number") {
476
+ result.absoluteX = roundToOneDecimal(node.absoluteX);
477
+ }
478
+ if (typeof node.absoluteY === "number") {
479
+ result.absoluteY = roundToOneDecimal(node.absoluteY);
480
+ }
343
481
  if (node.children && Array.isArray(node.children) && node.children.length > 0) {
344
482
  result.children = node.children.map((child) => extractSpaceInfo(child));
345
483
  }
@@ -365,6 +503,7 @@ var ErrorNames = {
365
503
  ["E015" /* SERVER_START_FAILED */]: "SERVER_START_FAILED",
366
504
  ["E016" /* SERVER_ALREADY_RUNNING */]: "SERVER_ALREADY_RUNNING",
367
505
  ["E017" /* CONNECTION_LOST */]: "CONNECTION_LOST",
506
+ ["E018" /* NO_EXTENSION_CONNECTED */]: "NO_EXTENSION_CONNECTED",
368
507
  ["E099" /* UNKNOWN_ERROR */]: "UNKNOWN_ERROR"
369
508
  };
370
509
  var ErrorMessages = {
@@ -385,6 +524,7 @@ var ErrorMessages = {
385
524
  ["E015" /* SERVER_START_FAILED */]: "\u81EA\u52A8\u542F\u52A8 Server \u5931\u8D25",
386
525
  ["E016" /* SERVER_ALREADY_RUNNING */]: "Server \u5DF2\u5728\u8FD0\u884C\u4E2D",
387
526
  ["E017" /* CONNECTION_LOST */]: "\u8FDE\u63A5\u65AD\u5F00",
527
+ ["E018" /* NO_EXTENSION_CONNECTED */]: "\u6CA1\u6709 Chrome \u6269\u5C55\u8FDE\u63A5\u5230 Server\u3002\u8BF7\u786E\u4FDD\u6D4F\u89C8\u5668\u5DF2\u6253\u5F00\u5E76\u5B89\u88C5\u4E86 MG Plugin",
388
528
  ["E099" /* UNKNOWN_ERROR */]: "\u672A\u77E5\u9519\u8BEF"
389
529
  };
390
530
  var MGError = class extends Error {
@@ -420,8 +560,8 @@ var MGError = class extends Error {
420
560
  import { WebSocketServer } from "ws";
421
561
 
422
562
  // src/server/logger.ts
423
- import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
424
- import { dirname as dirname2 } from "path";
563
+ import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
564
+ import { dirname as dirname3 } from "path";
425
565
  var levelPriority = {
426
566
  ["DEBUG" /* DEBUG */]: 0,
427
567
  ["INFO" /* INFO */]: 1,
@@ -438,9 +578,9 @@ var Logger = class {
438
578
  minLevel: options.minLevel ?? "INFO" /* INFO */
439
579
  };
440
580
  if (this.options.file) {
441
- const dir = dirname2(this.options.filePath);
442
- if (!existsSync2(dir)) {
443
- mkdirSync2(dir, { recursive: true });
581
+ const dir = dirname3(this.options.filePath);
582
+ if (!existsSync4(dir)) {
583
+ mkdirSync3(dir, { recursive: true });
444
584
  }
445
585
  }
446
586
  }
@@ -498,6 +638,12 @@ var ConnectionManager = class {
498
638
  providers = /* @__PURE__ */ new Map();
499
639
  /** Consumer 连接 */
500
640
  consumers = /* @__PURE__ */ new Map();
641
+ /** Chrome 扩展连接(按扩展 ID 索引) */
642
+ chromeExtensions = /* @__PURE__ */ new Map();
643
+ /** 扩展序号映射(扩展 ID → 序号) */
644
+ extensionIndexMap = /* @__PURE__ */ new Map();
645
+ /** 下一个可用的扩展序号 */
646
+ nextExtensionIndex = 1;
501
647
  /** 所有连接(按 ID 索引) */
502
648
  allConnections = /* @__PURE__ */ new Map();
503
649
  /** 心跳检查定时器 */
@@ -592,6 +738,16 @@ var ConnectionManager = class {
592
738
  } else if (connectionInfo.type === "consumer" /* CONSUMER */) {
593
739
  this.consumers.delete(connectionId);
594
740
  this.logger.info(`Consumer \u65AD\u5F00: ${connectionId}`);
741
+ } else if (connectionInfo.type === "chrome_extension" /* CHROME_EXTENSION */) {
742
+ for (const [extensionId, extWs] of this.chromeExtensions) {
743
+ if (extWs.connectionId === connectionId) {
744
+ const index = this.extensionIndexMap.get(extensionId);
745
+ this.chromeExtensions.delete(extensionId);
746
+ this.extensionIndexMap.delete(extensionId);
747
+ this.logger.info(`Chrome \u6269\u5C55\u65AD\u5F00: ${extensionId} (\u5E8F\u53F7: #${index})`);
748
+ break;
749
+ }
750
+ }
595
751
  }
596
752
  }
597
753
  /**
@@ -645,6 +801,7 @@ var ConnectionManager = class {
645
801
  return {
646
802
  providers: providerCount,
647
803
  consumers: this.consumers.size,
804
+ extensions: this.chromeExtensions.size,
648
805
  total: this.allConnections.size
649
806
  };
650
807
  }
@@ -654,6 +811,48 @@ var ConnectionManager = class {
654
811
  getConnectedPageUrls() {
655
812
  return Array.from(this.providers.keys());
656
813
  }
814
+ /**
815
+ * 添加 Chrome 扩展连接
816
+ */
817
+ addChromeExtension(ws, extensionId) {
818
+ const managedWs = this.addConnection(ws, "chrome_extension" /* CHROME_EXTENSION */);
819
+ const index = this.nextExtensionIndex++;
820
+ this.extensionIndexMap.set(extensionId, index);
821
+ this.chromeExtensions.set(extensionId, managedWs);
822
+ this.logger.info(`Chrome \u6269\u5C55\u8FDE\u63A5: ${extensionId} (\u5206\u914D\u5E8F\u53F7: #${index})`);
823
+ return managedWs;
824
+ }
825
+ /**
826
+ * 根据序号查找扩展
827
+ */
828
+ findExtensionByIndex(index) {
829
+ for (const [extensionId, extIndex] of this.extensionIndexMap) {
830
+ if (extIndex === index) {
831
+ return this.chromeExtensions.get(extensionId);
832
+ }
833
+ }
834
+ return void 0;
835
+ }
836
+ /**
837
+ * 获取第一个扩展
838
+ */
839
+ getFirstExtension() {
840
+ return this.chromeExtensions.values().next().value;
841
+ }
842
+ /**
843
+ * 获取所有扩展信息
844
+ */
845
+ getAllExtensions() {
846
+ const result = [];
847
+ for (const [extensionId, ws] of this.chromeExtensions) {
848
+ const index = this.extensionIndexMap.get(extensionId);
849
+ if (index !== void 0) {
850
+ result.push({ index, extensionId, ws });
851
+ }
852
+ }
853
+ result.sort((a, b) => a.index - b.index);
854
+ return result;
855
+ }
657
856
  /**
658
857
  * 关闭所有连接
659
858
  */
@@ -665,6 +864,8 @@ var ConnectionManager = class {
665
864
  }
666
865
  this.providers.clear();
667
866
  this.consumers.clear();
867
+ this.chromeExtensions.clear();
868
+ this.extensionIndexMap.clear();
668
869
  this.allConnections.clear();
669
870
  }
670
871
  };
@@ -782,6 +983,24 @@ var RequestHandler = class {
782
983
  this.logger.error(`\u53D1\u9001\u9519\u8BEF\u54CD\u5E94\u5931\u8D25: ${requestId}`, err);
783
984
  }
784
985
  }
986
+ /**
987
+ * 注册待处理请求(供外部使用,如 OPEN_PAGE)
988
+ * @param requestId 请求 ID
989
+ * @param consumer 发起请求的 Consumer
990
+ * @param sendCallback 发送请求的回调函数
991
+ */
992
+ registerPendingRequest(requestId, consumer, sendCallback) {
993
+ const timer = setTimeout(() => {
994
+ this.handleTimeout(requestId);
995
+ }, REQUEST_TIMEOUT);
996
+ this.pendingRequests.set(requestId, {
997
+ id: requestId,
998
+ consumer,
999
+ timer,
1000
+ timestamp: Date.now()
1001
+ });
1002
+ sendCallback();
1003
+ }
785
1004
  /**
786
1005
  * 清理特定连接的所有待处理请求
787
1006
  */
@@ -802,48 +1021,6 @@ var RequestHandler = class {
802
1021
  }
803
1022
  };
804
1023
 
805
- // src/shared/version.ts
806
- import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
807
- import { fileURLToPath } from "url";
808
- import { dirname as dirname3, join as join2 } from "path";
809
- var cachedVersion = null;
810
- function getVersion() {
811
- if (cachedVersion !== null) {
812
- return cachedVersion;
813
- }
814
- try {
815
- const currentFile = fileURLToPath(import.meta.url);
816
- const currentDir = dirname3(currentFile);
817
- const packageJsonPaths = [
818
- join2(currentDir, "..", "package.json"),
819
- // dist/xxx.js -> ../package.json
820
- join2(currentDir, "..", "..", "package.json")
821
- // src/shared/version.ts -> ../../package.json
822
- ];
823
- for (const packageJsonPath of packageJsonPaths) {
824
- if (existsSync3(packageJsonPath)) {
825
- const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
826
- if (packageJson.name === "@hangox/mg-cli") {
827
- cachedVersion = packageJson.version || "0.0.0";
828
- return cachedVersion;
829
- }
830
- }
831
- }
832
- cachedVersion = "0.0.0";
833
- return cachedVersion;
834
- } catch {
835
- cachedVersion = "0.0.0";
836
- return cachedVersion;
837
- }
838
- }
839
- var DEV_VERSION = "9.9.9";
840
- function isVersionMatch(version1, version2) {
841
- if (version1 === DEV_VERSION || version2 === DEV_VERSION) {
842
- return true;
843
- }
844
- return version1 === version2;
845
- }
846
-
847
1024
  // src/server/websocket-server.ts
848
1025
  var MGServer = class {
849
1026
  wss = null;
@@ -867,7 +1044,7 @@ var MGServer = class {
867
1044
  throw new MGError("E016" /* SERVER_ALREADY_RUNNING */, "Server \u5DF2\u5728\u8FD0\u884C\u4E2D");
868
1045
  }
869
1046
  const port = await this.findAvailablePort();
870
- return new Promise((resolve8, reject) => {
1047
+ return new Promise((resolve9, reject) => {
871
1048
  this.wss = new WebSocketServer({ port });
872
1049
  this.wss.on("listening", () => {
873
1050
  this.port = port;
@@ -875,7 +1052,7 @@ var MGServer = class {
875
1052
  this.startedAt = /* @__PURE__ */ new Date();
876
1053
  this.logger.info(`Server \u542F\u52A8\u6210\u529F\uFF0C\u76D1\u542C\u7AEF\u53E3: ${port}`);
877
1054
  this.connectionManager.startHeartbeatCheck(HEARTBEAT_INTERVAL);
878
- resolve8(port);
1055
+ resolve9(port);
879
1056
  });
880
1057
  this.wss.on("error", (error) => {
881
1058
  this.logger.error("Server \u9519\u8BEF:", error);
@@ -906,14 +1083,14 @@ var MGServer = class {
906
1083
  * 检查端口是否可用
907
1084
  */
908
1085
  isPortAvailable(port) {
909
- return new Promise((resolve8) => {
1086
+ return new Promise((resolve9) => {
910
1087
  const testServer = new WebSocketServer({ port });
911
1088
  testServer.on("listening", () => {
912
1089
  testServer.close();
913
- resolve8(true);
1090
+ resolve9(true);
914
1091
  });
915
1092
  testServer.on("error", () => {
916
- resolve8(false);
1093
+ resolve9(false);
917
1094
  });
918
1095
  });
919
1096
  }
@@ -961,12 +1138,22 @@ var MGServer = class {
961
1138
  */
962
1139
  handleRegister(ws, message) {
963
1140
  const { connectionType, pageUrl, pageId } = message.data;
964
- const managedWs = this.connectionManager.addConnection(
965
- ws,
966
- connectionType,
967
- pageUrl,
968
- pageId
969
- );
1141
+ const extensionId = message.data.extensionId;
1142
+ let managedWs;
1143
+ let extensionIndex;
1144
+ if (connectionType === "chrome_extension" /* CHROME_EXTENSION */ && extensionId) {
1145
+ managedWs = this.connectionManager.addChromeExtension(ws, extensionId);
1146
+ const exts = this.connectionManager.getAllExtensions();
1147
+ const ext = exts.find((e) => e.extensionId === extensionId);
1148
+ extensionIndex = ext?.index;
1149
+ } else {
1150
+ managedWs = this.connectionManager.addConnection(
1151
+ ws,
1152
+ connectionType,
1153
+ pageUrl,
1154
+ pageId
1155
+ );
1156
+ }
970
1157
  const ack = {
971
1158
  id: message.id || "",
972
1159
  type: "register_ack" /* REGISTER_ACK */,
@@ -974,7 +1161,8 @@ var MGServer = class {
974
1161
  data: {
975
1162
  connectionId: managedWs.connectionId,
976
1163
  pageUrl,
977
- serverVersion: getVersion()
1164
+ serverVersion: getVersion(),
1165
+ ...extensionIndex !== void 0 && { extensionIndex }
978
1166
  }
979
1167
  };
980
1168
  ws.send(JSON.stringify(ack));
@@ -991,6 +1179,15 @@ var MGServer = class {
991
1179
  case "get_server_status" /* GET_SERVER_STATUS */:
992
1180
  this.handleGetServerStatus(ws, message);
993
1181
  break;
1182
+ case "list_extensions" /* LIST_EXTENSIONS */:
1183
+ this.handleListExtensions(ws, message);
1184
+ break;
1185
+ case "open_page" /* OPEN_PAGE */:
1186
+ this.handleOpenPage(ws, message);
1187
+ break;
1188
+ case "navigate_to_node" /* NAVIGATE_TO_NODE */:
1189
+ this.handleNavigateToNode(ws, message);
1190
+ break;
994
1191
  case "response" /* RESPONSE */:
995
1192
  case "error" /* ERROR */:
996
1193
  this.requestHandler.handleResponse(message);
@@ -1023,6 +1220,13 @@ var MGServer = class {
1023
1220
  connectedAt: info.connectedAt.toISOString(),
1024
1221
  lastActiveAt: info.lastActiveAt.toISOString()
1025
1222
  }));
1223
+ const extensions = this.connectionManager.getAllExtensions();
1224
+ const connectedExtensions = extensions.map((ext) => ({
1225
+ index: ext.index,
1226
+ extensionId: ext.extensionId,
1227
+ connectedAt: ext.ws.connectionInfo.connectedAt.toISOString(),
1228
+ lastActiveAt: ext.ws.connectionInfo.lastActiveAt.toISOString()
1229
+ }));
1026
1230
  const uptimeMs = this.startedAt ? Date.now() - this.startedAt.getTime() : 0;
1027
1231
  const statusData = {
1028
1232
  running: this.isRunning,
@@ -1032,7 +1236,8 @@ var MGServer = class {
1032
1236
  uptime: formatDuration(uptimeMs),
1033
1237
  version: getVersion(),
1034
1238
  stats,
1035
- connectedPages
1239
+ connectedPages,
1240
+ connectedExtensions
1036
1241
  };
1037
1242
  const response = {
1038
1243
  id: message.id || "",
@@ -1043,6 +1248,156 @@ var MGServer = class {
1043
1248
  ws.send(JSON.stringify(response));
1044
1249
  this.logger.info("\u8FD4\u56DE Server \u72B6\u6001\u4FE1\u606F");
1045
1250
  }
1251
+ /**
1252
+ * 处理扩展列表查询
1253
+ */
1254
+ handleListExtensions(ws, message) {
1255
+ const extensions = this.connectionManager.getAllExtensions();
1256
+ const extensionList = extensions.map((ext) => ({
1257
+ index: ext.index,
1258
+ extensionId: ext.extensionId,
1259
+ connectedAt: ext.ws.connectionInfo.connectedAt.toISOString(),
1260
+ lastActiveAt: ext.ws.connectionInfo.lastActiveAt.toISOString()
1261
+ }));
1262
+ const response = {
1263
+ id: message.id || "",
1264
+ type: "response" /* RESPONSE */,
1265
+ success: true,
1266
+ data: {
1267
+ extensions: extensionList,
1268
+ totalCount: extensionList.length
1269
+ }
1270
+ };
1271
+ ws.send(JSON.stringify(response));
1272
+ this.logger.info(`\u8FD4\u56DE\u6269\u5C55\u5217\u8868\uFF0C\u5171 ${extensionList.length} \u4E2A\u6269\u5C55`);
1273
+ }
1274
+ /**
1275
+ * 处理打开页面请求
1276
+ */
1277
+ handleOpenPage(ws, message) {
1278
+ const params = message.params;
1279
+ const requestId = message.id || "";
1280
+ if (!params?.url) {
1281
+ const errorResponse = {
1282
+ id: requestId,
1283
+ type: "error" /* ERROR */,
1284
+ success: false,
1285
+ error: {
1286
+ code: "E011" /* INVALID_PARAMS */,
1287
+ name: "INVALID_PARAMS",
1288
+ message: "\u7F3A\u5C11 url \u53C2\u6570"
1289
+ }
1290
+ };
1291
+ ws.send(JSON.stringify(errorResponse));
1292
+ return;
1293
+ }
1294
+ const targetIndex = params.extensionIndex || 1;
1295
+ const targetExtension = this.connectionManager.findExtensionByIndex(targetIndex);
1296
+ if (!targetExtension) {
1297
+ const errorResponse = {
1298
+ id: requestId,
1299
+ type: "error" /* ERROR */,
1300
+ success: false,
1301
+ error: {
1302
+ code: "E018" /* NO_EXTENSION_CONNECTED */,
1303
+ name: "NO_EXTENSION_CONNECTED",
1304
+ message: targetIndex === 1 ? "\u6CA1\u6709 Chrome \u6269\u5C55\u8FDE\u63A5\u5230 Server\u3002\u8BF7\u786E\u4FDD\u6D4F\u89C8\u5668\u5DF2\u6253\u5F00\u5E76\u5B89\u88C5\u4E86 MG Plugin" : `\u672A\u627E\u5230\u5E8F\u53F7\u4E3A #${targetIndex} \u7684\u6269\u5C55\u5B9E\u4F8B`
1305
+ }
1306
+ };
1307
+ ws.send(JSON.stringify(errorResponse));
1308
+ this.logger.warn(`\u672A\u627E\u5230\u6269\u5C55\u5B9E\u4F8B #${targetIndex}`);
1309
+ return;
1310
+ }
1311
+ const forwardMessage = {
1312
+ id: requestId,
1313
+ type: "open_page" /* OPEN_PAGE */,
1314
+ params: { url: params.url }
1315
+ };
1316
+ this.requestHandler.registerPendingRequest(requestId, ws, () => {
1317
+ try {
1318
+ targetExtension.send(JSON.stringify(forwardMessage));
1319
+ this.logger.info(`\u8F6C\u53D1 OPEN_PAGE \u8BF7\u6C42\u5230\u6269\u5C55 #${targetIndex}: ${params.url}`);
1320
+ } catch (error) {
1321
+ this.logger.error(`\u8F6C\u53D1\u8BF7\u6C42\u5931\u8D25: ${error}`);
1322
+ const errorResponse = {
1323
+ id: requestId,
1324
+ type: "error" /* ERROR */,
1325
+ success: false,
1326
+ error: {
1327
+ code: "E001" /* CONNECTION_FAILED */,
1328
+ name: "CONNECTION_FAILED",
1329
+ message: "\u8F6C\u53D1\u8BF7\u6C42\u5931\u8D25"
1330
+ }
1331
+ };
1332
+ ws.send(JSON.stringify(errorResponse));
1333
+ }
1334
+ });
1335
+ }
1336
+ /**
1337
+ * 处理节点导航请求
1338
+ */
1339
+ handleNavigateToNode(ws, message) {
1340
+ const params = message.params;
1341
+ const requestId = message.id || "";
1342
+ if (!params?.pageUrl || !params?.nodeId) {
1343
+ const errorResponse = {
1344
+ id: requestId,
1345
+ type: "error" /* ERROR */,
1346
+ success: false,
1347
+ error: {
1348
+ code: "E011" /* INVALID_PARAMS */,
1349
+ name: "INVALID_PARAMS",
1350
+ message: "\u7F3A\u5C11 pageUrl \u6216 nodeId \u53C2\u6570"
1351
+ }
1352
+ };
1353
+ ws.send(JSON.stringify(errorResponse));
1354
+ return;
1355
+ }
1356
+ const targetIndex = params.extensionIndex || 1;
1357
+ const targetExtension = this.connectionManager.findExtensionByIndex(targetIndex);
1358
+ if (!targetExtension) {
1359
+ const errorResponse = {
1360
+ id: requestId,
1361
+ type: "error" /* ERROR */,
1362
+ success: false,
1363
+ error: {
1364
+ code: "E018" /* NO_EXTENSION_CONNECTED */,
1365
+ name: "NO_EXTENSION_CONNECTED",
1366
+ message: targetIndex === 1 ? "\u6CA1\u6709 Chrome \u6269\u5C55\u8FDE\u63A5\u5230 Server\u3002\u8BF7\u786E\u4FDD\u6D4F\u89C8\u5668\u5DF2\u6253\u5F00\u5E76\u5B89\u88C5\u4E86 MG Plugin" : `\u672A\u627E\u5230\u5E8F\u53F7\u4E3A #${targetIndex} \u7684\u6269\u5C55\u5B9E\u4F8B`
1367
+ }
1368
+ };
1369
+ ws.send(JSON.stringify(errorResponse));
1370
+ this.logger.warn(`\u672A\u627E\u5230\u6269\u5C55\u5B9E\u4F8B #${targetIndex}`);
1371
+ return;
1372
+ }
1373
+ const forwardMessage = {
1374
+ id: requestId,
1375
+ type: "navigate_to_node" /* NAVIGATE_TO_NODE */,
1376
+ params: {
1377
+ pageUrl: params.pageUrl,
1378
+ nodeId: params.nodeId
1379
+ }
1380
+ };
1381
+ this.requestHandler.registerPendingRequest(requestId, ws, () => {
1382
+ try {
1383
+ targetExtension.send(JSON.stringify(forwardMessage));
1384
+ this.logger.info(`\u8F6C\u53D1 NAVIGATE_TO_NODE \u8BF7\u6C42\u5230\u6269\u5C55 #${targetIndex}: ${params.pageUrl} -> ${params.nodeId}`);
1385
+ } catch (error) {
1386
+ this.logger.error(`\u8F6C\u53D1\u8BF7\u6C42\u5931\u8D25: ${error}`);
1387
+ const errorResponse = {
1388
+ id: requestId,
1389
+ type: "error" /* ERROR */,
1390
+ success: false,
1391
+ error: {
1392
+ code: "E001" /* CONNECTION_FAILED */,
1393
+ name: "CONNECTION_FAILED",
1394
+ message: "\u8F6C\u53D1\u8BF7\u6C42\u5931\u8D25"
1395
+ }
1396
+ };
1397
+ ws.send(JSON.stringify(errorResponse));
1398
+ }
1399
+ });
1400
+ }
1046
1401
  /**
1047
1402
  * 停止服务器
1048
1403
  */
@@ -1053,12 +1408,12 @@ var MGServer = class {
1053
1408
  this.logger.info("\u6B63\u5728\u505C\u6B62 Server...");
1054
1409
  this.requestHandler.cleanupAll();
1055
1410
  this.connectionManager.closeAll();
1056
- return new Promise((resolve8) => {
1411
+ return new Promise((resolve9) => {
1057
1412
  this.wss.close(() => {
1058
1413
  this.isRunning = false;
1059
1414
  this.wss = null;
1060
1415
  this.logger.info("Server \u5DF2\u505C\u6B62");
1061
- resolve8();
1416
+ resolve9();
1062
1417
  });
1063
1418
  });
1064
1419
  }
@@ -1159,7 +1514,7 @@ async function startServerDaemon(port) {
1159
1514
  ensureConfigDir();
1160
1515
  const currentFile = fileURLToPath2(import.meta.url);
1161
1516
  const currentDir = dirname4(currentFile);
1162
- const serverScript = join3(currentDir, "daemon-runner.js");
1517
+ const serverScript = join4(currentDir, "daemon-runner.js");
1163
1518
  const args = ["--foreground"];
1164
1519
  if (port) {
1165
1520
  args.push("--port", String(port));
@@ -1175,7 +1530,7 @@ async function startServerDaemon(port) {
1175
1530
  child.unref();
1176
1531
  const startTime = Date.now();
1177
1532
  while (Date.now() - startTime < SERVER_START_TIMEOUT) {
1178
- await new Promise((resolve8) => setTimeout(resolve8, 200));
1533
+ await new Promise((resolve9) => setTimeout(resolve9, 200));
1179
1534
  const { running: running2, info: info2 } = isServerRunning();
1180
1535
  if (running2 && info2) {
1181
1536
  return info2;
@@ -1196,7 +1551,7 @@ function stopServer() {
1196
1551
  }
1197
1552
  async function restartServer(port) {
1198
1553
  const { info: oldInfo } = stopServer();
1199
- await new Promise((resolve8) => setTimeout(resolve8, 500));
1554
+ await new Promise((resolve9) => setTimeout(resolve9, 500));
1200
1555
  return startServerDaemon(port || oldInfo?.port);
1201
1556
  }
1202
1557
  function getServerStatus() {
@@ -1269,7 +1624,7 @@ var MGClient = class {
1269
1624
  * 尝试连接指定端口
1270
1625
  */
1271
1626
  tryConnect(port) {
1272
- return new Promise((resolve8, reject) => {
1627
+ return new Promise((resolve9, reject) => {
1273
1628
  const ws = new WebSocket2(`ws://localhost:${port}`);
1274
1629
  const timer = setTimeout(() => {
1275
1630
  ws.close();
@@ -1279,7 +1634,7 @@ var MGClient = class {
1279
1634
  clearTimeout(timer);
1280
1635
  this.ws = ws;
1281
1636
  this.register();
1282
- resolve8();
1637
+ resolve9();
1283
1638
  });
1284
1639
  ws.on("error", (error) => {
1285
1640
  clearTimeout(timer);
@@ -1332,7 +1687,7 @@ var MGClient = class {
1332
1687
  pageUrl,
1333
1688
  timestamp: Date.now()
1334
1689
  };
1335
- return new Promise((resolve8, reject) => {
1690
+ return new Promise((resolve9, reject) => {
1336
1691
  const timer = setTimeout(() => {
1337
1692
  reject(new MGError("E012" /* REQUEST_TIMEOUT */, ErrorMessages["E012" /* REQUEST_TIMEOUT */]));
1338
1693
  }, REQUEST_TIMEOUT);
@@ -1343,7 +1698,7 @@ var MGClient = class {
1343
1698
  clearTimeout(timer);
1344
1699
  this.ws?.off("message", messageHandler);
1345
1700
  if (response.success) {
1346
- resolve8(response.data);
1701
+ resolve9(response.data);
1347
1702
  } else {
1348
1703
  const error = response.error;
1349
1704
  reject(
@@ -1525,11 +1880,11 @@ function createServerCommand() {
1525
1880
 
1526
1881
  // src/cli/commands/get-node-by-id.ts
1527
1882
  import { Command as Command2 } from "commander";
1528
- import { writeFileSync as writeFileSync2 } from "fs";
1883
+ import { writeFileSync as writeFileSync3 } from "fs";
1529
1884
  import { resolve as resolve2, dirname as dirname5 } from "path";
1530
- import { mkdirSync as mkdirSync3 } from "fs";
1885
+ import { mkdirSync as mkdirSync4 } from "fs";
1531
1886
  function createGetNodeByIdCommand() {
1532
- 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("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
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) => {
1533
1888
  await handleGetNodeById(options);
1534
1889
  });
1535
1890
  }
@@ -1554,15 +1909,16 @@ async function handleGetNodeById(options) {
1554
1909
  const outputData = options.raw ? data : trimNodeDefaults(data);
1555
1910
  const outputPath = resolve2(options.output);
1556
1911
  const outputDir = dirname5(outputPath);
1557
- mkdirSync3(outputDir, { recursive: true });
1558
- const jsonContent = JSON.stringify(outputData, null, 2);
1559
- writeFileSync2(outputPath, jsonContent, "utf-8");
1912
+ mkdirSync4(outputDir, { recursive: true });
1913
+ const jsonContent = JSON.stringify(outputData, null, options.pretty ? 2 : 0);
1914
+ writeFileSync3(outputPath, jsonContent, "utf-8");
1560
1915
  const size = jsonContent.length;
1561
1916
  const sizeKB = (size / 1024).toFixed(2);
1562
1917
  console.log(`\u6587\u4EF6\u8DEF\u5F84: ${outputPath}`);
1563
1918
  console.log(`\u8282\u70B9 ID: ${options.nodeId}`);
1564
1919
  console.log(`\u6570\u636E\u5927\u5C0F: ${size.toLocaleString()} \u5B57\u7B26 (\u7EA6 ${sizeKB} KB)`);
1565
1920
  console.log(`\u8282\u70B9\u6DF1\u5EA6: ${params.maxDepth}`);
1921
+ console.log(`\u8F93\u51FA\u683C\u5F0F: ${options.pretty ? "\u683C\u5F0F\u5316" : "\u538B\u7F29"}`);
1566
1922
  if (!options.raw) {
1567
1923
  console.log(`\u6570\u636E\u6A21\u5F0F: \u7CBE\u7B80\u6A21\u5F0F (\u4F7F\u7528 --raw \u83B7\u53D6\u5B8C\u6574\u6570\u636E)`);
1568
1924
  }
@@ -1576,11 +1932,11 @@ async function handleGetNodeById(options) {
1576
1932
 
1577
1933
  // src/cli/commands/get-node-by-link.ts
1578
1934
  import { Command as Command3 } from "commander";
1579
- import { writeFileSync as writeFileSync3 } from "fs";
1935
+ import { writeFileSync as writeFileSync4 } from "fs";
1580
1936
  import { resolve as resolve3, dirname as dirname6 } from "path";
1581
- import { mkdirSync as mkdirSync4 } from "fs";
1937
+ import { mkdirSync as mkdirSync5 } from "fs";
1582
1938
  function createGetNodeByLinkCommand() {
1583
- 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("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
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) => {
1584
1940
  await handleGetNodeByLink(options);
1585
1941
  });
1586
1942
  }
@@ -1629,9 +1985,9 @@ async function handleGetNodeByLink(options) {
1629
1985
  const outputData = options.raw ? data : trimNodeDefaults(data);
1630
1986
  const outputPath = resolve3(options.output);
1631
1987
  const outputDir = dirname6(outputPath);
1632
- mkdirSync4(outputDir, { recursive: true });
1633
- const jsonContent = JSON.stringify(outputData, null, 2);
1634
- writeFileSync3(outputPath, jsonContent, "utf-8");
1988
+ mkdirSync5(outputDir, { recursive: true });
1989
+ const jsonContent = JSON.stringify(outputData, null, options.pretty ? 2 : 0);
1990
+ writeFileSync4(outputPath, jsonContent, "utf-8");
1635
1991
  const size = jsonContent.length;
1636
1992
  const sizeKB = (size / 1024).toFixed(2);
1637
1993
  console.log(`\u6587\u4EF6\u8DEF\u5F84: ${outputPath}`);
@@ -1646,6 +2002,7 @@ async function handleGetNodeByLink(options) {
1646
2002
  }
1647
2003
  console.log(`\u6570\u636E\u5927\u5C0F: ${size.toLocaleString()} \u5B57\u7B26 (\u7EA6 ${sizeKB} KB)`);
1648
2004
  console.log(`\u904D\u5386\u6DF1\u5EA6: ${options.maxDepth || "1"}`);
2005
+ console.log(`\u8F93\u51FA\u683C\u5F0F: ${options.pretty ? "\u683C\u5F0F\u5316" : "\u538B\u7F29"}`);
1649
2006
  if (!options.raw) {
1650
2007
  console.log(`\u6570\u636E\u6A21\u5F0F: \u7CBE\u7B80\u6A21\u5F0F (\u4F7F\u7528 --raw \u83B7\u53D6\u5B8C\u6574\u6570\u636E)`);
1651
2008
  }
@@ -1663,11 +2020,11 @@ async function handleGetNodeByLink(options) {
1663
2020
 
1664
2021
  // src/cli/commands/get-all-nodes.ts
1665
2022
  import { Command as Command4 } from "commander";
1666
- import { writeFileSync as writeFileSync4 } from "fs";
2023
+ import { writeFileSync as writeFileSync5 } from "fs";
1667
2024
  import { resolve as resolve4, dirname as dirname7 } from "path";
1668
- import { mkdirSync as mkdirSync5 } from "fs";
2025
+ import { mkdirSync as mkdirSync6 } from "fs";
1669
2026
  function createGetAllNodesCommand() {
1670
- 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("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
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) => {
1671
2028
  await handleGetAllNodes(options);
1672
2029
  });
1673
2030
  }
@@ -1686,9 +2043,9 @@ async function handleGetAllNodes(options) {
1686
2043
  const outputData = options.raw ? data : Array.isArray(data) ? data.map((node) => trimNodeDefaults(node)) : trimNodeDefaults(data);
1687
2044
  const outputPath = resolve4(options.output);
1688
2045
  const outputDir = dirname7(outputPath);
1689
- mkdirSync5(outputDir, { recursive: true });
1690
- const jsonContent = JSON.stringify(outputData, null, 2);
1691
- writeFileSync4(outputPath, jsonContent, "utf-8");
2046
+ mkdirSync6(outputDir, { recursive: true });
2047
+ const jsonContent = JSON.stringify(outputData, null, options.pretty ? 2 : 0);
2048
+ writeFileSync5(outputPath, jsonContent, "utf-8");
1692
2049
  const size = jsonContent.length;
1693
2050
  const sizeKB = (size / 1024).toFixed(2);
1694
2051
  const nodeCount = Array.isArray(data) ? data.length : 1;
@@ -1696,6 +2053,7 @@ async function handleGetAllNodes(options) {
1696
2053
  console.log(`\u8282\u70B9\u6570\u91CF: ${nodeCount}`);
1697
2054
  console.log(`\u6570\u636E\u5927\u5C0F: ${size.toLocaleString()} \u5B57\u7B26 (\u7EA6 ${sizeKB} KB)`);
1698
2055
  console.log(`\u8282\u70B9\u6DF1\u5EA6: ${params.maxDepth}`);
2056
+ console.log(`\u8F93\u51FA\u683C\u5F0F: ${options.pretty ? "\u683C\u5F0F\u5316" : "\u538B\u7F29"}`);
1699
2057
  if (!options.raw) {
1700
2058
  console.log(`\u6570\u636E\u6A21\u5F0F: \u7CBE\u7B80\u6A21\u5F0F (\u4F7F\u7528 --raw \u83B7\u53D6\u5B8C\u6574\u6570\u636E)`);
1701
2059
  }
@@ -1709,9 +2067,9 @@ async function handleGetAllNodes(options) {
1709
2067
 
1710
2068
  // src/cli/commands/export-image.ts
1711
2069
  import { Command as Command5 } from "commander";
1712
- import { writeFileSync as writeFileSync5, unlinkSync as unlinkSync2 } from "fs";
2070
+ import { writeFileSync as writeFileSync6, unlinkSync as unlinkSync2 } from "fs";
1713
2071
  import { resolve as resolve5, dirname as dirname8, extname, basename } from "path";
1714
- import { mkdirSync as mkdirSync6 } from "fs";
2072
+ import { mkdirSync as mkdirSync7 } from "fs";
1715
2073
  import { tmpdir } from "os";
1716
2074
  import { vdConvert } from "vd-tool";
1717
2075
  function createExportImageCommand() {
@@ -1801,23 +2159,23 @@ async function handleExportImage(options) {
1801
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");
1802
2160
  }
1803
2161
  const outputDir = dirname8(outputPath);
1804
- mkdirSync6(outputDir, { recursive: true });
2162
+ mkdirSync7(outputDir, { recursive: true });
1805
2163
  const buffer = Buffer.from(response.data, "base64");
1806
2164
  let finalPath = outputPath;
1807
2165
  let finalSize = buffer.length;
1808
2166
  if (isVectorFormat) {
1809
2167
  const tempSvgPath = resolve5(tmpdir(), `temp_${Date.now()}.svg`);
1810
- writeFileSync5(tempSvgPath, buffer);
2168
+ writeFileSync6(tempSvgPath, buffer);
1811
2169
  try {
1812
2170
  const vectorOutputDir = dirname8(outputPath);
1813
2171
  const convertedPath = await convertSvgToVector(tempSvgPath, vectorOutputDir);
1814
2172
  const expectedOutputName = basename(tempSvgPath, ".svg") + ".xml";
1815
2173
  const expectedOutputPath = resolve5(vectorOutputDir, expectedOutputName);
1816
2174
  if (expectedOutputPath !== outputPath) {
1817
- const { renameSync, existsSync: existsSync4 } = await import("fs");
1818
- if (existsSync4(expectedOutputPath)) {
2175
+ const { renameSync, existsSync: existsSync5 } = await import("fs");
2176
+ if (existsSync5(expectedOutputPath)) {
1819
2177
  renameSync(expectedOutputPath, outputPath);
1820
- } else if (existsSync4(convertedPath)) {
2178
+ } else if (existsSync5(convertedPath)) {
1821
2179
  renameSync(convertedPath, outputPath);
1822
2180
  }
1823
2181
  }
@@ -1831,7 +2189,7 @@ async function handleExportImage(options) {
1831
2189
  }
1832
2190
  }
1833
2191
  } else {
1834
- writeFileSync5(outputPath, buffer);
2192
+ writeFileSync6(outputPath, buffer);
1835
2193
  }
1836
2194
  const sizeKB = (finalSize / 1024).toFixed(2);
1837
2195
  console.log(`\u6587\u4EF6\u8DEF\u5F84: ${finalPath}`);
@@ -1895,7 +2253,7 @@ async function convertSvgToVector(svgPath, outputDir) {
1895
2253
  // src/cli/commands/execute-code.ts
1896
2254
  import { Command as Command6 } from "commander";
1897
2255
  function createExecuteCodeCommand() {
1898
- return new Command6("execute_code").description("\u5728 MasterGo \u9875\u9762\u6267\u884C\u81EA\u5B9A\u4E49 JavaScript \u4EE3\u7801\u3002\u901A\u8FC7 mg \u53D8\u91CF\u8BBF\u95EE MasterGo API\uFF0C\u7ED3\u679C\u4F1A\u88AB JSON \u5E8F\u5217\u5316\u8FD4\u56DE").argument("<code>", "\u8981\u6267\u884C\u7684\u4EE3\u7801\u3002\u53EF\u4F7F\u7528 mg \u53D8\u91CF\uFF0C\u5982 mg.currentPage.name\u3001mg.currentPage.selection").option("--link <url>", "mgp:// \u534F\u8BAE\u94FE\u63A5\uFF0C\u7528\u4E8E\u6307\u5B9A\u76EE\u6807\u9875\u9762\u3002\u4E0E --domain/--fileId \u4E8C\u9009\u4E00").option("--domain <domain>", "MasterGo \u57DF\u540D\uFF0C\u9ED8\u8BA4 mastergo.netease.com", "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("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (code, options) => {
2256
+ return new Command6("execute_code").description("\u5728 MasterGo \u9875\u9762\u6267\u884C\u81EA\u5B9A\u4E49 JavaScript \u4EE3\u7801\u3002\u901A\u8FC7 mg \u53D8\u91CF\u8BBF\u95EE MasterGo API\uFF0C\u7ED3\u679C\u4F1A\u88AB JSON \u5E8F\u5217\u5316\u8FD4\u56DE").argument("<code>", "\u8981\u6267\u884C\u7684\u4EE3\u7801\u3002\u53EF\u4F7F\u7528 mg \u53D8\u91CF\uFF0C\u5982 mg.currentPage.name\u3001mg.currentPage.selection").option("--link <url>", "mgp:// \u534F\u8BAE\u94FE\u63A5\uFF0C\u7528\u4E8E\u6307\u5B9A\u76EE\u6807\u9875\u9762\u3002\u4E0E --domain/--fileId \u4E8C\u9009\u4E00").option("--domain <domain>", "MasterGo \u57DF\u540D\uFF0C\u9ED8\u8BA4 mastergo.netease.com", "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("--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 (code, options) => {
1899
2257
  await handleExecuteCode(code, options);
1900
2258
  });
1901
2259
  }
@@ -1928,7 +2286,7 @@ async function handleExecuteCode(code, options) {
1928
2286
  if (result === null || result === void 0) {
1929
2287
  console.log("\u6267\u884C\u5B8C\u6210\uFF08\u65E0\u8FD4\u56DE\u503C\uFF09");
1930
2288
  } else if (typeof result === "object") {
1931
- console.log(JSON.stringify(result, null, 2));
2289
+ console.log(JSON.stringify(result, null, options.pretty ? 2 : 0));
1932
2290
  } else {
1933
2291
  console.log(result);
1934
2292
  }
@@ -1942,12 +2300,12 @@ async function handleExecuteCode(code, options) {
1942
2300
 
1943
2301
  // src/cli/commands/get-all-pages.ts
1944
2302
  import { Command as Command7 } from "commander";
1945
- import { writeFileSync as writeFileSync6 } from "fs";
2303
+ import { writeFileSync as writeFileSync7 } from "fs";
1946
2304
  import { resolve as resolve6, dirname as dirname9 } from "path";
1947
- import { mkdirSync as mkdirSync7 } from "fs";
2305
+ import { mkdirSync as mkdirSync8 } from "fs";
1948
2306
  import { tmpdir as tmpdir2 } from "os";
1949
2307
  function createGetAllPagesCommand() {
1950
- 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("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
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) => {
1951
2309
  await handleGetAllPages(options);
1952
2310
  });
1953
2311
  }
@@ -1992,15 +2350,16 @@ async function handleGetAllPages(options) {
1992
2350
  outputPath = resolve6(tmpdir2(), filename);
1993
2351
  }
1994
2352
  const outputDir = dirname9(outputPath);
1995
- mkdirSync7(outputDir, { recursive: true });
1996
- const jsonContent = JSON.stringify(data, null, 2);
1997
- writeFileSync6(outputPath, jsonContent, "utf-8");
2353
+ mkdirSync8(outputDir, { recursive: true });
2354
+ const jsonContent = JSON.stringify(data, null, options.pretty ? 2 : 0);
2355
+ writeFileSync7(outputPath, jsonContent, "utf-8");
1998
2356
  const size = jsonContent.length;
1999
2357
  const sizeKB = (size / 1024).toFixed(2);
2000
2358
  console.log(`\u6587\u4EF6\u8DEF\u5F84: ${outputPath}`);
2001
2359
  console.log(`\u6587\u6863\u540D\u79F0: ${data.documentName}`);
2002
2360
  console.log(`\u9875\u9762\u6570\u91CF: ${data.totalCount}`);
2003
2361
  console.log(`\u6570\u636E\u5927\u5C0F: ${size.toLocaleString()} \u5B57\u7B26 (\u7EA6 ${sizeKB} KB)`);
2362
+ console.log(`\u8F93\u51FA\u683C\u5F0F: ${options.pretty ? "\u683C\u5F0F\u5316" : "\u538B\u7F29"}`);
2004
2363
  } catch (error) {
2005
2364
  console.error(`\u9519\u8BEF: ${error instanceof Error ? error.message : error}`);
2006
2365
  process.exit(1);
@@ -2011,11 +2370,11 @@ async function handleGetAllPages(options) {
2011
2370
 
2012
2371
  // src/cli/commands/get-node-for-space.ts
2013
2372
  import { Command as Command8 } from "commander";
2014
- import { writeFileSync as writeFileSync7 } from "fs";
2373
+ import { writeFileSync as writeFileSync8 } from "fs";
2015
2374
  import { resolve as resolve7, dirname as dirname10 } from "path";
2016
- import { mkdirSync as mkdirSync8 } from "fs";
2375
+ import { mkdirSync as mkdirSync9 } from "fs";
2017
2376
  function createGetNodeForSpaceCommand() {
2018
- 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("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
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) => {
2019
2378
  await handleGetNodeForSpace(options);
2020
2379
  });
2021
2380
  }
@@ -2093,9 +2452,9 @@ async function handleGetNodeForSpace(options) {
2093
2452
  const spaceData = extractSpaceInfo(data);
2094
2453
  const outputPath = resolve7(options.output);
2095
2454
  const outputDir = dirname10(outputPath);
2096
- mkdirSync8(outputDir, { recursive: true });
2097
- const jsonContent = JSON.stringify(spaceData, null, 2);
2098
- writeFileSync7(outputPath, jsonContent, "utf-8");
2455
+ mkdirSync9(outputDir, { recursive: true });
2456
+ const jsonContent = JSON.stringify(spaceData, null, options.pretty ? 2 : 0);
2457
+ writeFileSync8(outputPath, jsonContent, "utf-8");
2099
2458
  const size = jsonContent.length;
2100
2459
  const sizeKB = (size / 1024).toFixed(2);
2101
2460
  console.log(`\u6587\u4EF6\u8DEF\u5F84: ${outputPath}`);
@@ -2112,6 +2471,7 @@ async function handleGetNodeForSpace(options) {
2112
2471
  }
2113
2472
  console.log(`\u6570\u636E\u5927\u5C0F: ${size.toLocaleString()} \u5B57\u7B26 (\u7EA6 ${sizeKB} KB)`);
2114
2473
  console.log(`\u8282\u70B9\u6DF1\u5EA6: ${maxDepth}`);
2474
+ console.log(`\u8F93\u51FA\u683C\u5F0F: ${options.pretty ? "\u683C\u5F0F\u5316" : "\u538B\u7F29"}`);
2115
2475
  console.log(`\u6570\u636E\u6A21\u5F0F: \u7A7A\u95F4\u4FE1\u606F (\u4EC5 id, name, x, y, width, height)`);
2116
2476
  } catch (error) {
2117
2477
  if (error instanceof MGError) {
@@ -2125,8 +2485,595 @@ async function handleGetNodeForSpace(options) {
2125
2485
  }
2126
2486
  }
2127
2487
 
2488
+ // src/cli/commands/list-extensions.ts
2489
+ import { Command as Command9 } from "commander";
2490
+ function createListExtensionsCommand() {
2491
+ return new Command9("list_extensions").description("\u5217\u51FA\u6240\u6709\u5DF2\u8FDE\u63A5\u7684 Chrome \u6269\u5C55\u5B9E\u4F8B").option("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
2492
+ await handleListExtensions(options);
2493
+ });
2494
+ }
2495
+ async function handleListExtensions(options) {
2496
+ const client = new MGClient({
2497
+ noAutoStart: options.noAutoStart,
2498
+ noRetry: options.noRetry
2499
+ });
2500
+ try {
2501
+ await client.connect();
2502
+ const data = await client.requestWithRetry("list_extensions" /* LIST_EXTENSIONS */, {});
2503
+ if (data.totalCount === 0) {
2504
+ console.log("\u5F53\u524D\u6CA1\u6709\u8FDE\u63A5\u7684\u6269\u5C55\u5B9E\u4F8B");
2505
+ console.log("\n\u8BF7\u786E\u4FDD:");
2506
+ console.log(" 1. \u6D4F\u89C8\u5668\u5DF2\u6253\u5F00");
2507
+ console.log(" 2. \u5DF2\u5B89\u88C5 MG Plugin \u6269\u5C55");
2508
+ console.log(" 3. \u6269\u5C55\u5DF2\u6210\u529F\u8FDE\u63A5\u5230 Server");
2509
+ } else {
2510
+ console.log(`\u627E\u5230 ${data.totalCount} \u4E2A\u6269\u5C55\u5B9E\u4F8B:
2511
+ `);
2512
+ for (const ext of data.extensions) {
2513
+ const connectedAt = new Date(ext.connectedAt);
2514
+ const lastActiveAt = new Date(ext.lastActiveAt);
2515
+ console.log(`\u6269\u5C55 #${ext.index}`);
2516
+ console.log(` Extension ID: ${ext.extensionId}`);
2517
+ console.log(` \u8FDE\u63A5\u65F6\u95F4: ${connectedAt.toLocaleString()}`);
2518
+ console.log(` \u6700\u540E\u6D3B\u8DC3: ${lastActiveAt.toLocaleString()}`);
2519
+ console.log("");
2520
+ }
2521
+ }
2522
+ } catch (error) {
2523
+ console.error(`\u9519\u8BEF: ${error instanceof Error ? error.message : error}`);
2524
+ process.exit(1);
2525
+ } finally {
2526
+ client.close();
2527
+ }
2528
+ }
2529
+
2530
+ // src/cli/commands/open-page.ts
2531
+ import { Command as Command10 } from "commander";
2532
+ function createOpenPageCommand() {
2533
+ return new Command10("open_page").description("\u901A\u8FC7 Chrome \u6269\u5C55\u6253\u5F00\u6307\u5B9A\u7684\u9875\u9762").requiredOption("--url <url>", "\u8981\u6253\u5F00\u7684\u9875\u9762 URL\uFF08\u5B8C\u6574 HTTPS URL\uFF09").option("--ext <index>", "\u6269\u5C55\u5B9E\u4F8B\u5E8F\u53F7\uFF081, 2, 3...\uFF09\uFF0C\u9ED8\u8BA4\u81EA\u52A8\u9009\u62E9\u7B2C\u4E00\u4E2A\u53EF\u7528\u6269\u5C55").option("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
2534
+ await handleOpenPage(options);
2535
+ });
2536
+ }
2537
+ async function handleOpenPage(options) {
2538
+ const client = new MGClient({
2539
+ noAutoStart: options.noAutoStart,
2540
+ noRetry: options.noRetry
2541
+ });
2542
+ try {
2543
+ if (!options.url.startsWith("https://") && !options.url.startsWith("http://")) {
2544
+ console.error("\u9519\u8BEF: URL \u5FC5\u987B\u4EE5 https:// \u6216 http:// \u5F00\u5934");
2545
+ console.error(`\u63D0\u4F9B\u7684 URL: ${options.url}`);
2546
+ process.exit(1);
2547
+ }
2548
+ await client.connect();
2549
+ let extensionIndex;
2550
+ if (typeof options.ext === "string") {
2551
+ extensionIndex = parseInt(options.ext, 10);
2552
+ } else if (typeof options.ext === "number") {
2553
+ extensionIndex = options.ext;
2554
+ } else {
2555
+ const extensionsData = await client.requestWithRetry("list_extensions" /* LIST_EXTENSIONS */, {});
2556
+ if (extensionsData.totalCount === 0) {
2557
+ console.error("\u9519\u8BEF: \u6CA1\u6709 Chrome \u6269\u5C55\u8FDE\u63A5\u5230 Server\u3002\u8BF7\u786E\u4FDD\u6D4F\u89C8\u5668\u5DF2\u6253\u5F00\u5E76\u5B89\u88C5\u4E86 MG Plugin");
2558
+ console.error("\n\u{1F4A1} \u89E3\u51B3\u65B9\u6CD5:");
2559
+ console.error(" 1. \u67E5\u770B\u53EF\u7528\u7684\u6269\u5C55\u5B9E\u4F8B:");
2560
+ console.error(" npx -y @hangox/mg-cli@latest list_extensions");
2561
+ console.error("");
2562
+ console.error(" 2. \u5982\u679C\u6CA1\u6709\u6269\u5C55\u8FDE\u63A5\uFF0C\u8BF7\u68C0\u67E5:");
2563
+ console.error(" \u2022 \u6D4F\u89C8\u5668\u662F\u5426\u5DF2\u6253\u5F00");
2564
+ console.error(" \u2022 MG Plugin \u6269\u5C55\u662F\u5426\u5DF2\u5B89\u88C5\u5E76\u542F\u7528");
2565
+ console.error(" \u2022 \u5C1D\u8BD5\u5237\u65B0\u6D4F\u89C8\u5668\u9875\u9762\u6216\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55");
2566
+ process.exit(1);
2567
+ }
2568
+ extensionIndex = extensionsData.extensions[0].index;
2569
+ console.log(`\u672A\u6307\u5B9A\u6269\u5C55\u5E8F\u53F7\uFF0C\u81EA\u52A8\u4F7F\u7528\u6269\u5C55 #${extensionIndex}`);
2570
+ }
2571
+ if (isNaN(extensionIndex) || extensionIndex < 1) {
2572
+ console.error("\u9519\u8BEF: \u6269\u5C55\u5E8F\u53F7\u5FC5\u987B\u662F\u5927\u4E8E 0 \u7684\u6574\u6570");
2573
+ console.error(`\u63D0\u4F9B\u7684\u5E8F\u53F7: ${options.ext}`);
2574
+ process.exit(1);
2575
+ }
2576
+ console.log(`\u6B63\u5728\u901A\u8FC7\u6269\u5C55 #${extensionIndex} \u6253\u5F00\u9875\u9762...`);
2577
+ console.log(`URL: ${options.url}`);
2578
+ const params = {
2579
+ url: options.url,
2580
+ extensionIndex
2581
+ };
2582
+ const data = await client.requestWithRetry(
2583
+ "open_page" /* OPEN_PAGE */,
2584
+ params
2585
+ );
2586
+ console.log(`
2587
+ \u2713 \u9875\u9762\u5DF2\u6253\u5F00`);
2588
+ console.log(`Tab ID: ${data.tabId}`);
2589
+ } catch (error) {
2590
+ console.error(`
2591
+ \u9519\u8BEF: ${error instanceof Error ? error.message : error}`);
2592
+ const errorMsg = error instanceof Error ? error.message : String(error);
2593
+ if (errorMsg.includes("NO_EXTENSION_CONNECTED") || errorMsg.includes("\u672A\u627E\u5230")) {
2594
+ console.error("\n\u{1F4A1} \u89E3\u51B3\u65B9\u6CD5:");
2595
+ console.error(" 1. \u67E5\u770B\u53EF\u7528\u7684\u6269\u5C55\u5B9E\u4F8B:");
2596
+ console.error(" npx -y @hangox/mg-cli@latest list_extensions");
2597
+ console.error("");
2598
+ console.error(" 2. \u5982\u679C\u6CA1\u6709\u6269\u5C55\u8FDE\u63A5\uFF0C\u8BF7\u68C0\u67E5:");
2599
+ console.error(" \u2022 \u6D4F\u89C8\u5668\u662F\u5426\u5DF2\u6253\u5F00");
2600
+ console.error(" \u2022 MG Plugin \u6269\u5C55\u662F\u5426\u5DF2\u5B89\u88C5\u5E76\u542F\u7528");
2601
+ console.error(" \u2022 \u5C1D\u8BD5\u5237\u65B0\u6D4F\u89C8\u5668\u9875\u9762\u6216\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55");
2602
+ console.error("");
2603
+ console.error(" 3. \u67E5\u770B\u6269\u5C55\u5B89\u88C5\u6307\u5357:");
2604
+ console.error(" https://github.com/hangox/MasterGoPlugin#chrome-plugin");
2605
+ }
2606
+ process.exit(1);
2607
+ } finally {
2608
+ client.close();
2609
+ }
2610
+ }
2611
+
2612
+ // src/cli/commands/navigate-to-node.ts
2613
+ import { Command as Command11 } from "commander";
2614
+ function createNavigateToNodeCommand() {
2615
+ return new Command11("navigate_to_node").description("\u5BFC\u822A\u5230 mgp:// \u94FE\u63A5\u6307\u5B9A\u7684\u8282\u70B9\uFF08\u81EA\u52A8\u5207\u6362\u6216\u6253\u5F00\u9875\u9762\uFF09").requiredOption("--link <url>", "mgp:// \u534F\u8BAE\u94FE\u63A5").option("--ext <index>", "\u6269\u5C55\u5B9E\u4F8B\u5E8F\u53F7\uFF081, 2, 3...\uFF09\uFF0C\u9ED8\u8BA4\u81EA\u52A8\u9009\u62E9\u7B2C\u4E00\u4E2A\u53EF\u7528\u6269\u5C55").option("--no-auto-start", "\u7981\u7528\u81EA\u52A8\u542F\u52A8 Server").option("--no-retry", "\u7981\u7528\u81EA\u52A8\u91CD\u8BD5").action(async (options) => {
2616
+ await handleNavigateToNode(options);
2617
+ });
2618
+ }
2619
+ async function handleNavigateToNode(options) {
2620
+ const parsed = parseMgpLink(options.link);
2621
+ if (!parsed) {
2622
+ console.error(`\u9519\u8BEF [${"E010" /* INVALID_LINK */}]: \u65E0\u6548\u7684 mgp:// \u94FE\u63A5\u683C\u5F0F`);
2623
+ console.error(`\u63D0\u4F9B\u7684\u94FE\u63A5: ${options.link}`);
2624
+ console.error(`\u671F\u671B\u683C\u5F0F: mgp://[mastergo_page_url]?nodeId=[\u8282\u70B9ID]`);
2625
+ process.exit(1);
2626
+ }
2627
+ const { pageUrl, nodeId } = parsed;
2628
+ if (!nodeId) {
2629
+ console.error(`\u9519\u8BEF [${"E010" /* INVALID_LINK */}]: \u94FE\u63A5\u5FC5\u987B\u5305\u542B nodeId \u53C2\u6570`);
2630
+ console.error(`\u63D0\u4F9B\u7684\u94FE\u63A5: ${options.link}`);
2631
+ console.error(`\u671F\u671B\u683C\u5F0F: mgp://[mastergo_page_url]?nodeId=[\u8282\u70B9ID]`);
2632
+ process.exit(1);
2633
+ }
2634
+ const client = new MGClient({
2635
+ noAutoStart: options.noAutoStart,
2636
+ noRetry: options.noRetry
2637
+ });
2638
+ try {
2639
+ await client.connect();
2640
+ let extensionIndex;
2641
+ if (typeof options.ext === "string") {
2642
+ extensionIndex = parseInt(options.ext, 10);
2643
+ } else if (typeof options.ext === "number") {
2644
+ extensionIndex = options.ext;
2645
+ } else {
2646
+ const extensionsData = await client.requestWithRetry("list_extensions" /* LIST_EXTENSIONS */, {});
2647
+ if (extensionsData.totalCount === 0) {
2648
+ console.error("\u9519\u8BEF: \u6CA1\u6709 Chrome \u6269\u5C55\u8FDE\u63A5\u5230 Server\u3002\u8BF7\u786E\u4FDD\u6D4F\u89C8\u5668\u5DF2\u6253\u5F00\u5E76\u5B89\u88C5\u4E86 MG Plugin");
2649
+ console.error("\n\u{1F4A1} \u89E3\u51B3\u65B9\u6CD5:");
2650
+ console.error(" 1. \u67E5\u770B\u53EF\u7528\u7684\u6269\u5C55\u5B9E\u4F8B:");
2651
+ console.error(" npx -y @hangox/mg-cli@latest list_extensions");
2652
+ console.error("");
2653
+ console.error(" 2. \u5982\u679C\u6CA1\u6709\u6269\u5C55\u8FDE\u63A5\uFF0C\u8BF7\u68C0\u67E5:");
2654
+ console.error(" \u2022 \u6D4F\u89C8\u5668\u662F\u5426\u5DF2\u6253\u5F00");
2655
+ console.error(" \u2022 MG Plugin \u6269\u5C55\u662F\u5426\u5DF2\u5B89\u88C5\u5E76\u542F\u7528");
2656
+ console.error(" \u2022 \u5C1D\u8BD5\u5237\u65B0\u6D4F\u89C8\u5668\u9875\u9762\u6216\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55");
2657
+ process.exit(1);
2658
+ }
2659
+ extensionIndex = extensionsData.extensions[0].index;
2660
+ console.log(`\u672A\u6307\u5B9A\u6269\u5C55\u5E8F\u53F7\uFF0C\u81EA\u52A8\u4F7F\u7528\u6269\u5C55 #${extensionIndex}`);
2661
+ }
2662
+ if (isNaN(extensionIndex) || extensionIndex < 1) {
2663
+ console.error("\u9519\u8BEF: \u6269\u5C55\u5E8F\u53F7\u5FC5\u987B\u662F\u5927\u4E8E 0 \u7684\u6574\u6570");
2664
+ console.error(`\u63D0\u4F9B\u7684\u5E8F\u53F7: ${options.ext}`);
2665
+ process.exit(1);
2666
+ }
2667
+ console.log(`\u6B63\u5728\u901A\u8FC7\u6269\u5C55 #${extensionIndex} \u5BFC\u822A\u5230\u8282\u70B9...`);
2668
+ console.log(`\u9875\u9762 URL: ${pageUrl}`);
2669
+ console.log(`\u8282\u70B9 ID: ${nodeId}`);
2670
+ const params = {
2671
+ pageUrl,
2672
+ nodeId,
2673
+ extensionIndex
2674
+ };
2675
+ const data = await client.requestWithRetry("navigate_to_node" /* NAVIGATE_TO_NODE */, params);
2676
+ console.log(`
2677
+ \u2713 \u5BFC\u822A\u6210\u529F`);
2678
+ console.log(`\u64CD\u4F5C: ${data.action === "switched" ? "\u5207\u6362\u5230\u5DF2\u6253\u5F00\u7684 Tab" : "\u6253\u5F00\u65B0 Tab"}`);
2679
+ console.log(`Tab ID: ${data.tabId}`);
2680
+ console.log(`URL: ${data.url}`);
2681
+ } catch (error) {
2682
+ if (error instanceof MGError) {
2683
+ console.error(`
2684
+ \u9519\u8BEF [${error.code}]: ${error.message}`);
2685
+ } else {
2686
+ console.error(`
2687
+ \u9519\u8BEF: ${error instanceof Error ? error.message : error}`);
2688
+ }
2689
+ const errorMsg = error instanceof Error ? error.message : String(error);
2690
+ if (errorMsg.includes("NO_EXTENSION_CONNECTED") || errorMsg.includes("\u672A\u627E\u5230")) {
2691
+ console.error("\n\u{1F4A1} \u89E3\u51B3\u65B9\u6CD5:");
2692
+ console.error(" 1. \u67E5\u770B\u53EF\u7528\u7684\u6269\u5C55\u5B9E\u4F8B:");
2693
+ console.error(" npx -y @hangox/mg-cli@latest list_extensions");
2694
+ console.error("");
2695
+ console.error(" 2. \u5982\u679C\u6CA1\u6709\u6269\u5C55\u8FDE\u63A5\uFF0C\u8BF7\u68C0\u67E5:");
2696
+ console.error(" \u2022 \u6D4F\u89C8\u5668\u662F\u5426\u5DF2\u6253\u5F00");
2697
+ console.error(" \u2022 MG Plugin \u6269\u5C55\u662F\u5426\u5DF2\u5B89\u88C5\u5E76\u542F\u7528");
2698
+ console.error(" \u2022 \u5C1D\u8BD5\u5237\u65B0\u6D4F\u89C8\u5668\u9875\u9762\u6216\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55");
2699
+ console.error("");
2700
+ console.error(" 3. \u67E5\u770B\u6269\u5C55\u5B89\u88C5\u6307\u5357:");
2701
+ console.error(" https://github.com/hangox/MasterGoPlugin#chrome-plugin");
2702
+ }
2703
+ process.exit(1);
2704
+ } finally {
2705
+ client.close();
2706
+ }
2707
+ }
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
+
2128
3075
  // src/cli/index.ts
2129
- var program = new Command9();
3076
+ var program = new Command13();
2130
3077
  program.name("mg-cli").description("MasterGo CLI \u5DE5\u5177 - \u7528\u4E8E Claude Code \u4E0E MasterGo \u901A\u4FE1").version("1.0.0");
2131
3078
  program.addCommand(createServerCommand());
2132
3079
  program.addCommand(createGetNodeByIdCommand());
@@ -2136,5 +3083,10 @@ program.addCommand(createExportImageCommand());
2136
3083
  program.addCommand(createExecuteCodeCommand());
2137
3084
  program.addCommand(createGetAllPagesCommand());
2138
3085
  program.addCommand(createGetNodeForSpaceCommand());
3086
+ program.addCommand(createListExtensionsCommand());
3087
+ program.addCommand(createOpenPageCommand());
3088
+ program.addCommand(createNavigateToNodeCommand());
3089
+ program.addCommand(createVisualizeCommand());
3090
+ checkForUpdates();
2139
3091
  program.parse();
2140
3092
  //# sourceMappingURL=cli.js.map