@hangox/mg-cli 1.0.5 → 1.0.7

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.
@@ -3,12 +3,26 @@ import { WebSocket } from 'ws';
3
3
  /**
4
4
  * MG Plugin 常量定义
5
5
  */
6
- /** 默认端口 */
7
- declare const DEFAULT_PORT = 9527;
8
- /** 端口范围:起始 */
9
- declare const PORT_RANGE_START = 9527;
10
- /** 端口范围:结束 */
11
- declare const PORT_RANGE_END = 9536;
6
+ /** 是否为开发模式(通过环境变量 MG_DEV_MODE=true 设置) */
7
+ declare const IS_DEV_MODE: boolean;
8
+ /** 生产环境默认端口 */
9
+ declare const PROD_DEFAULT_PORT = 9527;
10
+ /** 生产环境端口范围:起始 */
11
+ declare const PROD_PORT_RANGE_START = 9527;
12
+ /** 生产环境端口范围:结束 */
13
+ declare const PROD_PORT_RANGE_END = 9536;
14
+ /** 开发环境默认端口 */
15
+ declare const DEV_DEFAULT_PORT = 19527;
16
+ /** 开发环境端口范围:起始 */
17
+ declare const DEV_PORT_RANGE_START = 19527;
18
+ /** 开发环境端口范围:结束 */
19
+ declare const DEV_PORT_RANGE_END = 19536;
20
+ /** 默认端口(根据运行模式自动选择) */
21
+ declare const DEFAULT_PORT: number;
22
+ /** 端口范围:起始(根据运行模式自动选择) */
23
+ declare const PORT_RANGE_START: number;
24
+ /** 端口范围:结束(根据运行模式自动选择) */
25
+ declare const PORT_RANGE_END: number;
12
26
  /** 最大尝试端口数 */
13
27
  declare const MAX_PORT_ATTEMPTS = 10;
14
28
  /** 端口扫描超时(毫秒) */
