@hangox/mg-cli 1.1.1 → 1.1.3

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
@@ -231,17 +231,22 @@ function parseMgpLink(link) {
231
231
  if (questionMarkIndex === -1) {
232
232
  return null;
233
233
  }
234
- const pageUrl = urlPart.slice(0, questionMarkIndex);
234
+ const basePath = urlPart.slice(0, questionMarkIndex);
235
235
  const queryString = urlPart.slice(questionMarkIndex + 1);
236
236
  const params = new URLSearchParams(queryString);
237
237
  const encodedNodeId = params.get("nodeId");
238
238
  const encodedPageId = params.get("pageId");
239
+ const mgPageId = params.get("page_id");
239
240
  if (!encodedNodeId && !encodedPageId) {
240
241
  return null;
241
242
  }
242
243
  if (encodedNodeId && encodedPageId) {
243
244
  return null;
244
245
  }
246
+ let pageUrl = basePath;
247
+ if (mgPageId) {
248
+ pageUrl = `${basePath}?page_id=${mgPageId}`;
249
+ }
245
250
  if (encodedNodeId) {
246
251
  const nodeId = decodeURIComponent(encodedNodeId);
247
252
  if (!/^(\d+:\d+)(\/\d+:\d+)*$/.test(nodeId)) {
@@ -256,10 +261,19 @@ function parseMgpLink(link) {
256
261
  return null;
257
262
  }
258
263
  }
264
+ const childNodeIdsRaw = params.get("childNodeIds");
265
+ let childNodeIds;
266
+ if (childNodeIdsRaw) {
267
+ childNodeIds = childNodeIdsRaw.split(",").map((id) => decodeURIComponent(id));
268
+ if (!childNodeIds.every((id) => /^(\d+:\d+)(\/\d+:\d+)*$/.test(id))) {
269
+ return null;
270
+ }
271
+ }
259
272
  return {
260
273
  pageUrl,
261
274
  nodeId,
262
- nodePath
275
+ nodePath,
276
+ childNodeIds
263
277
  };
264
278
  }
265
279
  if (encodedPageId) {
@@ -384,6 +398,8 @@ var NODE_DEFAULTS = {
384
398
  dashCap: "NONE",
385
399
  fillStyleId: "",
386
400
  strokeStyleId: "",
401
+ strokeFillStyleId: "",
402
+ strokeWidthStyleId: "",
387
403
  // Corner 属性
388
404
  cornerSmooth: 0,
389
405
  cornerRadius: 0,
@@ -391,6 +407,7 @@ var NODE_DEFAULTS = {
391
407
  topRightRadius: 0,
392
408
  bottomLeftRadius: 0,
393
409
  bottomRightRadius: 0,
410
+ cornerRadiusStyleId: "",
394
411
  // Layout 属性
395
412
  rotation: 0,
396
413
  flexGrow: 0,
@@ -406,9 +423,13 @@ var NODE_DEFAULTS = {
406
423
  paddingRight: 0,
407
424
  paddingBottom: 0,
408
425
  paddingLeft: 0,
426
+ paddingStyleId: "",
427
+ spacingStyleId: "",
409
428
  clipsContent: false,
410
429
  itemReverseZIndex: false,
411
430
  strokesIncludedInLayout: false,
431
+ overflowDirection: "NONE",
432
+ gridStyleId: "",
412
433
  // 其他属性
413
434
  componentPropertyReferences: null
414
435
  };
@@ -419,7 +440,8 @@ var EMPTY_ARRAY_FIELDS = [
419
440
  "strokeDashes",
420
441
  "exportSettings",
421
442
  "reactions",
422
- "attachedConnectors"
443
+ "attachedConnectors",
444
+ "layoutGrids"
423
445
  ];
424
446
  function isEmptyArray(value) {
425
447
  return Array.isArray(value) && value.length === 0;
@@ -648,8 +670,8 @@ var ConnectionManager = class {
648
670
  allConnections = /* @__PURE__ */ new Map();
649
671
  /** 心跳检查定时器 */
650
672
  heartbeatTimer = null;
651
- constructor(logger) {
652
- this.logger = logger;
673
+ constructor(logger2) {
674
+ this.logger = logger2;
653
675
  }
654
676
  /**
655
677
  * 启动心跳检查
@@ -876,9 +898,9 @@ var RequestHandler = class {
876
898
  connectionManager;
877
899
  /** 待处理的请求 */
878
900
  pendingRequests = /* @__PURE__ */ new Map();
879
- constructor(connectionManager, logger) {
901
+ constructor(connectionManager, logger2) {
880
902
  this.connectionManager = connectionManager;
881
- this.logger = logger;
903
+ this.logger = logger2;
882
904
  }
883
905
  /**
884
906
  * 处理 Consumer 请求
@@ -1021,6 +1043,60 @@ var RequestHandler = class {
1021
1043
  }
1022
1044
  };
1023
1045
 
1046
+ // src/server/analytics.ts
1047
+ import { PostHog } from "posthog-node";
1048
+ var POSTHOG_API_KEY = "phc_cSZZIxZOPhWOlsw92Wz8tmKQ5z8eQYsI3m9aM2Ujia9";
1049
+ var POSTHOG_HOST = "https://us.i.posthog.com";
1050
+ var posthogClient = null;
1051
+ var logger = null;
1052
+ function isTelemetryDisabled() {
1053
+ return process.env.MG_TELEMETRY_DISABLED === "1" || process.env.MG_TELEMETRY_DISABLED === "true" || process.env.DO_NOT_TRACK === "1" || process.env.DO_NOT_TRACK === "true";
1054
+ }
1055
+ function initServerAnalytics(serverLogger) {
1056
+ logger = serverLogger;
1057
+ if (isTelemetryDisabled()) {
1058
+ logger.info("\u9065\u6D4B\u5DF2\u7981\u7528 (MG_TELEMETRY_DISABLED \u6216 DO_NOT_TRACK)");
1059
+ return;
1060
+ }
1061
+ if (!posthogClient) {
1062
+ posthogClient = new PostHog(POSTHOG_API_KEY, {
1063
+ host: POSTHOG_HOST,
1064
+ // Server 端使用批量模式,减少网络请求
1065
+ flushAt: 10,
1066
+ flushInterval: 3e4
1067
+ // 30 秒
1068
+ });
1069
+ logger.info("PostHog Analytics \u5DF2\u521D\u59CB\u5316");
1070
+ }
1071
+ }
1072
+ function trackEvent(eventData) {
1073
+ if (!posthogClient) {
1074
+ return;
1075
+ }
1076
+ try {
1077
+ posthogClient.capture({
1078
+ distinctId: eventData.distinctId,
1079
+ event: eventData.event,
1080
+ properties: eventData.properties
1081
+ });
1082
+ logger?.info(`\u7EDF\u8BA1\u4E8B\u4EF6\u5DF2\u8BB0\u5F55: ${eventData.event}`, eventData.properties);
1083
+ } catch (error) {
1084
+ logger?.error("\u7EDF\u8BA1\u4E8B\u4EF6\u8BB0\u5F55\u5931\u8D25:", error);
1085
+ }
1086
+ }
1087
+ async function shutdownServerAnalytics() {
1088
+ if (posthogClient) {
1089
+ try {
1090
+ logger?.info("\u6B63\u5728\u53D1\u9001\u5269\u4F59\u7EDF\u8BA1\u4E8B\u4EF6...");
1091
+ await posthogClient.shutdown();
1092
+ logger?.info("PostHog Analytics \u5DF2\u5173\u95ED");
1093
+ } catch (error) {
1094
+ logger?.error("\u5173\u95ED PostHog \u5931\u8D25:", error);
1095
+ }
1096
+ posthogClient = null;
1097
+ }
1098
+ }
1099
+
1024
1100
  // src/server/websocket-server.ts
1025
1101
  var MGServer = class {
1026
1102
  wss = null;
@@ -1052,6 +1128,7 @@ var MGServer = class {
1052
1128
  this.startedAt = /* @__PURE__ */ new Date();
1053
1129
  this.logger.info(`Server \u542F\u52A8\u6210\u529F\uFF0C\u76D1\u542C\u7AEF\u53E3: ${port}`);
1054
1130
  this.connectionManager.startHeartbeatCheck(HEARTBEAT_INTERVAL);
1131
+ initServerAnalytics(this.logger);
1055
1132
  resolve9(port);
1056
1133
  });
1057
1134
  this.wss.on("error", (error) => {
@@ -1111,6 +1188,10 @@ var MGServer = class {
1111
1188
  this.handleRegister(ws, message);
1112
1189
  return;
1113
1190
  }
1191
+ if (message.type === "track_analytics" /* TRACK_ANALYTICS */) {
1192
+ this.handleTrackAnalytics(message);
1193
+ return;
1194
+ }
1114
1195
  const managedWs = ws;
1115
1196
  if (!managedWs.connectionId) {
1116
1197
  this.logger.warn("\u672A\u6CE8\u518C\u7684\u8FDE\u63A5\u53D1\u9001\u6D88\u606F\uFF0C\u5FFD\u7565");
@@ -1188,6 +1269,9 @@ var MGServer = class {
1188
1269
  case "navigate_to_node" /* NAVIGATE_TO_NODE */:
1189
1270
  this.handleNavigateToNode(ws, message);
1190
1271
  break;
1272
+ case "track_analytics" /* TRACK_ANALYTICS */:
1273
+ this.handleTrackAnalytics(message);
1274
+ break;
1191
1275
  case "response" /* RESPONSE */:
1192
1276
  case "error" /* ERROR */:
1193
1277
  this.requestHandler.handleResponse(message);
@@ -1333,6 +1417,15 @@ var MGServer = class {
1333
1417
  }
1334
1418
  });
1335
1419
  }
1420
+ /**
1421
+ * 处理统计事件
1422
+ */
1423
+ handleTrackAnalytics(message) {
1424
+ const eventData = message.data;
1425
+ if (eventData) {
1426
+ trackEvent(eventData);
1427
+ }
1428
+ }
1336
1429
  /**
1337
1430
  * 处理节点导航请求
1338
1431
  */
@@ -1406,6 +1499,7 @@ var MGServer = class {
1406
1499
  return;
1407
1500
  }
1408
1501
  this.logger.info("\u6B63\u5728\u505C\u6B62 Server...");
1502
+ await shutdownServerAnalytics();
1409
1503
  this.requestHandler.cleanupAll();
1410
1504
  this.connectionManager.closeAll();
1411
1505
  return new Promise((resolve9) => {
@@ -1466,14 +1560,14 @@ async function startServerForeground(port) {
1466
1560
  );
1467
1561
  }
1468
1562
  ensureConfigDir();
1469
- const logger = createLogger({
1563
+ const logger2 = createLogger({
1470
1564
  console: true,
1471
1565
  file: true,
1472
1566
  minLevel: "INFO" /* INFO */
1473
1567
  });
1474
1568
  const server = createServer({
1475
1569
  port: port || DEFAULT_PORT,
1476
- logger
1570
+ logger: logger2
1477
1571
  });
1478
1572
  const cleanup = async () => {
1479
1573
  console.log("\n\u6B63\u5728\u505C\u6B62 Server...");
@@ -1499,7 +1593,7 @@ MG Server \u542F\u52A8\u6210\u529F`);
1499
1593
  console.log(`
1500
1594
  \u6309 Ctrl+C \u505C\u6B62...`);
1501
1595
  } catch (error) {
1502
- logger.error("Server \u542F\u52A8\u5931\u8D25:", error);
1596
+ logger2.error("Server \u542F\u52A8\u5931\u8D25:", error);
1503
1597
  throw error;
1504
1598
  }
1505
1599
  }
@@ -1758,10 +1852,106 @@ var MGClient = class {
1758
1852
  }
1759
1853
  };
1760
1854
 
1855
+ // src/cli/analytics.ts
1856
+ import { WebSocket as WebSocket3 } from "ws";
1857
+ import { createHash } from "crypto";
1858
+ import { hostname, userInfo, platform, release } from "os";
1859
+ var machineId = null;
1860
+ function isTelemetryDisabled2() {
1861
+ return process.env.MG_TELEMETRY_DISABLED === "1" || process.env.MG_TELEMETRY_DISABLED === "true" || process.env.DO_NOT_TRACK === "1" || process.env.DO_NOT_TRACK === "true";
1862
+ }
1863
+ function getMachineId() {
1864
+ if (machineId) {
1865
+ return machineId;
1866
+ }
1867
+ try {
1868
+ const hostName = hostname();
1869
+ const userName = userInfo().username;
1870
+ const raw = `${hostName}-${userName}-mg-cli`;
1871
+ machineId = createHash("sha256").update(raw).digest("hex").substring(0, 16);
1872
+ } catch {
1873
+ machineId = createHash("sha256").update(Math.random().toString()).digest("hex").substring(0, 16);
1874
+ }
1875
+ return machineId;
1876
+ }
1877
+ function sendToServer(eventData) {
1878
+ try {
1879
+ const serverInfo = readServerInfo();
1880
+ if (!serverInfo) {
1881
+ return;
1882
+ }
1883
+ if (!isProcessRunning(serverInfo.pid)) {
1884
+ return;
1885
+ }
1886
+ const ws = new WebSocket3(`ws://localhost:${serverInfo.port}`);
1887
+ const timeout = setTimeout(() => {
1888
+ ws.close();
1889
+ }, 1e3);
1890
+ ws.on("open", () => {
1891
+ const message = {
1892
+ type: "track_analytics" /* TRACK_ANALYTICS */,
1893
+ data: eventData
1894
+ };
1895
+ ws.send(JSON.stringify(message));
1896
+ clearTimeout(timeout);
1897
+ ws.close();
1898
+ });
1899
+ ws.on("error", () => {
1900
+ clearTimeout(timeout);
1901
+ });
1902
+ } catch {
1903
+ }
1904
+ }
1905
+ function trackCommand(properties) {
1906
+ if (isTelemetryDisabled2()) {
1907
+ return;
1908
+ }
1909
+ const eventData = {
1910
+ distinctId: getMachineId(),
1911
+ event: "cli_command",
1912
+ properties: {
1913
+ ...properties,
1914
+ version: getVersion(),
1915
+ os: platform(),
1916
+ os_release: release(),
1917
+ node_version: process.version
1918
+ }
1919
+ };
1920
+ sendToServer(eventData);
1921
+ }
1922
+ function createCommandTracker(commandName) {
1923
+ const startTime = Date.now();
1924
+ return {
1925
+ /**
1926
+ * 记录命令成功
1927
+ */
1928
+ success() {
1929
+ trackCommand({
1930
+ command: commandName,
1931
+ success: true,
1932
+ duration_ms: Date.now() - startTime
1933
+ });
1934
+ },
1935
+ /**
1936
+ * 记录命令失败
1937
+ */
1938
+ failure(error) {
1939
+ trackCommand({
1940
+ command: commandName,
1941
+ success: false,
1942
+ duration_ms: Date.now() - startTime,
1943
+ error_code: error && "code" in error ? String(error.code) : void 0,
1944
+ error_message: error?.message
1945
+ });
1946
+ }
1947
+ };
1948
+ }
1949
+
1761
1950
  // src/cli/commands/server.ts
1762
1951
  function createServerCommand() {
1763
1952
  const serverCmd = new Command("server").description("Server \u7BA1\u7406\u547D\u4EE4");
1764
1953
  serverCmd.command("start").description("\u542F\u52A8 MG Server").option("--port <number>", "\u6307\u5B9A\u542F\u52A8\u7AEF\u53E3", (value) => parseInt(value, 10)).option("--foreground", "\u524D\u53F0\u6A21\u5F0F\u8FD0\u884C\uFF08\u4E0D\u4F5C\u4E3A\u5B88\u62A4\u8FDB\u7A0B\uFF09", false).action(async (options) => {
1954
+ const tracker = createCommandTracker("server_start");
1765
1955
  try {
1766
1956
  if (options.foreground) {
1767
1957
  await startServerForeground(options.port);
@@ -1773,12 +1963,15 @@ function createServerCommand() {
1773
1963
  console.log(`\u8FDB\u7A0B PID: ${info.pid}`);
1774
1964
  console.log(`\u8FD0\u884C\u6A21\u5F0F: \u5B88\u62A4\u8FDB\u7A0B`);
1775
1965
  }
1966
+ tracker.success();
1776
1967
  } catch (error) {
1777
1968
  console.error(`\u9519\u8BEF: ${error.message}`);
1969
+ tracker.failure(error);
1778
1970
  process.exit(1);
1779
1971
  }
1780
1972
  });
1781
1973
  serverCmd.command("stop").description("\u505C\u6B62 MG Server").action(() => {
1974
+ const tracker = createCommandTracker("server_stop");
1782
1975
  try {
1783
1976
  const { stopped, info } = stopServer();
1784
1977
  if (stopped && info) {
@@ -1800,12 +1993,15 @@ function createServerCommand() {
1800
1993
  } else {
1801
1994
  console.log("MG Server \u672A\u8FD0\u884C");
1802
1995
  }
1996
+ tracker.success();
1803
1997
  } catch (error) {
1804
1998
  console.error(`\u9519\u8BEF: ${error.message}`);
1999
+ tracker.failure(error);
1805
2000
  process.exit(1);
1806
2001
  }
1807
2002
  });
1808
2003
  serverCmd.command("restart").description("\u91CD\u542F MG Server").option("--port <number>", "\u91CD\u542F\u540E\u4F7F\u7528\u7684\u7AEF\u53E3", (value) => parseInt(value, 10)).action(async (options) => {
2004
+ const tracker = createCommandTracker("server_restart");
1809
2005
  try {
1810
2006
  const status = getServerStatus();
1811
2007
  if (status.running) {
@@ -1816,17 +2012,21 @@ function createServerCommand() {
1816
2012
  console.log(`\u7248\u672C: ${info.version}`);
1817
2013
  console.log(`\u76D1\u542C\u7AEF\u53E3: ${info.port}`);
1818
2014
  console.log(`\u65B0\u8FDB\u7A0B PID: ${info.pid}`);
2015
+ tracker.success();
1819
2016
  } catch (error) {
1820
2017
  console.error(`\u9519\u8BEF: ${error.message}`);
2018
+ tracker.failure(error);
1821
2019
  process.exit(1);
1822
2020
  }
1823
2021
  });
1824
2022
  serverCmd.command("status").description("\u67E5\u770B Server \u8FD0\u884C\u72B6\u6001").action(async () => {
2023
+ const tracker = createCommandTracker("server_status");
1825
2024
  try {
1826
2025
  const basicStatus = getServerStatus();
1827
2026
  if (!basicStatus.running) {
1828
2027
  console.log("MG Server \u72B6\u6001: \u672A\u8FD0\u884C \u2717");
1829
2028
  console.log("\u63D0\u793A: \u4F7F\u7528 'mg-cli server start' \u542F\u52A8 Server");
2029
+ tracker.success();
1830
2030
  return;
1831
2031
  }
1832
2032
  try {
@@ -1858,6 +2058,7 @@ function createServerCommand() {
1858
2058
  console.log(`\u63D0\u793A: \u8BF7\u5B89\u88C5 MG Plugin \u6D4F\u89C8\u5668\u6269\u5C55\u5E76\u5728 Chrome \u4E2D\u6253\u5F00 MasterGo \u9875\u9762`);
1859
2059
  console.log(` \u63D2\u4EF6\u5730\u5740: https://chromewebstore.google.com/detail/mg-plugin/ddhihanlpcdneicohnglnaliefnkaeja`);
1860
2060
  }
2061
+ tracker.success();
1861
2062
  } catch {
1862
2063
  console.log("MG Server \u72B6\u6001: \u8FD0\u884C\u4E2D \u2713");
1863
2064
  if (basicStatus.version) {
@@ -1869,9 +2070,11 @@ function createServerCommand() {
1869
2070
  console.log(`\u8FD0\u884C\u65F6\u957F: ${basicStatus.uptime}`);
1870
2071
  console.log(``);
1871
2072
  console.log(`\u6CE8\u610F: \u65E0\u6CD5\u83B7\u53D6\u8BE6\u7EC6\u8FDE\u63A5\u4FE1\u606F`);
2073
+ tracker.success();
1872
2074
  }
1873
2075
  } catch (error) {
1874
2076
  console.error(`\u9519\u8BEF: ${error.message}`);
2077
+ tracker.failure(error);
1875
2078
  process.exit(1);
1876
2079
  }
1877
2080
  });
@@ -1889,6 +2092,7 @@ function createGetNodeByIdCommand() {
1889
2092
  });
1890
2093
  }
1891
2094
  async function handleGetNodeById(options) {
2095
+ const tracker = createCommandTracker("get_node_by_id");
1892
2096
  const client = new MGClient({
1893
2097
  noAutoStart: options.noAutoStart,
1894
2098
  noRetry: options.noRetry
@@ -1922,8 +2126,10 @@ async function handleGetNodeById(options) {
1922
2126
  if (!options.raw) {
1923
2127
  console.log(`\u6570\u636E\u6A21\u5F0F: \u7CBE\u7B80\u6A21\u5F0F (\u4F7F\u7528 --raw \u83B7\u53D6\u5B8C\u6574\u6570\u636E)`);
1924
2128
  }
2129
+ tracker.success();
1925
2130
  } catch (error) {
1926
2131
  console.error(`\u9519\u8BEF: ${error instanceof Error ? error.message : error}`);
2132
+ tracker.failure(error instanceof Error ? error : void 0);
1927
2133
  process.exit(1);
1928
2134
  } finally {
1929
2135
  client.close();
@@ -1941,6 +2147,7 @@ function createGetNodeByLinkCommand() {
1941
2147
  });
1942
2148
  }
1943
2149
  async function handleGetNodeByLink(options) {
2150
+ const tracker = createCommandTracker("get_node_by_link");
1944
2151
  const parsed = parseMgpLink(options.link);
1945
2152
  if (!parsed) {
1946
2153
  console.error(`\u9519\u8BEF [${"E010" /* INVALID_LINK */}]: \u65E0\u6548\u7684 mgp:// \u94FE\u63A5\u683C\u5F0F`);
@@ -1948,10 +2155,12 @@ async function handleGetNodeByLink(options) {
1948
2155
  console.error(`\u671F\u671B\u683C\u5F0F:`);
1949
2156
  console.error(` \u8282\u70B9\u94FE\u63A5: mgp://[mastergo_page_url]?nodeId=[\u8282\u70B9ID]`);
1950
2157
  console.error(` \u9875\u9762\u94FE\u63A5: mgp://[mastergo_page_url]?pageId=[\u9875\u9762ID]`);
2158
+ tracker.failure({ code: "E010" /* INVALID_LINK */, message: "\u65E0\u6548\u7684 mgp:// \u94FE\u63A5\u683C\u5F0F" });
1951
2159
  process.exit(1);
1952
2160
  }
1953
- const { pageUrl, nodeId, pageId } = parsed;
2161
+ const { pageUrl, nodeId, pageId, childNodeIds } = parsed;
1954
2162
  const isPageLink = !!pageId;
2163
+ const isMultiSelect = childNodeIds && childNodeIds.length > 0;
1955
2164
  const client = new MGClient({
1956
2165
  noAutoStart: options.noAutoStart,
1957
2166
  noRetry: options.noRetry
@@ -1974,7 +2183,9 @@ async function handleGetNodeByLink(options) {
1974
2183
  const params = {
1975
2184
  nodeId,
1976
2185
  maxDepth: parseInt(options.maxDepth || "1", 10),
1977
- includeInvisible: options.includeInvisible || false
2186
+ includeInvisible: options.includeInvisible || false,
2187
+ // 多选模式:传递 childNodeIds 进行过滤
2188
+ ...isMultiSelect && { childNodeIds }
1978
2189
  };
1979
2190
  data = await client.requestWithRetry(
1980
2191
  "get_node_by_id" /* GET_NODE_BY_ID */,
@@ -1998,7 +2209,12 @@ async function handleGetNodeByLink(options) {
1998
2209
  console.log(`\u7C7B\u578B: \u9875\u9762`);
1999
2210
  } else {
2000
2211
  console.log(`\u8282\u70B9 ID: ${nodeId}`);
2001
- console.log(`\u7C7B\u578B: \u8282\u70B9`);
2212
+ if (isMultiSelect) {
2213
+ console.log(`\u7C7B\u578B: \u591A\u9009\u8282\u70B9 (\u516C\u5171\u7236\u5143\u7D20)`);
2214
+ console.log(`\u9009\u4E2D\u5B50\u8282\u70B9: ${childNodeIds.length} \u4E2A`);
2215
+ } else {
2216
+ console.log(`\u7C7B\u578B: \u8282\u70B9`);
2217
+ }
2002
2218
  }
2003
2219
  console.log(`\u6570\u636E\u5927\u5C0F: ${size.toLocaleString()} \u5B57\u7B26 (\u7EA6 ${sizeKB} KB)`);
2004
2220
  console.log(`\u904D\u5386\u6DF1\u5EA6: ${options.maxDepth || "1"}`);
@@ -2006,11 +2222,14 @@ async function handleGetNodeByLink(options) {
2006
2222
  if (!options.raw) {
2007
2223
  console.log(`\u6570\u636E\u6A21\u5F0F: \u7CBE\u7B80\u6A21\u5F0F (\u4F7F\u7528 --raw \u83B7\u53D6\u5B8C\u6574\u6570\u636E)`);
2008
2224
  }
2225
+ tracker.success();
2009
2226
  } catch (error) {
2010
2227
  if (error instanceof MGError) {
2011
2228
  console.error(`\u9519\u8BEF [${error.code}]: ${error.message}`);
2229
+ tracker.failure(error);
2012
2230
  } else {
2013
2231
  console.error(`\u9519\u8BEF: ${error instanceof Error ? error.message : error}`);
2232
+ tracker.failure(error instanceof Error ? error : void 0);
2014
2233
  }
2015
2234
  process.exit(1);
2016
2235
  } finally {
@@ -2390,6 +2609,7 @@ async function handleGetNodeForSpace(options) {
2390
2609
  let pageUrl;
2391
2610
  let nodeId;
2392
2611
  let pageId;
2612
+ let childNodeIds;
2393
2613
  let isPageMode = false;
2394
2614
  if (options.link) {
2395
2615
  const parsed = parseMgpLink(options.link);
@@ -2400,6 +2620,7 @@ async function handleGetNodeForSpace(options) {
2400
2620
  process.exit(1);
2401
2621
  }
2402
2622
  pageUrl = parsed.pageUrl;
2623
+ childNodeIds = parsed.childNodeIds;
2403
2624
  if (parsed.pageId) {
2404
2625
  isPageMode = true;
2405
2626
  pageId = parsed.pageId;
@@ -2438,10 +2659,13 @@ async function handleGetNodeForSpace(options) {
2438
2659
  pageUrl
2439
2660
  );
2440
2661
  } else {
2662
+ const isMultiSelect = childNodeIds && childNodeIds.length > 0;
2441
2663
  const params = {
2442
2664
  nodeId,
2443
2665
  maxDepth,
2444
- includeInvisible
2666
+ includeInvisible,
2667
+ // 多选模式:传递 childNodeIds 进行过滤
2668
+ ...isMultiSelect && { childNodeIds }
2445
2669
  };
2446
2670
  data = await client.requestWithRetry(
2447
2671
  "get_node_by_id" /* GET_NODE_BY_ID */,
@@ -3014,8 +3238,9 @@ async function handleVisualize(options) {
3014
3238
  console.error(` \u9875\u9762\u94FE\u63A5: mgp://[mastergo_page_url]?pageId=[\u9875\u9762ID]`);
3015
3239
  process.exit(1);
3016
3240
  }
3017
- const { pageUrl, nodeId, pageId } = parsed;
3241
+ const { pageUrl, nodeId, pageId, childNodeIds } = parsed;
3018
3242
  const isPageLink = !!pageId;
3243
+ const isMultiSelect = childNodeIds && childNodeIds.length > 0;
3019
3244
  const client = new MGClient({
3020
3245
  noAutoStart: options.noAutoStart,
3021
3246
  noRetry: options.noRetry
@@ -3034,7 +3259,9 @@ async function handleVisualize(options) {
3034
3259
  const params = {
3035
3260
  nodeId,
3036
3261
  maxDepth: parseInt(options.maxDepth || "3", 10),
3037
- includeInvisible: false
3262
+ includeInvisible: false,
3263
+ // 多选模式:传递 childNodeIds 进行过滤
3264
+ ...isMultiSelect && { childNodeIds }
3038
3265
  };
3039
3266
  data = await client.requestWithRetry("get_node_by_id" /* GET_NODE_BY_ID */, params, pageUrl);
3040
3267
  }