@@ -47,6 +61,7 @@ declare enum MessageType {
47
61
  REGISTER = "register",
48
62
  REGISTER_ACK = "register_ack",
49
63
  GET_NODE_BY_ID = "get_node_by_id",
64
+ GET_PAGE_BY_ID = "get_page_by_id",
50
65
  GET_ALL_NODES = "get_all_nodes",
51
66
  GET_SELECTION = "get_selection",
52
67
  EXPORT_IMAGE = "export_image",
@@ -216,7 +231,139 @@ interface ConnectionInfo {
216
231
  /** 最后活跃时间 */
217
232
  lastActiveAt: Date;
218
233
  }
219
- /** 节点信息(简化版) */
234
+ /**
235
+ * 节点信息
236
+ *
237
+ * 包含 MasterGo 节点的所有属性,根据节点类型不同,可用属性也不同。
238
+ *
239
+ * ## 通用属性 (所有节点)
240
+ * - `id`: 节点唯一标识
241
+ * - `name`: 节点名称
242
+ * - `type`: 节点类型 (FRAME, TEXT, RECTANGLE, ELLIPSE, POLYGON, STAR, LINE, PEN, COMPONENT, COMPONENT_SET, INSTANCE, GROUP, BOOLEAN_OPERATION, SLICE, CONNECTOR, SECTION)
243
+ * - `visible`: 是否可见
244
+ * - `isLocked`: 是否锁定
245
+ * - `slotInfo`: 插槽信息
246
+ * - `componentPropertyReferences`: 组件属性引用
247
+ * - `attachedConnectorIds`: 连接的连接线 ID 数组
248
+ *
249
+ * ## 布局属性 (LayoutMixin)
250
+ * - `x`, `y`: 坐标
251
+ * - `width`, `height`: 尺寸
252
+ * - `rotation`: 旋转角度
253
+ * - `absoluteTransform`: 绝对变换矩阵
254
+ * - `relativeTransform`: 相对变换矩阵
255
+ * - `absoluteRenderBounds`: 渲染边界(含阴影/外描边)
256
+ * - `absoluteBoundingBox`: 绝对边界框
257
+ * - `bound`: 边界
258
+ * - `minWidth`, `maxWidth`, `minHeight`, `maxHeight`: 尺寸约束
259
+ * - `constrainProportions`: 锁定宽高比
260
+ * - `layoutPositioning`: 布局定位方式 (AUTO | ABSOLUTE)
261
+ * - `alignSelf`: 交叉轴对齐 (STRETCH | INHERIT)
262
+ * - `flexGrow`: 弹性增长 (0 | 1)
263
+ * - `scaleFactor`: 缩放因子
264
+ *
265
+ * ## 混合属性 (BlendMixin)
266
+ * - `opacity`: 不透明度
267
+ * - `blendMode`: 混合模式
268
+ * - `isMask`: 是否蒙版
269
+ * - `isMaskOutline`: 轮廓蒙版
270
+ * - `isMaskVisible`: 蒙版可见
271
+ * - `effects`: 效果数组(阴影、模糊等)
272
+ * - `effectStyleId`: 效果样式 ID
273
+ *
274
+ * ## 几何属性 (GeometryMixin)
275
+ * - `fills`: 填充数组
276
+ * - `strokes`: 描边数组
277
+ * - `strokeWeight`: 描边粗细
278
+ * - `strokeAlign`: 描边对齐 (CENTER | INSIDE | OUTSIDE)
279
+ * - `strokeCap`: 端点装饰
280
+ * - `strokeJoin`: 拐角装饰
281
+ * - `strokeStyle`: 描边样式 (SOLID | DASH | CUSTOM)
282
+ * - `dashCap`: 虚线端点
283
+ * - `strokeDashes`: 虚线数组
284
+ * - `fillStyleId`: 填充样式 ID
285
+ * - `strokeFillStyleId`: 描边填充样式 ID
286
+ * - `strokeWidthStyleId`: 描边宽度样式 ID
287
+ * - `paddingStyleId`: 内边距样式 ID
288
+ * - `spacingStyleId`: 间距样式 ID
289
+ * - `cornerRadiusStyleId`: 圆角样式 ID
290
+ *
291
+ * ## 矩形描边属性 (RectangleStrokeWeightMixin)
292
+ * - `strokeTopWeight`, `strokeLeftWeight`, `strokeBottomWeight`, `strokeRightWeight`: 各边描边粗细
293
+ *
294
+ * ## 圆角属性 (CornerMixin)
295
+ * - `cornerSmooth`: 角平滑度
296
+ * - `cornerRadius`: 统一圆角
297
+ * - `topLeftRadius`, `topRightRadius`, `bottomLeftRadius`, `bottomRightRadius`: 各角圆角
298
+ *
299
+ * ## 约束属性 (ConstraintMixin)
300
+ * - `constraints`: 约束设置 { horizontal, vertical }
301
+ *
302
+ * ## 容器属性 (ContainerMixin)
303
+ * - `expanded`: 是否展开
304
+ *
305
+ * ## 自动布局属性 (FrameContainerMixin)
306
+ * - `flexMode`: 自动布局方向 (NONE | HORIZONTAL | VERTICAL)
307
+ * - `flexWrap`: 换行设置 (WRAP | NO_WRAP)
308
+ * - `itemSpacing`: 主轴间距
309
+ * - `crossAxisSpacing`: 交叉轴间距
310
+ * - `mainAxisAlignItems`: 主轴对齐
311
+ * - `crossAxisAlignItems`: 交叉轴对齐
312
+ * - `mainAxisSizingMode`: 主轴尺寸模式 (FIXED | AUTO)
313
+ * - `crossAxisSizingMode`: 交叉轴尺寸模式 (FIXED | AUTO)
314
+ * - `crossAxisAlignContent`: 多行对齐
315
+ * - `strokesIncludedInLayout`: 描边计入布局
316
+ * - `itemReverseZIndex`: 反转堆叠
317
+ * - `paddingTop`, `paddingRight`, `paddingBottom`, `paddingLeft`: 内边距
318
+ * - `clipsContent`: 裁剪内容
319
+ * - `layoutGrids`: 布局网格
320
+ * - `gridStyleId`: 网格样式 ID
321
+ * - `overflowDirection`: 溢出方向
322
+ *
323
+ * ## 原型交互属性 (ReactionMixin)
324
+ * - `reactions`: 原型交互数组
325
+ *
326
+ * ## 导出属性 (ExportMixin)
327
+ * - `exportSettings`: 导出设置数组
328
+ *
329
+ * ## TEXT 节点特有属性
330
+ * - `characters`: 文本内容
331
+ * - `hasMissingFont`: 缺失字体
332
+ * - `hyperlinks`: 超链接数组
333
+ * - `textAlignHorizontal`: 水平对齐
334
+ * - `textAlignVertical`: 垂直对齐
335
+ * - `textAutoResize`: 自动调整
336
+ * - `paragraphSpacing`: 段落间距
337
+ * - `textStyles`: 文本样式数组
338
+ * - `listStyles`: 列表样式数组
339
+ *
340
+ * ## COMPONENT/COMPONENT_SET 节点特有属性
341
+ * - `variantProperties`: 变体属性
342
+ * - `componentPropertyValues`: 组件属性值
343
+ * - `description`: 描述
344
+ * - `alias`: 别名
345
+ * - `documentationLinks`: 文档链接
346
+ * - `isExternal`: 是否团队库组件
347
+ * - `ukey`: 唯一键
348
+ * - `publishStatus`: 发布状态
349
+ *
350
+ * ## INSTANCE 节点特有属性
351
+ * - `componentProperties`: 组件属性
352
+ * - `exposedInstances`: 暴露的实例 ID 数组
353
+ * - `isExposedInstance`: 是否暴露的实例
354
+ * - `mainComponentId`: 主组件 ID
355
+ * - `mainComponentName`: 主组件名称
356
+ *
357
+ * ## 其他节点特有属性
358
+ * - ELLIPSE: `arcData`
359
+ * - POLYGON/STAR: `pointCount`
360
+ * - STAR: `innerRadius`
361
+ * - LINE: `leftStrokeCap`, `rightStrokeCap`
362
+ * - PEN: `penNetwork`, `penPaths`
363
+ * - BOOLEAN_OPERATION: `booleanOperation`
364
+ * - CONNECTOR: `connectorStart`, `connectorEnd`, `connectorStartStrokeCap`, `connectorEndStrokeCap`, `connectorText`
365
+ * - SLICE: `isPreserveRatio`
366
+ */
220
367
  interface NodeInfo {
221
368
  /** 节点 ID */
222
369
  id: string;
@@ -236,7 +383,7 @@ interface NodeInfo {
236
383
  height?: number;
237
384
  /** 子节点 */
238
385
  children?: NodeInfo[];
239
- /** 其他属性 */
386
+ /** 其他属性(根据节点类型不同而不同) */
240
387
  [key: string]: unknown;
241
388
  }
242
389
  /** 空间节点信息(仅包含位置和尺寸,用于 AI 理解空间布局) */
@@ -267,6 +414,17 @@ interface GetNodeParams {
267
414
  /** 索引签名 */
268
415
  [key: string]: unknown;
269
416
  }
417
+ /** 获取页面参数 */
418
+ interface GetPageParams {
419
+ /** 页面 ID */
420
+ pageId: string;
421
+ /** 遍历深度 */
422
+ maxDepth?: number;
423
+ /** 是否包含不可见节点 */
424
+ includeInvisible?: boolean;
425
+ /** 索引签名 */
426
+ [key: string]: unknown;
427
+ }
270
428
  /** 获取所有节点参数 */
271
429
  interface GetAllNodesParams {
272
430
  /** 遍历深度 */
@@ -518,7 +676,7 @@ interface ManagedWebSocket extends WebSocket {
518
676
  */
519
677
  declare class ConnectionManager {
520
678
  private logger;
521
- /** Provider 连接(按页面 URL 索引) */
679
+ /** Provider 连接(按页面 URL 索引,支持同 URL 多个连接) */
522
680
  private providers;
523
681
  /** Consumer 连接 */
524
682
  private consumers;
@@ -552,7 +710,7 @@ declare class ConnectionManager {
552
710
  */
553
711
  updateLastActive(ws: ManagedWebSocket): void;
554
712
  /**
555
- * 根据页面 URL 查找 Provider
713
+ * 根据页面 URL 查找 Provider(返回第一个可用的连接)
556
714
  */
557
715
  findProviderByPageUrl(pageUrl: string): ManagedWebSocket | undefined;
558
716
  /**
@@ -625,4 +783,4 @@ declare class RequestHandler {
625
783
  cleanupAll(): void;
626
784
  }
627
785
 
628
- export { type MgPageInfo as A, type BaseMessage as B, CONFIG_DIR as C, DEFAULT_PORT as D, ErrorCode as E, type AllPagesInfo as F, type GetNodeParams as G, HEARTBEAT_INTERVAL as H, type ConnectedPageInfo as I, type ServerStatusResponse as J, MGServer as K, LOG_DIR as L, MAX_PORT_ATTEMPTS as M, type NodeInfo as N, type OutputFormatter as O, PORT_RANGE_START as P, createServer as Q, REQUEST_TIMEOUT as R, type ServerInfo as S, type ServerOptions as T, ConnectionManager as U, type ManagedWebSocket as V, RequestHandler as W, Logger as X, createLogger as Y, LogLevel as Z, type LoggerOptions as _, type SpaceNodeInfo as a, PORT_RANGE_END as b, PORT_SCAN_TIMEOUT as c, SERVER_INFO_FILE as d, SERVER_LOG_FILE as e, HEARTBEAT_TIMEOUT as f, SERVER_START_TIMEOUT as g, RETRY_INTERVALS as h, MAX_RETRY_COUNT as i, ConnectionType as j, MessageType as k, ErrorNames as l, ErrorMessages as m, MGError as n, createError as o, type RequestMessage as p, type ResponseMessage as q, type ErrorInfo as r, type RegisterMessage as s, type PingMessage as t, type PongMessage as u, type ConnectionInfo as v, type GetAllNodesParams as w, type ExportImageParams as x, type CliOptions as y, type RGBA as z };
786
+ export { type ServerOptions as $, type PongMessage as A, type BaseMessage as B, CONFIG_DIR as C, DEV_DEFAULT_PORT as D, ErrorCode as E, type ConnectionInfo as F, type GetNodeParams as G, HEARTBEAT_INTERVAL as H, IS_DEV_MODE as I, type GetPageParams as J, type GetAllNodesParams as K, LOG_DIR as L, MAX_PORT_ATTEMPTS as M, type NodeInfo as N, type ExportImageParams as O, PROD_DEFAULT_PORT as P, type OutputFormatter as Q, REQUEST_TIMEOUT as R, type ServerInfo as S, type CliOptions as T, type RGBA as U, type MgPageInfo as V, type AllPagesInfo as W, type ConnectedPageInfo as X, type ServerStatusResponse as Y, MGServer as Z, createServer as _, type SpaceNodeInfo as a, ConnectionManager as a0, type ManagedWebSocket as a1, RequestHandler as a2, Logger as a3, createLogger as a4, LogLevel as a5, type LoggerOptions as a6, PROD_PORT_RANGE_START as b, PROD_PORT_RANGE_END as c, DEV_PORT_RANGE_START as d, DEV_PORT_RANGE_END as e, DEFAULT_PORT as f, PORT_RANGE_START as g, PORT_RANGE_END as h, PORT_SCAN_TIMEOUT as i, SERVER_INFO_FILE as j, SERVER_LOG_FILE as k, HEARTBEAT_TIMEOUT as l, SERVER_START_TIMEOUT as m, RETRY_INTERVALS as n, MAX_RETRY_COUNT as o, ConnectionType as p, MessageType as q, ErrorNames as r, ErrorMessages as s, MGError as t, createError as u, type RequestMessage as v, type ResponseMessage as w, type ErrorInfo as x, type RegisterMessage as y, type PingMessage as z };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as ServerInfo, N as NodeInfo, a as SpaceNodeInfo } from './index--AigFaD9.js';
2
- export { F as AllPagesInfo, B as BaseMessage, C as CONFIG_DIR, y as CliOptions, I as ConnectedPageInfo, v as ConnectionInfo, U as ConnectionManager, j as ConnectionType, D as DEFAULT_PORT, E as ErrorCode, r as ErrorInfo, m as ErrorMessages, l as ErrorNames, x as ExportImageParams, w as GetAllNodesParams, G as GetNodeParams, H as HEARTBEAT_INTERVAL, f as HEARTBEAT_TIMEOUT, L as LOG_DIR, Z as LogLevel, X as Logger, _ as LoggerOptions, M as MAX_PORT_ATTEMPTS, i as MAX_RETRY_COUNT, n as MGError, K as MGServer, V as ManagedWebSocket, k as MessageType, A as MgPageInfo, O as OutputFormatter, b as PORT_RANGE_END, P as PORT_RANGE_START, c as PORT_SCAN_TIMEOUT, t as PingMessage, u as PongMessage, R as REQUEST_TIMEOUT, h as RETRY_INTERVALS, z as RGBA, s as RegisterMessage, W as RequestHandler, p as RequestMessage, q as ResponseMessage, d as SERVER_INFO_FILE, e as SERVER_LOG_FILE, g as SERVER_START_TIMEOUT, T as ServerOptions, J as ServerStatusResponse, o as createError, Y as createLogger, Q as createServer } from './index--AigFaD9.js';
1
+ import { S as ServerInfo, N as NodeInfo, a as SpaceNodeInfo } from './index-DUQN5gSR.js';
2
+ export { W as AllPagesInfo, B as BaseMessage, C as CONFIG_DIR, T as CliOptions, X as ConnectedPageInfo, F as ConnectionInfo, a0 as ConnectionManager, p as ConnectionType, f as DEFAULT_PORT, D as DEV_DEFAULT_PORT, e as DEV_PORT_RANGE_END, d as DEV_PORT_RANGE_START, E as ErrorCode, x as ErrorInfo, s as ErrorMessages, r as ErrorNames, O as ExportImageParams, K as GetAllNodesParams, G as GetNodeParams, J as GetPageParams, H as HEARTBEAT_INTERVAL, l as HEARTBEAT_TIMEOUT, I as IS_DEV_MODE, L as LOG_DIR, a5 as LogLevel, a3 as Logger, a6 as LoggerOptions, M as MAX_PORT_ATTEMPTS, o as MAX_RETRY_COUNT, t as MGError, Z as MGServer, a1 as ManagedWebSocket, q as MessageType, V as MgPageInfo, Q as OutputFormatter, h as PORT_RANGE_END, g as PORT_RANGE_START, i as PORT_SCAN_TIMEOUT, P as PROD_DEFAULT_PORT, c as PROD_PORT_RANGE_END, b as PROD_PORT_RANGE_START, z as PingMessage, A as PongMessage, R as REQUEST_TIMEOUT, n as RETRY_INTERVALS, U as RGBA, y as RegisterMessage, a2 as RequestHandler, v as RequestMessage, w as ResponseMessage, j as SERVER_INFO_FILE, k as SERVER_LOG_FILE, m as SERVER_START_TIMEOUT, $ as ServerOptions, Y as ServerStatusResponse, u as createError, a4 as createLogger, _ as createServer } from './index-DUQN5gSR.js';
3
3
  import 'ws';
4
4
 
5
5
  /**
@@ -56,15 +56,18 @@ declare function isDesignPageUrl(url: string): boolean;
56
56
  /**
57
57
  * 解析 mgp:// 链接
58
58
  *
59
- * 支持的格式 (queryParams 格式,nodeId 需要 URL 编码):
59
+ * 支持的格式 (queryParams 格式,nodeId/pageId 需要 URL 编码):
60
60
  * - mgp://mastergo.netease.com/file/174135798361888?nodeId=123%3A456 (单个节点)
61
61
  * - mgp://mastergo.netease.com/file/174135798361888?nodeId=0%3A8633&nodePath=314%3A13190%2F0%3A8633 (带父节点路径)
62
+ * - mgp://mastergo.netease.com/file/174135798361888?pageId=0%3A1 (页面链接)
62
63
  *
63
64
  * 输出: { pageUrl: 'mastergo.netease.com/file/174135798361888', nodeId: '0:8633', nodePath: ['314:13190', '0:8633'] }
65
+ * 或 { pageUrl: 'mastergo.netease.com/file/174135798361888', pageId: '0:1' }
64
66
  */
65
67
  declare function parseMgpLink(link: string): {
66
68
  pageUrl: string;
67
- nodeId: string;
69
+ nodeId?: string;
70
+ pageId?: string;
68
71
  nodePath?: string[];
69
72
  } | null;
70
73
  /**
@@ -75,6 +78,13 @@ declare function parseMgpLink(link: string): {
75
78
  * @param nodePath 可选的父节点路径(会被 URL 编码)
76
79
  */
77
80
  declare function generateMgpLink(pageUrl: string, nodeId: string, nodePath?: string[]): string;
81
+ /**
82
+ * 生成 mgp:// 页面链接
83
+ *
84
+ * @param pageUrl 页面 URL(会被标准化)
85
+ * @param pageId 页面 ID(会被 URL 编码)
86
+ */
87
+ declare function generatePageLink(pageUrl: string, pageId: string): string;
78
88
  /**
79
89
  * 格式化文件大小
80
90
  */
@@ -153,4 +163,4 @@ declare function trimNodeDefaults<T extends Record<string, unknown>>(node: T): T
153
163
  */
154
164
  declare function extractSpaceInfo(node: NodeInfo): SpaceNodeInfo;
155
165
 
156
- export { NODE_DEFAULTS, NodeInfo, ServerInfo, SpaceNodeInfo, deleteServerInfo, ensureConfigDir, ensureDir, ensureOutputDir, extractFileId, extractFileIdFromMgpLink, extractFileIdFromUrl, extractSpaceInfo, formatDuration, formatFileSize, formatLogTime, generateId, generateMgpLink, getCurrentISOTime, isDesignPageUrl, isProcessRunning, killProcess, normalizePageUrl, parseMgpLink, readServerInfo, resolveOutputPath, roundToOneDecimal, trimNodeDefaults, writeServerInfo };
166
+ export { NODE_DEFAULTS, NodeInfo, ServerInfo, SpaceNodeInfo, deleteServerInfo, ensureConfigDir, ensureDir, ensureOutputDir, extractFileId, extractFileIdFromMgpLink, extractFileIdFromUrl, extractSpaceInfo, formatDuration, formatFileSize, formatLogTime, generateId, generateMgpLink, generatePageLink, getCurrentISOTime, isDesignPageUrl, isProcessRunning, killProcess, normalizePageUrl, parseMgpLink, readServerInfo, resolveOutputPath, roundToOneDecimal, trimNodeDefaults, writeServerInfo };
package/dist/index.js CHANGED
@@ -1,9 +1,16 @@
1
1
  // src/shared/constants.ts
2
2
  import { homedir } from "os";
3
3
  import { join } from "path";
4
- var DEFAULT_PORT = 9527;
5
- var PORT_RANGE_START = 9527;
6
- var PORT_RANGE_END = 9536;
4
+ var IS_DEV_MODE = process.env.MG_DEV_MODE === "true";
5
+ var PROD_DEFAULT_PORT = 9527;
6
+ var PROD_PORT_RANGE_START = 9527;
7
+ var PROD_PORT_RANGE_END = 9536;
8
+ var DEV_DEFAULT_PORT = 19527;
9
+ var DEV_PORT_RANGE_START = 19527;
10
+ var DEV_PORT_RANGE_END = 19536;
11
+ var DEFAULT_PORT = IS_DEV_MODE ? DEV_DEFAULT_PORT : PROD_DEFAULT_PORT;
12
+ var PORT_RANGE_START = IS_DEV_MODE ? DEV_PORT_RANGE_START : PROD_PORT_RANGE_START;
13
+ var PORT_RANGE_END = IS_DEV_MODE ? DEV_PORT_RANGE_END : PROD_PORT_RANGE_END;
7
14
  var MAX_PORT_ATTEMPTS = 10;
8
15
  var PORT_SCAN_TIMEOUT = 500;
9
16
  var CONFIG_DIR = join(homedir(), ".mg-plugin");
@@ -27,6 +34,7 @@ var MessageType = /* @__PURE__ */ ((MessageType2) => {
27
34
  MessageType2["REGISTER"] = "register";
28
35
  MessageType2["REGISTER_ACK"] = "register_ack";
29
36
  MessageType2["GET_NODE_BY_ID"] = "get_node_by_id";
37
+ MessageType2["GET_PAGE_BY_ID"] = "get_page_by_id";
30
38
  MessageType2["GET_ALL_NODES"] = "get_all_nodes";
31
39
  MessageType2["GET_SELECTION"] = "get_selection";
32
40
  MessageType2["EXPORT_IMAGE"] = "export_image";
@@ -83,7 +91,7 @@ var ErrorNames = {
83
91
  var ErrorMessages = {
84
92
  ["E001" /* CONNECTION_FAILED */]: "\u65E0\u6CD5\u8FDE\u63A5\u5230 MG Server",
85
93
  ["E002" /* CONNECTION_TIMEOUT */]: "\u8FDE\u63A5\u8D85\u65F6",
86
- ["E003" /* NO_PAGE_CONNECTED */]: "\u6CA1\u6709 MasterGo \u9875\u9762\u8FDE\u63A5\u5230 Server",
94
+ ["E003" /* NO_PAGE_CONNECTED */]: "\u6CA1\u6709 MasterGo \u9875\u9762\u8FDE\u63A5\u5230 Server\u3002\u8BF7\u5B89\u88C5 MG Plugin \u6D4F\u89C8\u5668\u6269\u5C55: https://chromewebstore.google.com/detail/mg-plugin/ddhihanlpcdneicohnglnaliefnkaeja",
87
95
  ["E004" /* PAGE_NOT_FOUND */]: "\u672A\u627E\u5230\u5339\u914D\u7684\u9875\u9762",
88
96
  ["E005" /* NODE_NOT_FOUND */]: "\u8282\u70B9\u4E0D\u5B58\u5728",
89
97
  ["E006" /* NO_SELECTION */]: "\u6CA1\u6709\u9009\u4E2D\u4EFB\u4F55\u8282\u70B9",
@@ -218,27 +226,44 @@ function parseMgpLink(link) {
218
226
  const queryString = urlPart.slice(questionMarkIndex + 1);
219
227
  const params = new URLSearchParams(queryString);
220
228
  const encodedNodeId = params.get("nodeId");
221
- if (!encodedNodeId) {
229
+ const encodedPageId = params.get("pageId");
230
+ if (!encodedNodeId && !encodedPageId) {
222
231
  return null;
223
232
  }
224
- const nodeId = decodeURIComponent(encodedNodeId);
225
- if (!/^(\d+:\d+)(\/\d+:\d+)*$/.test(nodeId)) {
233
+ if (encodedNodeId && encodedPageId) {
226
234
  return null;
227
235
  }
228
- const encodedNodePath = params.get("nodePath");
229
- let nodePath;
230
- if (encodedNodePath) {
231
- const decodedNodePath = decodeURIComponent(encodedNodePath);
232
- nodePath = decodedNodePath.split("/").filter(Boolean);
233
- if (!nodePath.every((segment) => /^\d+:\d+$/.test(segment))) {
236
+ if (encodedNodeId) {
237
+ const nodeId = decodeURIComponent(encodedNodeId);
238
+ if (!/^(\d+:\d+)(\/\d+:\d+)*$/.test(nodeId)) {
234
239
  return null;
235
240
  }
241
+ const encodedNodePath = params.get("nodePath");
242
+ let nodePath;
243
+ if (encodedNodePath) {
244
+ const decodedNodePath = decodeURIComponent(encodedNodePath);
245
+ nodePath = decodedNodePath.split("/").filter(Boolean);
246
+ if (!nodePath.every((segment) => /^\d+:\d+$/.test(segment))) {
247
+ return null;
248
+ }
249
+ }
250
+ return {
251
+ pageUrl,
252
+ nodeId,
253
+ nodePath
254
+ };
236
255
  }
237
- return {
238
- pageUrl,
239
- nodeId,
240
- nodePath
241
- };
256
+ if (encodedPageId) {
257
+ const pageId = decodeURIComponent(encodedPageId);
258
+ if (!/^\d+:\d+$/.test(pageId)) {
259
+ return null;
260
+ }
261
+ return {
262
+ pageUrl,
263
+ pageId
264
+ };
265
+ }
266
+ return null;
242
267
  } catch {
243
268
  return null;
244
269
  }
@@ -253,6 +278,11 @@ function generateMgpLink(pageUrl, nodeId, nodePath) {
253
278
  }
254
279
  return link;
255
280
  }
281
+ function generatePageLink(pageUrl, pageId) {
282
+ const normalizedUrl = normalizePageUrl(pageUrl);
283
+ const encodedPageId = encodeURIComponent(pageId);
284
+ return `mgp://${normalizedUrl}?pageId=${encodedPageId}`;
285
+ }
256
286
  function formatFileSize(bytes) {
257
287
  if (bytes < 1024) {
258
288
  return `${bytes} \u5B57\u8282`;
@@ -550,7 +580,7 @@ function createLogger(options) {
550
580
  // src/server/connection-manager.ts
551
581
  var ConnectionManager = class {
552
582
  logger;
553
- /** Provider 连接(按页面 URL 索引) */
583
+ /** Provider 连接(按页面 URL 索引,支持同 URL 多个连接) */
554
584
  providers = /* @__PURE__ */ new Map();
555
585
  /** Consumer 连接 */
556
586
  consumers = /* @__PURE__ */ new Map();
@@ -586,7 +616,8 @@ var ConnectionManager = class {
586
616
  */
587
617
  checkHeartbeats() {
588
618
  const now = Date.now();
589
- for (const [id, ws] of this.allConnections) {
619
+ const entries = Array.from(this.allConnections.entries());
620
+ for (const [id, ws] of entries) {
590
621
  const lastActive = ws.connectionInfo.lastActiveAt.getTime();
591
622
  const elapsed = now - lastActive;
592
623
  if (elapsed > HEARTBEAT_TIMEOUT) {
@@ -616,14 +647,10 @@ var ConnectionManager = class {
616
647
  managedWs.isAlive = true;
617
648
  this.allConnections.set(connectionId, managedWs);
618
649
  if (type === "provider" /* PROVIDER */ && pageUrl) {
619
- const existing = this.providers.get(pageUrl);
620
- if (existing) {
621
- this.logger.info(`\u9875\u9762 ${pageUrl} \u5DF2\u6709\u8FDE\u63A5\uFF0C\u66FF\u6362\u4E3A\u65B0\u8FDE\u63A5`);
622
- this.removeConnection(existing);
623
- existing.close();
624
- }
625
- this.providers.set(pageUrl, managedWs);
626
- this.logger.info(`Provider \u8FDE\u63A5: ${pageUrl}`);
650
+ const existing = this.providers.get(pageUrl) || [];
651
+ existing.push(managedWs);
652
+ this.providers.set(pageUrl, existing);
653
+ this.logger.info(`Provider \u8FDE\u63A5: ${pageUrl} (\u5F53\u524D\u8BE5\u9875\u9762\u8FDE\u63A5\u6570: ${existing.length})`);
627
654
  } else if (type === "consumer" /* CONSUMER */) {
628
655
  this.consumers.set(connectionId, managedWs);
629
656
  this.logger.info(`Consumer \u8FDE\u63A5: ${connectionId}`);
@@ -637,8 +664,17 @@ var ConnectionManager = class {
637
664
  const { connectionId, connectionInfo } = ws;
638
665
  this.allConnections.delete(connectionId);
639
666
  if (connectionInfo.type === "provider" /* PROVIDER */ && connectionInfo.pageUrl) {
640
- this.providers.delete(connectionInfo.pageUrl);
641
- this.logger.info(`Provider \u65AD\u5F00: ${connectionInfo.pageUrl}`);
667
+ const connections = this.providers.get(connectionInfo.pageUrl);
668
+ if (connections) {
669
+ const index = connections.findIndex((c) => c.connectionId === connectionId);
670
+ if (index !== -1) {
671
+ connections.splice(index, 1);
672
+ if (connections.length === 0) {
673
+ this.providers.delete(connectionInfo.pageUrl);
674
+ }
675
+ }
676
+ }
677
+ this.logger.info(`Provider \u65AD\u5F00: ${connectionInfo.pageUrl} (\u8FDE\u63A5ID: ${connectionId})`);
642
678
  } else if (connectionInfo.type === "consumer" /* CONSUMER */) {
643
679
  this.consumers.delete(connectionId);
644
680
  this.logger.info(`Consumer \u65AD\u5F00: ${connectionId}`);
@@ -652,31 +688,48 @@ var ConnectionManager = class {
652
688
  ws.isAlive = true;
653
689
  }
654
690
  /**
655
- * 根据页面 URL 查找 Provider
691
+ * 根据页面 URL 查找 Provider(返回第一个可用的连接)
656
692
  */
657
693
  findProviderByPageUrl(pageUrl) {
658
- return this.providers.get(pageUrl);
694
+ const connections = this.providers.get(pageUrl);
695
+ return connections?.[0];
659
696
  }
660
697
  /**
661
698
  * 获取第一个可用的 Provider
662
699
  */
663
700
  getFirstProvider() {
664
- const iterator = this.providers.values();
665
- const first = iterator.next();
666
- return first.value;
701
+ const allConnections = Array.from(this.providers.values());
702
+ for (const connections of allConnections) {
703
+ if (connections.length > 0) {
704
+ return connections[0];
705
+ }
706
+ }
707
+ return void 0;
667
708
  }
668
709
  /**
669
710
  * 获取所有 Provider 信息
670
711
  */
671
712
  getAllProviders() {
672
- return Array.from(this.providers.values()).map((ws) => ws.connectionInfo);
713
+ const result = [];
714
+ const allConnections = Array.from(this.providers.values());
715
+ for (const connections of allConnections) {
716
+ for (const ws of connections) {
717
+ result.push(ws.connectionInfo);
718
+ }
719
+ }
720
+ return result;
673
721
  }
674
722
  /**
675
723
  * 获取连接统计
676
724
  */
677
725
  getStats() {
726
+ let providerCount = 0;
727
+ const allConnections = Array.from(this.providers.values());
728
+ for (const connections of allConnections) {
729
+ providerCount += connections.length;
730
+ }
678
731
  return {
679
- providers: this.providers.size,
732
+ providers: providerCount,
680
733
  consumers: this.consumers.size,
681
734
  total: this.allConnections.size
682
735
  };
@@ -692,7 +745,8 @@ var ConnectionManager = class {
692
745
  */
693
746
  closeAll() {
694
747
  this.stopHeartbeatCheck();
695
- for (const ws of this.allConnections.values()) {
748
+ const allWs = Array.from(this.allConnections.values());
749
+ for (const ws of allWs) {
696
750
  ws.close();
697
751
  }
698
752
  this.providers.clear();
@@ -728,7 +782,7 @@ var RequestHandler = class {
728
782
  } else {
729
783
  provider = this.connectionManager.getFirstProvider();
730
784
  if (!provider) {
731
- this.sendError(consumer, requestId, "E003" /* NO_PAGE_CONNECTED */, "\u6CA1\u6709\u9875\u9762\u8FDE\u63A5\u5230 Server");
785
+ this.sendError(consumer, requestId, "E003" /* NO_PAGE_CONNECTED */, ErrorMessages["E003" /* NO_PAGE_CONNECTED */]);
732
786
  return;
733
787
  }
734
788
  }
@@ -846,24 +900,11 @@ function getVersion() {
846
900
  try {
847
901
  const currentFile = fileURLToPath(import.meta.url);
848
902
  const currentDir = dirname3(currentFile);
849
- const versionFilePaths = [
850
- join2(currentDir, "..", "VERSION"),
851
- // dist/xxx.js -> ../VERSION
852
- join2(currentDir, "..", "..", "VERSION")
853
- // src/shared/version.ts -> ../../VERSION
854
- ];
855
- for (const versionFilePath of versionFilePaths) {
856
- if (existsSync3(versionFilePath)) {
857
- const version = readFileSync2(versionFilePath, "utf-8").trim();
858
- if (version) {
859
- cachedVersion = version;
860
- return cachedVersion;
861
- }
862
- }
863
- }
864
903
  const packageJsonPaths = [
865
904
  join2(currentDir, "..", "package.json"),
905
+ // dist/xxx.js -> ../package.json
866
906
  join2(currentDir, "..", "..", "package.json")
907
+ // src/shared/version.ts -> ../../package.json
867
908
  ];
868
909
  for (const packageJsonPath of packageJsonPaths) {
869
910
  if (existsSync3(packageJsonPath)) {
@@ -1132,11 +1173,15 @@ export {
1132
1173
  ConnectionManager,
1133
1174
  ConnectionType,
1134
1175
  DEFAULT_PORT,
1176
+ DEV_DEFAULT_PORT,
1177
+ DEV_PORT_RANGE_END,
1178
+ DEV_PORT_RANGE_START,
1135
1179
  ErrorCode,
1136
1180
  ErrorMessages,
1137
1181
  ErrorNames,
1138
1182
  HEARTBEAT_INTERVAL,
1139
1183
  HEARTBEAT_TIMEOUT,
1184
+ IS_DEV_MODE,
1140
1185
  LOG_DIR,
1141
1186
  LogLevel,
1142
1187
  Logger,
@@ -1149,6 +1194,9 @@ export {
1149
1194
  PORT_RANGE_END,
1150
1195
  PORT_RANGE_START,
1151
1196
  PORT_SCAN_TIMEOUT,
1197
+ PROD_DEFAULT_PORT,
1198
+ PROD_PORT_RANGE_END,
1199
+ PROD_PORT_RANGE_START,
1152
1200
  REQUEST_TIMEOUT,
1153
1201
  RETRY_INTERVALS,
1154
1202
  RequestHandler,
@@ -1171,6 +1219,7 @@ export {
1171
1219
  formatLogTime,
1172
1220
  generateId,
1173
1221
  generateMgpLink,
1222
+ generatePageLink,
1174
1223
  getCurrentISOTime,
1175
1224
  isDesignPageUrl,
1176
1225
  isProcessRunning,