@hangox/mg-cli 1.0.6 → 1.0.8

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.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/index.ts","../src/cli/commands/server.ts","../src/server/daemon.ts","../src/shared/utils.ts","../src/shared/constants.ts","../src/shared/errors.ts","../src/server/websocket-server.ts","../src/server/logger.ts","../src/server/connection-manager.ts","../src/server/request-handler.ts","../src/shared/version.ts","../src/cli/client.ts","../src/cli/commands/get-node-by-id.ts","../src/cli/commands/get-node-by-link.ts","../src/cli/commands/get-all-nodes.ts","../src/cli/commands/export-image.ts","../src/cli/commands/execute-code.ts","../src/cli/commands/get-all-pages.ts","../src/cli/commands/get-node-for-space.ts"],"sourcesContent":["/**\n * MG CLI 入口\n */\n\nimport { Command } from 'commander'\nimport { createServerCommand } from './commands/server.js'\nimport { createGetNodeByIdCommand } from './commands/get-node-by-id.js'\nimport { createGetNodeByLinkCommand } from './commands/get-node-by-link.js'\nimport { createGetAllNodesCommand } from './commands/get-all-nodes.js'\nimport { createExportImageCommand } from './commands/export-image.js'\nimport { createExecuteCodeCommand } from './commands/execute-code.js'\nimport { createGetAllPagesCommand } from './commands/get-all-pages.js'\nimport { createGetNodeForSpaceCommand } from './commands/get-node-for-space.js'\n\nconst program = new Command()\n\nprogram\n .name('mg-cli')\n .description('MasterGo CLI 工具 - 用于 Claude Code 与 MasterGo 通信')\n .version('1.0.0')\n\n// 添加 server 命令组\nprogram.addCommand(createServerCommand())\n\n// 添加业务命令\nprogram.addCommand(createGetNodeByIdCommand())\nprogram.addCommand(createGetNodeByLinkCommand())\nprogram.addCommand(createGetAllNodesCommand())\nprogram.addCommand(createExportImageCommand())\nprogram.addCommand(createExecuteCodeCommand())\nprogram.addCommand(createGetAllPagesCommand())\nprogram.addCommand(createGetNodeForSpaceCommand())\n\n// 解析命令行参数\nprogram.parse()\n","/**\n * Server 管理命令\n */\n\nimport { Command } from 'commander'\nimport {\n startServerForeground,\n startServerDaemon,\n stopServer,\n restartServer,\n getServerStatus,\n} from '../../server/daemon.js'\nimport { MGClient } from '../client.js'\nimport { MessageType } from '../../shared/constants.js'\nimport type { ServerStatusResponse } from '../../shared/types.js'\n\n/**\n * 创建 server 命令组\n */\nexport function createServerCommand(): Command {\n const serverCmd = new Command('server')\n .description('Server 管理命令')\n\n // server start\n serverCmd\n .command('start')\n .description('启动 MG Server')\n .option('--port <number>', '指定启动端口', (value) => parseInt(value, 10))\n .option('--foreground', '前台模式运行(不作为守护进程)', false)\n .action(async (options) => {\n try {\n if (options.foreground) {\n await startServerForeground(options.port)\n } else {\n const info = await startServerDaemon(options.port)\n console.log('MG Server 启动成功')\n console.log(`版本: ${info.version}`)\n console.log(`监听端口: ${info.port}`)\n console.log(`进程 PID: ${info.pid}`)\n console.log(`运行模式: 守护进程`)\n }\n } catch (error: any) {\n console.error(`错误: ${error.message}`)\n process.exit(1)\n }\n })\n\n // server stop\n serverCmd\n .command('stop')\n .description('停止 MG Server')\n .action(() => {\n try {\n const { stopped, info } = stopServer()\n\n if (stopped && info) {\n console.log('MG Server 已停止')\n console.log(`PID: ${info.pid}`)\n\n // 计算运行时长\n const uptimeMs = Date.now() - new Date(info.startedAt).getTime()\n const seconds = Math.floor(uptimeMs / 1000)\n const minutes = Math.floor(seconds / 60)\n const hours = Math.floor(minutes / 60)\n\n let uptime = ''\n if (hours > 0) {\n uptime = `${hours} 小时 ${minutes % 60} 分钟`\n } else if (minutes > 0) {\n uptime = `${minutes} 分钟 ${seconds % 60} 秒`\n } else {\n uptime = `${seconds} 秒`\n }\n\n console.log(`运行时长: ${uptime}`)\n } else {\n console.log('MG Server 未运行')\n }\n } catch (error: any) {\n console.error(`错误: ${error.message}`)\n process.exit(1)\n }\n })\n\n // server restart\n serverCmd\n .command('restart')\n .description('重启 MG Server')\n .option('--port <number>', '重启后使用的端口', (value) => parseInt(value, 10))\n .action(async (options) => {\n try {\n const status = getServerStatus()\n\n if (status.running) {\n console.log(`正在停止 MG Server (PID: ${status.pid})...`)\n }\n\n const info = await restartServer(options.port)\n\n console.log('MG Server 已重启')\n console.log(`版本: ${info.version}`)\n console.log(`监听端口: ${info.port}`)\n console.log(`新进程 PID: ${info.pid}`)\n } catch (error: any) {\n console.error(`错误: ${error.message}`)\n process.exit(1)\n }\n })\n\n // server status\n serverCmd\n .command('status')\n .description('查看 Server 运行状态')\n .action(async () => {\n try {\n // 先检查进程是否运行\n const basicStatus = getServerStatus()\n\n if (!basicStatus.running) {\n console.log('MG Server 状态: 未运行 ✗')\n console.log(\"提示: 使用 'mg-cli server start' 启动 Server\")\n return\n }\n\n // 连接 Server 获取详细状态(包括连接的页面)\n try {\n const client = new MGClient({ noAutoStart: true })\n await client.connect()\n\n const status = await client.request<ServerStatusResponse>(\n MessageType.GET_SERVER_STATUS\n )\n\n client.close()\n\n // 显示状态信息\n console.log('MG Server 状态: 运行中 ✓')\n console.log(`版本: ${status.version}`)\n console.log(`监听端口: ${status.port}`)\n console.log(`进程 PID: ${status.pid}`)\n console.log(`启动时间: ${status.startedAt}`)\n console.log(`运行时长: ${status.uptime}`)\n\n // 显示连接统计\n console.log(``)\n console.log(`连接统计:`)\n console.log(` Provider 数量: ${status.stats.providers}`)\n console.log(` Consumer 数量: ${status.stats.consumers}`)\n\n // 显示已连接的页面\n if (status.connectedPages.length > 0) {\n console.log(``)\n console.log(`已连接页面 (${status.connectedPages.length}):`)\n for (const page of status.connectedPages) {\n console.log(` - ${page.pageUrl}`)\n }\n } else {\n console.log(``)\n console.log(`已连接页面: 无`)\n console.log(`提示: 请安装 MG Plugin 浏览器扩展并在 Chrome 中打开 MasterGo 页面`)\n console.log(` 插件地址: https://chromewebstore.google.com/detail/mg-plugin/ddhihanlpcdneicohnglnaliefnkaeja`)\n }\n } catch {\n // 无法连接但进程存在,显示基本信息\n console.log('MG Server 状态: 运行中 ✓')\n if (basicStatus.version) {\n console.log(`版本: ${basicStatus.version}`)\n }\n console.log(`监听端口: ${basicStatus.port}`)\n console.log(`进程 PID: ${basicStatus.pid}`)\n console.log(`启动时间: ${basicStatus.startedAt}`)\n console.log(`运行时长: ${basicStatus.uptime}`)\n console.log(``)\n console.log(`注意: 无法获取详细连接信息`)\n }\n } catch (error: any) {\n console.error(`错误: ${error.message}`)\n process.exit(1)\n }\n })\n\n return serverCmd\n}\n","/**\n * 守护进程管理\n */\n\nimport { spawn, ChildProcess } from 'node:child_process'\nimport { fileURLToPath } from 'node:url'\nimport { dirname, join } from 'node:path'\nimport {\n readServerInfo,\n writeServerInfo,\n deleteServerInfo,\n isProcessRunning,\n killProcess,\n ensureConfigDir,\n getCurrentISOTime,\n formatDuration,\n} from '../shared/utils.js'\nimport { SERVER_START_TIMEOUT, DEFAULT_PORT } from '../shared/constants.js'\nimport { ErrorCode, MGError } from '../shared/errors.js'\nimport type { ServerInfo } from '../shared/types.js'\nimport { createServer } from './websocket-server.js'\nimport { createLogger, LogLevel } from './logger.js'\nimport { getVersion } from '../shared/version.js'\n\n/**\n * 检查 Server 是否在运行\n */\nexport function isServerRunning(): { running: boolean; info: ServerInfo | null } {\n const info = readServerInfo()\n\n if (!info) {\n return { running: false, info: null }\n }\n\n // 检查进程是否存在\n if (!isProcessRunning(info.pid)) {\n // 进程不存在,清理旧文件\n deleteServerInfo()\n return { running: false, info: null }\n }\n\n return { running: true, info }\n}\n\n/**\n * 启动 Server(前台模式)\n */\nexport async function startServerForeground(port?: number): Promise<void> {\n const { running, info } = isServerRunning()\n\n if (running && info) {\n throw new MGError(\n ErrorCode.SERVER_ALREADY_RUNNING,\n `Server 已在运行中 (PID: ${info.pid}, 端口: ${info.port})`\n )\n }\n\n ensureConfigDir()\n\n const logger = createLogger({\n console: true,\n file: true,\n minLevel: LogLevel.INFO,\n })\n\n const server = createServer({\n port: port || DEFAULT_PORT,\n logger,\n })\n\n // 注册信号处理\n const cleanup = async () => {\n console.log('\\n正在停止 Server...')\n await server.stop()\n deleteServerInfo()\n process.exit(0)\n }\n\n process.on('SIGINT', cleanup)\n process.on('SIGTERM', cleanup)\n\n try {\n const actualPort = await server.start()\n\n // 写入 Server 信息(包含版本号)\n writeServerInfo({\n port: actualPort,\n pid: process.pid,\n startedAt: getCurrentISOTime(),\n version: getVersion(),\n })\n\n console.log(`\\nMG Server 启动成功`)\n console.log(`监听端口: ${actualPort}`)\n console.log(`进程 PID: ${process.pid}`)\n console.log(`运行模式: 前台`)\n console.log(`\\n按 Ctrl+C 停止...`)\n } catch (error) {\n logger.error('Server 启动失败:', error)\n throw error\n }\n}\n\n/**\n * 启动 Server(守护进程模式)\n */\nexport async function startServerDaemon(port?: number): Promise<ServerInfo> {\n const { running, info } = isServerRunning()\n\n if (running && info) {\n throw new MGError(\n ErrorCode.SERVER_ALREADY_RUNNING,\n `Server 已在运行中 (PID: ${info.pid}, 端口: ${info.port})`\n )\n }\n\n ensureConfigDir()\n\n // 获取当前模块的路径\n const currentFile = fileURLToPath(import.meta.url)\n const currentDir = dirname(currentFile)\n const serverScript = join(currentDir, 'daemon-runner.js')\n\n // 启动子进程\n const args = ['--foreground']\n if (port) {\n args.push('--port', String(port))\n }\n\n const child: ChildProcess = spawn(process.execPath, [serverScript, ...args], {\n detached: true,\n stdio: 'ignore',\n env: {\n ...process.env,\n MG_DAEMON: '1',\n },\n })\n\n child.unref()\n\n // 等待 Server 启动\n const startTime = Date.now()\n while (Date.now() - startTime < SERVER_START_TIMEOUT) {\n await new Promise((resolve) => setTimeout(resolve, 200))\n\n const { running, info } = isServerRunning()\n if (running && info) {\n return info\n }\n }\n\n throw new MGError(ErrorCode.SERVER_START_FAILED, 'Server 启动超时')\n}\n\n/**\n * 停止 Server\n */\nexport function stopServer(): { stopped: boolean; info: ServerInfo | null } {\n const { running, info } = isServerRunning()\n\n if (!running || !info) {\n return { stopped: false, info: null }\n }\n\n // 终止进程\n const killed = killProcess(info.pid)\n\n if (killed) {\n deleteServerInfo()\n }\n\n return { stopped: killed, info }\n}\n\n/**\n * 重启 Server\n */\nexport async function restartServer(port?: number): Promise<ServerInfo> {\n const { info: oldInfo } = stopServer()\n\n // 等待旧进程完全停止\n await new Promise((resolve) => setTimeout(resolve, 500))\n\n // 启动新 Server\n return startServerDaemon(port || oldInfo?.port)\n}\n\n/**\n * 获取 Server 状态\n */\nexport function getServerStatus(): {\n running: boolean\n port?: number\n pid?: number\n startedAt?: string\n uptime?: string\n version?: string\n} {\n const { running, info } = isServerRunning()\n\n if (!running || !info) {\n return { running: false }\n }\n\n const uptimeMs = Date.now() - new Date(info.startedAt).getTime()\n\n return {\n running: true,\n port: info.port,\n pid: info.pid,\n startedAt: info.startedAt,\n uptime: formatDuration(uptimeMs),\n version: info.version,\n }\n}\n","/**\n * MG Plugin 工具函数\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs'\nimport { dirname, resolve, isAbsolute } from 'node:path'\nimport { CONFIG_DIR, SERVER_INFO_FILE, LOG_DIR } from './constants.js'\nimport type { ServerInfo, SpaceNodeInfo, NodeInfo } from './types.js'\n\n// ==================== 文件操作 ====================\n\n/**\n * 确保目录存在\n */\nexport function ensureDir(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n}\n\n/**\n * 确保配置目录存在\n */\nexport function ensureConfigDir(): void {\n ensureDir(CONFIG_DIR)\n ensureDir(LOG_DIR)\n}\n\n/**\n * 读取 Server 信息文件\n */\nexport function readServerInfo(): ServerInfo | null {\n try {\n if (!existsSync(SERVER_INFO_FILE)) {\n return null\n }\n const content = readFileSync(SERVER_INFO_FILE, 'utf-8')\n return JSON.parse(content) as ServerInfo\n } catch {\n return null\n }\n}\n\n/**\n * 写入 Server 信息文件\n */\nexport function writeServerInfo(info: ServerInfo): void {\n ensureConfigDir()\n writeFileSync(SERVER_INFO_FILE, JSON.stringify(info, null, 2), 'utf-8')\n}\n\n/**\n * 删除 Server 信息文件\n */\nexport function deleteServerInfo(): void {\n try {\n if (existsSync(SERVER_INFO_FILE)) {\n unlinkSync(SERVER_INFO_FILE)\n }\n } catch {\n // 忽略删除错误\n }\n}\n\n/**\n * 解析输出路径(支持相对路径和绝对路径)\n */\nexport function resolveOutputPath(outputPath: string): string {\n if (isAbsolute(outputPath)) {\n return outputPath\n }\n return resolve(process.cwd(), outputPath)\n}\n\n/**\n * 确保输出路径的目录存在\n */\nexport function ensureOutputDir(outputPath: string): void {\n const dir = dirname(outputPath)\n ensureDir(dir)\n}\n\n// ==================== 进程管理 ====================\n\n/**\n * 检查进程是否存在\n */\nexport function isProcessRunning(pid: number): boolean {\n try {\n // 发送信号 0 不会终止进程,但如果进程不存在会抛出错误\n process.kill(pid, 0)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * 终止进程\n */\nexport function killProcess(pid: number): boolean {\n try {\n process.kill(pid, 'SIGTERM')\n return true\n } catch {\n return false\n }\n}\n\n// ==================== URL 处理 ====================\n\n/**\n * 标准化页面 URL\n *\n * 输入: https://mastergo.netease.com/file/174135798361888?fileOpenFrom=home&page_id=0%3A8347\n * 输出: mastergo.netease.com/file/174135798361888\n */\nexport function normalizePageUrl(url: string): string {\n try {\n const urlObj = new URL(url)\n // 返回 host + pathname,去掉 https:// 前缀和查询参数\n return urlObj.host + urlObj.pathname\n } catch {\n // 如果已经是标准化的格式,直接返回\n return url\n }\n}\n\n/**\n * 检查是否是设计页面 URL\n */\nexport function isDesignPageUrl(url: string): boolean {\n // 匹配 /file/数字 格式\n return /\\/file\\/\\d+/.test(url)\n}\n\n/**\n * 解析 mgp:// 链接\n *\n * 支持的格式 (queryParams 格式,nodeId 需要 URL 编码):\n * - mgp://mastergo.netease.com/file/174135798361888?nodeId=123%3A456 (单个节点)\n * - mgp://mastergo.netease.com/file/174135798361888?nodeId=0%3A8633&nodePath=314%3A13190%2F0%3A8633 (带父节点路径)\n *\n * 输出: { pageUrl: 'mastergo.netease.com/file/174135798361888', nodeId: '0:8633', nodePath: ['314:13190', '0:8633'] }\n */\nexport function parseMgpLink(link: string): { pageUrl: string; nodeId: string; nodePath?: string[] } | null {\n // 检查是否是 mgp:// 协议\n if (!link.startsWith('mgp://')) {\n return null\n }\n\n try {\n // 移除 mgp:// 前缀,构造为可解析的 URL\n const urlPart = link.slice(6) // 移除 'mgp://'\n const questionMarkIndex = urlPart.indexOf('?')\n\n if (questionMarkIndex === -1) {\n // 没有查询参数,无效格式\n return null\n }\n\n const pageUrl = urlPart.slice(0, questionMarkIndex)\n const queryString = urlPart.slice(questionMarkIndex + 1)\n\n // 解析查询参数\n const params = new URLSearchParams(queryString)\n const encodedNodeId = params.get('nodeId')\n\n if (!encodedNodeId) {\n return null\n }\n\n // 解码 nodeId\n const nodeId = decodeURIComponent(encodedNodeId)\n\n // 验证 nodeId 格式:支持单个节点 (数字:数字) 或带路径的格式 (数字:数字/数字:数字/...)\n if (!/^(\\d+:\\d+)(\\/\\d+:\\d+)*$/.test(nodeId)) {\n return null\n }\n\n // 解析可选的 nodePath\n const encodedNodePath = params.get('nodePath')\n let nodePath: string[] | undefined\n\n if (encodedNodePath) {\n const decodedNodePath = decodeURIComponent(encodedNodePath)\n nodePath = decodedNodePath.split('/').filter(Boolean)\n // 验证每个路径段的格式\n if (!nodePath.every(segment => /^\\d+:\\d+$/.test(segment))) {\n return null\n }\n }\n\n return {\n pageUrl,\n nodeId,\n nodePath,\n }\n } catch {\n return null\n }\n}\n\n/**\n * 生成 mgp:// 链接\n *\n * @param pageUrl 页面 URL(会被标准化)\n * @param nodeId 节点 ID(会被 URL 编码)\n * @param nodePath 可选的父节点路径(会被 URL 编码)\n */\nexport function generateMgpLink(pageUrl: string, nodeId: string, nodePath?: string[]): string {\n const normalizedUrl = normalizePageUrl(pageUrl)\n const encodedNodeId = encodeURIComponent(nodeId)\n\n let link = `mgp://${normalizedUrl}?nodeId=${encodedNodeId}`\n\n if (nodePath && nodePath.length > 0) {\n const encodedNodePath = encodeURIComponent(nodePath.join('/'))\n link += `&nodePath=${encodedNodePath}`\n }\n\n return link\n}\n\n// ==================== 输出格式化 ====================\n\n/**\n * 格式化文件大小\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) {\n return `${bytes} 字节`\n }\n const kb = bytes / 1024\n if (kb < 1024) {\n return `${kb.toFixed(2)} KB`\n }\n const mb = kb / 1024\n return `${mb.toFixed(2)} MB`\n}\n\n/**\n * 格式化时间间隔\n */\nexport function formatDuration(ms: number): string {\n const seconds = Math.floor(ms / 1000)\n const minutes = Math.floor(seconds / 60)\n const hours = Math.floor(minutes / 60)\n const days = Math.floor(hours / 24)\n\n if (days > 0) {\n return `${days} 天 ${hours % 24} 小时`\n }\n if (hours > 0) {\n return `${hours} 小时 ${minutes % 60} 分钟`\n }\n if (minutes > 0) {\n return `${minutes} 分钟 ${seconds % 60} 秒`\n }\n return `${seconds} 秒`\n}\n\n/**\n * 生成唯一 ID\n */\nexport function generateId(): string {\n return `${Date.now()}_${Math.random().toString(36).substring(2, 9)}`\n}\n\n/**\n * 获取当前时间的 ISO 格式字符串\n */\nexport function getCurrentISOTime(): string {\n return new Date().toISOString()\n}\n\n/**\n * 格式化日期时间用于日志\n */\nexport function formatLogTime(date: Date = new Date()): string {\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const day = String(date.getDate()).padStart(2, '0')\n const hours = String(date.getHours()).padStart(2, '0')\n const minutes = String(date.getMinutes()).padStart(2, '0')\n const seconds = String(date.getSeconds()).padStart(2, '0')\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`\n}\n\n// ==================== FileId 提取 ====================\n\n/**\n * 从完整 URL 提取 fileId\n * \n * 支持格式:\n * - https://mastergo.netease.com/file/174875497054651?page_id=321%3A11979\n * - mastergo.netease.com/file/174875497054651\n * \n * @returns fileId 或 null\n */\nexport function extractFileIdFromUrl(url: string): string | null {\n // 匹配 /file/数字 格式\n const match = url.match(/\\/file\\/(\\d+)/)\n return match ? match[1] : null\n}\n\n/**\n * 从 mgp:// 链接提取 fileId\n * \n * 支持格式:\n * - mgp://mastergo.netease.com/file/174875497054651?nodeId=xxx\n * \n * @returns fileId 或 null\n */\nexport function extractFileIdFromMgpLink(link: string): string | null {\n if (!link.startsWith('mgp://')) {\n return null\n }\n return extractFileIdFromUrl(link)\n}\n\n/**\n * 统一处理输入,提取 fileId\n * \n * 支持三种输入格式:\n * 1. 完整 URL: https://mastergo.netease.com/file/174875497054651?page_id=xxx\n * 2. mgp 协议: mgp://mastergo.netease.com/file/174875497054651?nodeId=xxx\n * 3. 纯 fileId: 174875497054651\n * \n * @returns fileId 或 null\n */\nexport function extractFileId(input: string): string | null {\n const trimmed = input.trim()\n \n // 1. 纯数字 fileId\n if (/^\\d+$/.test(trimmed)) {\n return trimmed\n }\n \n // 2. mgp:// 协议\n if (trimmed.startsWith('mgp://')) {\n return extractFileIdFromMgpLink(trimmed)\n }\n \n // 3. 完整 URL (http:// 或 https://)\n if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {\n return extractFileIdFromUrl(trimmed)\n }\n \n // 4. 尝试作为不带协议的 URL 解析\n return extractFileIdFromUrl(trimmed)\n}\n\n// ==================== 节点数据精简 ====================\n\n/**\n * 需要四舍五入的数值字段\n */\nconst NUMERIC_FIELDS = [\n 'x', 'y', 'width', 'height',\n 'rotation',\n 'opacity',\n 'cornerRadius', 'topLeftRadius', 'topRightRadius', 'bottomLeftRadius', 'bottomRightRadius',\n 'strokeWeight', 'strokeTopWeight', 'strokeLeftWeight', 'strokeBottomWeight', 'strokeRightWeight',\n 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft',\n 'itemSpacing', 'crossAxisSpacing',\n 'flexGrow',\n]\n\n/**\n * 四舍五入到一位小数\n * 25.666667938232422 -> 25.7\n */\nexport function roundToOneDecimal(value: number): number {\n return Math.round(value * 10) / 10\n}\n\n/**\n * MasterGo 节点属性默认值\n * 基于官方文档定义,当值等于默认值时可以省略以减少 JSON 体积\n */\nexport const NODE_DEFAULTS: Record<string, unknown> = {\n // Scene Node 属性\n visible: true,\n isVisible: true,\n isLocked: false,\n\n // Blend 属性\n opacity: 1,\n blendMode: 'NORMAL',\n isMask: false,\n isMaskOutline: false,\n isMaskVisible: false,\n effectStyleId: '',\n\n // Geometry 属性\n strokeStyle: 'SOLID',\n strokeWeight: 0,\n strokeTopWeight: 0,\n strokeLeftWeight: 0,\n strokeBottomWeight: 0,\n strokeRightWeight: 0,\n strokeAlign: 'CENTER',\n strokeCap: 'NONE',\n strokeJoin: 'MITER',\n dashCap: 'NONE',\n fillStyleId: '',\n strokeStyleId: '',\n\n // Corner 属性\n cornerSmooth: 0,\n cornerRadius: 0,\n topLeftRadius: 0,\n topRightRadius: 0,\n bottomLeftRadius: 0,\n bottomRightRadius: 0,\n\n // Layout 属性\n rotation: 0,\n flexGrow: 0,\n alignSelf: 'INHERIT',\n layoutPositioning: 'AUTO',\n constrainProportions: false,\n\n // 容器专属属性 (FrameNode)\n flexMode: 'NONE',\n flexWrap: 'NO_WRAP',\n itemSpacing: 0,\n crossAxisSpacing: 0,\n paddingTop: 0,\n paddingRight: 0,\n paddingBottom: 0,\n paddingLeft: 0,\n clipsContent: false,\n itemReverseZIndex: false,\n strokesIncludedInLayout: false,\n\n // 其他属性\n componentPropertyReferences: null,\n}\n\n/**\n * 需要检查是否为空数组的字段\n */\nconst EMPTY_ARRAY_FIELDS = [\n 'fills',\n 'strokes',\n 'effects',\n 'strokeDashes',\n 'exportSettings',\n 'reactions',\n 'attachedConnectors',\n]\n\n/**\n * 检查值是否为空数组\n */\nfunction isEmptyArray(value: unknown): boolean {\n return Array.isArray(value) && value.length === 0\n}\n\n/**\n * 检查两个值是否相等(简单比较)\n */\nfunction isEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (a === null || b === null) return a === b\n if (typeof a !== typeof b) return false\n return false\n}\n\n/**\n * 精简节点数据,移除等于默认值的字段\n * 递归处理 children 数组\n *\n * @param node 原始节点数据\n * @returns 精简后的节点数据\n */\nexport function trimNodeDefaults<T extends Record<string, unknown>>(node: T): T {\n const result: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(node)) {\n // 递归处理 children 数组\n if (key === 'children' && Array.isArray(value)) {\n const trimmedChildren = value.map((child) =>\n typeof child === 'object' && child !== null\n ? trimNodeDefaults(child as Record<string, unknown>)\n : child\n )\n // 只有非空时才保留\n if (trimmedChildren.length > 0) {\n result[key] = trimmedChildren\n }\n continue\n }\n\n // 检查是否是需要检查空数组的字段\n if (EMPTY_ARRAY_FIELDS.includes(key) && isEmptyArray(value)) {\n continue // 跳过空数组\n }\n\n // 对数值字段进行四舍五入\n if (NUMERIC_FIELDS.includes(key) && typeof value === 'number') {\n const roundedValue = roundToOneDecimal(value)\n // 检查四舍五入后是否等于默认值\n if (key in NODE_DEFAULTS && isEqual(roundedValue, NODE_DEFAULTS[key])) {\n continue // 跳过等于默认值的字段\n }\n result[key] = roundedValue\n continue\n }\n\n // 检查是否有默认值定义\n if (key in NODE_DEFAULTS) {\n const defaultValue = NODE_DEFAULTS[key]\n if (isEqual(value, defaultValue)) {\n continue // 跳过等于默认值的字段\n }\n }\n\n // 保留此字段\n result[key] = value\n }\n\n return result as T\n}\n\n// ==================== 空间信息提取 ====================\n\n/**\n * 提取节点的空间信息\n * 只保留 id、name、x、y、width、height 和 children 字段\n * 用于让 AI 以最少的字段理解节点的空间布局关系\n *\n * @param node 原始节点数据\n * @returns 精简的空间节点信息\n */\nexport function extractSpaceInfo(node: NodeInfo): SpaceNodeInfo {\n const result: SpaceNodeInfo = {\n id: node.id,\n name: node.name,\n x: roundToOneDecimal(typeof node.x === 'number' ? node.x : 0),\n y: roundToOneDecimal(typeof node.y === 'number' ? node.y : 0),\n width: roundToOneDecimal(typeof node.width === 'number' ? node.width : 0),\n height: roundToOneDecimal(typeof node.height === 'number' ? node.height : 0),\n }\n\n // 递归处理 children\n if (node.children && Array.isArray(node.children) && node.children.length > 0) {\n result.children = node.children.map((child) => extractSpaceInfo(child))\n }\n\n return result\n}\n","/**\n * MG Plugin 常量定义\n */\n\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\n// ==================== 运行模式 ====================\n\n/** 是否为开发模式(通过环境变量 MG_DEV_MODE=true 设置) */\nexport const IS_DEV_MODE = process.env.MG_DEV_MODE === 'true'\n\n// ==================== 端口配置 ====================\n\n/** 生产环境默认端口 */\nexport const PROD_DEFAULT_PORT = 9527\n\n/** 生产环境端口范围:起始 */\nexport const PROD_PORT_RANGE_START = 9527\n\n/** 生产环境端口范围:结束 */\nexport const PROD_PORT_RANGE_END = 9536\n\n/** 开发环境默认端口 */\nexport const DEV_DEFAULT_PORT = 19527\n\n/** 开发环境端口范围:起始 */\nexport const DEV_PORT_RANGE_START = 19527\n\n/** 开发环境端口范围:结束 */\nexport const DEV_PORT_RANGE_END = 19536\n\n/** 默认端口(根据运行模式自动选择) */\nexport const DEFAULT_PORT = IS_DEV_MODE ? DEV_DEFAULT_PORT : PROD_DEFAULT_PORT\n\n/** 端口范围:起始(根据运行模式自动选择) */\nexport const PORT_RANGE_START = IS_DEV_MODE ? DEV_PORT_RANGE_START : PROD_PORT_RANGE_START\n\n/** 端口范围:结束(根据运行模式自动选择) */\nexport const PORT_RANGE_END = IS_DEV_MODE ? DEV_PORT_RANGE_END : PROD_PORT_RANGE_END\n\n/** 最大尝试端口数 */\nexport const MAX_PORT_ATTEMPTS = 10\n\n/** 端口扫描超时(毫秒) */\nexport const PORT_SCAN_TIMEOUT = 500\n\n// ==================== 路径配置 ====================\n\n/** 配置目录 */\nexport const CONFIG_DIR = join(homedir(), '.mg-plugin')\n\n/** Server 状态文件 */\nexport const SERVER_INFO_FILE = join(CONFIG_DIR, 'server.json')\n\n/** 日志目录 */\nexport const LOG_DIR = join(CONFIG_DIR, 'logs')\n\n/** Server 日志文件 */\nexport const SERVER_LOG_FILE = join(LOG_DIR, 'server.log')\n\n// ==================== 超时配置 ====================\n\n/** 心跳间隔(毫秒)- 30 秒 */\nexport const HEARTBEAT_INTERVAL = 30000\n\n/** 心跳超时(毫秒)- 90 秒(3 次心跳) */\nexport const HEARTBEAT_TIMEOUT = 90000\n\n/** 请求超时(毫秒)- 30 秒 */\nexport const REQUEST_TIMEOUT = 30000\n\n/** Server 启动等待超时(毫秒)- 5 秒 */\nexport const SERVER_START_TIMEOUT = 5000\n\n/** CLI 重试间隔(毫秒) */\nexport const RETRY_INTERVALS = [1000, 2000, 4000]\n\n/** 最大重试次数 */\nexport const MAX_RETRY_COUNT = 3\n\n// ==================== 连接类型 ====================\n\n/** 连接类型 */\nexport enum ConnectionType {\n /** 获取端 (CLI/MCP) */\n CONSUMER = 'consumer',\n /** 提供端 (Injector) */\n PROVIDER = 'provider',\n}\n\n// ==================== 消息类型 ====================\n\n/** WebSocket 消息类型 */\nexport enum MessageType {\n // 系统消息\n PING = 'ping',\n PONG = 'pong',\n REGISTER = 'register',\n REGISTER_ACK = 'register_ack',\n\n // 业务消息\n GET_NODE_BY_ID = 'get_node_by_id',\n GET_ALL_NODES = 'get_all_nodes',\n GET_SELECTION = 'get_selection',\n EXPORT_IMAGE = 'export_image',\n EXECUTE_CODE = 'execute_code',\n GET_ALL_PAGES = 'get_all_pages',\n\n // Server 状态消息(本地请求,不转发到 Provider)\n GET_SERVER_STATUS = 'get_server_status',\n\n // 响应\n RESPONSE = 'response',\n ERROR = 'error',\n}\n","/**\n * MG Plugin 错误码定义\n */\n\n/** 错误码枚举 */\nexport enum ErrorCode {\n /** 无法连接到 MG Server */\n CONNECTION_FAILED = 'E001',\n /** 连接超时 */\n CONNECTION_TIMEOUT = 'E002',\n /** 没有 MasterGo 页面连接到 Server */\n NO_PAGE_CONNECTED = 'E003',\n /** 未找到匹配的页面 */\n PAGE_NOT_FOUND = 'E004',\n /** 节点不存在 */\n NODE_NOT_FOUND = 'E005',\n /** 没有选中任何节点 */\n NO_SELECTION = 'E006',\n /** mg 对象不可用 */\n MG_UNAVAILABLE = 'E007',\n /** 导出图片失败 */\n EXPORT_FAILED = 'E008',\n /** 文件写入失败 */\n FILE_WRITE_FAILED = 'E009',\n /** 无效的 mgp:// 链接格式 */\n INVALID_LINK = 'E010',\n /** 参数校验失败 */\n INVALID_PARAMS = 'E011',\n /** 请求超时 */\n REQUEST_TIMEOUT = 'E012',\n /** 所有备选端口均被占用 */\n PORT_EXHAUSTED = 'E013',\n /** 无法发现 Server (端口扫描失败) */\n SERVER_DISCOVERY_FAILED = 'E014',\n /** 自动启动 Server 失败 */\n SERVER_START_FAILED = 'E015',\n /** Server 已在运行中 */\n SERVER_ALREADY_RUNNING = 'E016',\n /** 连接断开 */\n CONNECTION_LOST = 'E017',\n /** 未知错误 */\n UNKNOWN_ERROR = 'E099',\n}\n\n/** 错误码对应的错误名称 */\nexport const ErrorNames: Record<ErrorCode, string> = {\n [ErrorCode.CONNECTION_FAILED]: 'CONNECTION_FAILED',\n [ErrorCode.CONNECTION_TIMEOUT]: 'CONNECTION_TIMEOUT',\n [ErrorCode.NO_PAGE_CONNECTED]: 'NO_PAGE_CONNECTED',\n [ErrorCode.PAGE_NOT_FOUND]: 'PAGE_NOT_FOUND',\n [ErrorCode.NODE_NOT_FOUND]: 'NODE_NOT_FOUND',\n [ErrorCode.NO_SELECTION]: 'NO_SELECTION',\n [ErrorCode.MG_UNAVAILABLE]: 'MG_UNAVAILABLE',\n [ErrorCode.EXPORT_FAILED]: 'EXPORT_FAILED',\n [ErrorCode.FILE_WRITE_FAILED]: 'FILE_WRITE_FAILED',\n [ErrorCode.INVALID_LINK]: 'INVALID_LINK',\n [ErrorCode.INVALID_PARAMS]: 'INVALID_PARAMS',\n [ErrorCode.REQUEST_TIMEOUT]: 'REQUEST_TIMEOUT',\n [ErrorCode.PORT_EXHAUSTED]: 'PORT_EXHAUSTED',\n [ErrorCode.SERVER_DISCOVERY_FAILED]: 'SERVER_DISCOVERY_FAILED',\n [ErrorCode.SERVER_START_FAILED]: 'SERVER_START_FAILED',\n [ErrorCode.SERVER_ALREADY_RUNNING]: 'SERVER_ALREADY_RUNNING',\n [ErrorCode.CONNECTION_LOST]: 'CONNECTION_LOST',\n [ErrorCode.UNKNOWN_ERROR]: 'UNKNOWN_ERROR',\n}\n\n/** 错误码对应的默认消息 */\nexport const ErrorMessages: Record<ErrorCode, string> = {\n [ErrorCode.CONNECTION_FAILED]: '无法连接到 MG Server',\n [ErrorCode.CONNECTION_TIMEOUT]: '连接超时',\n [ErrorCode.NO_PAGE_CONNECTED]: '没有 MasterGo 页面连接到 Server。请安装 MG Plugin 浏览器扩展: https://chromewebstore.google.com/detail/mg-plugin/ddhihanlpcdneicohnglnaliefnkaeja',\n [ErrorCode.PAGE_NOT_FOUND]: '未找到匹配的页面',\n [ErrorCode.NODE_NOT_FOUND]: '节点不存在',\n [ErrorCode.NO_SELECTION]: '没有选中任何节点',\n [ErrorCode.MG_UNAVAILABLE]: 'mg 对象不可用',\n [ErrorCode.EXPORT_FAILED]: '导出图片失败',\n [ErrorCode.FILE_WRITE_FAILED]: '文件写入失败',\n [ErrorCode.INVALID_LINK]: '无效的 mgp:// 链接格式',\n [ErrorCode.INVALID_PARAMS]: '参数校验失败',\n [ErrorCode.REQUEST_TIMEOUT]: '请求超时',\n [ErrorCode.PORT_EXHAUSTED]: '所有备选端口均被占用',\n [ErrorCode.SERVER_DISCOVERY_FAILED]: '无法发现 Server (端口扫描失败)',\n [ErrorCode.SERVER_START_FAILED]: '自动启动 Server 失败',\n [ErrorCode.SERVER_ALREADY_RUNNING]: 'Server 已在运行中',\n [ErrorCode.CONNECTION_LOST]: '连接断开',\n [ErrorCode.UNKNOWN_ERROR]: '未知错误',\n}\n\n/** MG Plugin 错误类 */\nexport class MGError extends Error {\n /** 错误码 */\n code: ErrorCode\n /** 错误名称 */\n errorName: string\n /** 额外详情 */\n details?: Record<string, unknown>\n\n constructor(code: ErrorCode, message?: string, details?: Record<string, unknown>) {\n super(message || ErrorMessages[code])\n this.name = 'MGError'\n this.code = code\n this.errorName = ErrorNames[code]\n this.details = details\n }\n\n /** 转换为 JSON 格式 */\n toJSON() {\n return {\n code: this.code,\n name: this.errorName,\n message: this.message,\n details: this.details,\n }\n }\n\n /** 格式化输出 */\n toString() {\n return `错误 [${this.code}]: ${this.message}`\n }\n}\n\n/**\n * 创建错误\n */\nexport function createError(\n code: ErrorCode,\n message?: string,\n details?: Record<string, unknown>\n): MGError {\n return new MGError(code, message, details)\n}\n","/**\n * WebSocket 服务器\n */\n\nimport { WebSocketServer, WebSocket } from 'ws'\nimport type { IncomingMessage } from 'node:http'\nimport {\n ConnectionType,\n MessageType,\n DEFAULT_PORT,\n PORT_RANGE_START,\n PORT_RANGE_END,\n HEARTBEAT_INTERVAL,\n} from '../shared/constants.js'\nimport { ErrorCode, MGError } from '../shared/errors.js'\nimport type { BaseMessage, RegisterMessage, ResponseMessage, ServerStatusResponse, ConnectedPageInfo } from '../shared/types.js'\nimport { formatDuration } from '../shared/utils.js'\nimport { Logger, createLogger } from './logger.js'\nimport { ConnectionManager, ManagedWebSocket } from './connection-manager.js'\nimport { RequestHandler } from './request-handler.js'\nimport { getVersion } from '../shared/version.js'\n\nexport interface ServerOptions {\n /** 监听端口 */\n port?: number\n /** 日志选项 */\n logger?: Logger\n}\n\n/**\n * MG WebSocket 服务器\n */\nexport class MGServer {\n private wss: WebSocketServer | null = null\n private logger: Logger\n private connectionManager: ConnectionManager\n private requestHandler: RequestHandler\n\n private port: number\n private isRunning = false\n private startedAt: Date | null = null\n\n constructor(options: ServerOptions = {}) {\n this.port = options.port || DEFAULT_PORT\n this.logger = options.logger || createLogger()\n this.connectionManager = new ConnectionManager(this.logger)\n this.requestHandler = new RequestHandler(this.connectionManager, this.logger)\n }\n\n /**\n * 启动服务器\n */\n async start(): Promise<number> {\n if (this.isRunning) {\n throw new MGError(ErrorCode.SERVER_ALREADY_RUNNING, 'Server 已在运行中')\n }\n\n // 尝试绑定端口\n const port = await this.findAvailablePort()\n\n return new Promise((resolve, reject) => {\n this.wss = new WebSocketServer({ port })\n\n this.wss.on('listening', () => {\n this.port = port\n this.isRunning = true\n this.startedAt = new Date()\n this.logger.info(`Server 启动成功,监听端口: ${port}`)\n\n // 启动心跳检查\n this.connectionManager.startHeartbeatCheck(HEARTBEAT_INTERVAL)\n\n resolve(port)\n })\n\n this.wss.on('error', (error: NodeJS.ErrnoException) => {\n this.logger.error('Server 错误:', error)\n reject(error)\n })\n\n this.wss.on('connection', (ws: WebSocket, request: IncomingMessage) => {\n this.handleConnection(ws, request)\n })\n })\n }\n\n /**\n * 查找可用端口\n */\n private async findAvailablePort(): Promise<number> {\n for (let port = PORT_RANGE_START; port <= PORT_RANGE_END; port++) {\n const available = await this.isPortAvailable(port)\n if (available) {\n return port\n }\n this.logger.debug(`端口 ${port} 被占用,尝试下一个`)\n }\n\n throw new MGError(\n ErrorCode.PORT_EXHAUSTED,\n `端口 ${PORT_RANGE_START}-${PORT_RANGE_END} 均被占用`\n )\n }\n\n /**\n * 检查端口是否可用\n */\n private isPortAvailable(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const testServer = new WebSocketServer({ port })\n\n testServer.on('listening', () => {\n testServer.close()\n resolve(true)\n })\n\n testServer.on('error', () => {\n resolve(false)\n })\n })\n }\n\n /**\n * 处理新连接\n */\n private handleConnection(ws: WebSocket, _request: IncomingMessage): void {\n this.logger.info('新连接建立')\n\n // 等待注册消息\n const registerTimeout = setTimeout(() => {\n this.logger.warn('连接注册超时,关闭连接')\n ws.close()\n }, 5000)\n\n ws.on('message', (data) => {\n try {\n const message = JSON.parse(data.toString()) as BaseMessage\n\n // 处理注册消息\n if (message.type === MessageType.REGISTER) {\n clearTimeout(registerTimeout)\n this.handleRegister(ws, message as RegisterMessage)\n return\n }\n\n // 其他消息需要已注册\n const managedWs = ws as ManagedWebSocket\n if (!managedWs.connectionId) {\n this.logger.warn('未注册的连接发送消息,忽略')\n return\n }\n\n this.handleMessage(managedWs, message)\n } catch (error) {\n this.logger.error('消息解析失败:', error)\n }\n })\n\n ws.on('close', () => {\n clearTimeout(registerTimeout)\n const managedWs = ws as ManagedWebSocket\n if (managedWs.connectionId) {\n this.requestHandler.cleanupConnectionRequests(managedWs.connectionId)\n this.connectionManager.removeConnection(managedWs)\n }\n })\n\n ws.on('error', (error) => {\n this.logger.error('WebSocket 错误:', error)\n })\n }\n\n /**\n * 处理注册消息\n */\n private handleRegister(ws: WebSocket, message: RegisterMessage): void {\n const { connectionType, pageUrl, pageId } = message.data\n\n const managedWs = this.connectionManager.addConnection(\n ws,\n connectionType,\n pageUrl,\n pageId\n )\n\n // 发送注册确认(包含 Server 版本信息)\n const ack: ResponseMessage = {\n id: message.id || '',\n type: MessageType.REGISTER_ACK,\n success: true,\n data: {\n connectionId: managedWs.connectionId,\n pageUrl,\n serverVersion: getVersion(),\n },\n }\n\n ws.send(JSON.stringify(ack))\n }\n\n /**\n * 处理消息\n */\n private handleMessage(ws: ManagedWebSocket, message: BaseMessage): void {\n this.connectionManager.updateLastActive(ws)\n\n switch (message.type) {\n case MessageType.PING:\n this.handlePing(ws, message)\n break\n\n case MessageType.GET_SERVER_STATUS:\n // 直接处理 Server 状态查询,不转发给 Provider\n this.handleGetServerStatus(ws, message)\n break\n\n case MessageType.RESPONSE:\n case MessageType.ERROR:\n // Provider 返回的响应\n this.requestHandler.handleResponse(message as ResponseMessage)\n break\n\n default:\n // Consumer 发送的请求\n if (ws.connectionInfo.type === ConnectionType.CONSUMER) {\n this.requestHandler.handleRequest(ws, message as any)\n }\n break\n }\n }\n\n /**\n * 处理心跳\n */\n private handlePing(ws: ManagedWebSocket, message: BaseMessage): void {\n const pong = {\n type: MessageType.PONG,\n timestamp: (message as any).timestamp || Date.now(),\n }\n ws.send(JSON.stringify(pong))\n }\n\n /**\n * 处理 Server 状态查询\n */\n private handleGetServerStatus(ws: ManagedWebSocket, message: BaseMessage): void {\n const providers = this.connectionManager.getAllProviders()\n const stats = this.connectionManager.getStats()\n\n // 构建连接页面信息\n const connectedPages: ConnectedPageInfo[] = providers.map((info) => ({\n pageUrl: info.pageUrl || '',\n connectedAt: info.connectedAt.toISOString(),\n lastActiveAt: info.lastActiveAt.toISOString(),\n }))\n\n const uptimeMs = this.startedAt ? Date.now() - this.startedAt.getTime() : 0\n\n const statusData: ServerStatusResponse = {\n running: this.isRunning,\n port: this.port,\n pid: process.pid,\n startedAt: this.startedAt?.toISOString() || '',\n uptime: formatDuration(uptimeMs),\n version: getVersion(),\n stats,\n connectedPages,\n }\n\n const response: ResponseMessage = {\n id: message.id || '',\n type: MessageType.RESPONSE,\n success: true,\n data: statusData,\n }\n\n ws.send(JSON.stringify(response))\n this.logger.info('返回 Server 状态信息')\n }\n\n /**\n * 停止服务器\n */\n async stop(): Promise<void> {\n if (!this.isRunning || !this.wss) {\n return\n }\n\n this.logger.info('正在停止 Server...')\n\n // 清理所有请求\n this.requestHandler.cleanupAll()\n\n // 关闭所有连接\n this.connectionManager.closeAll()\n\n // 关闭服务器\n return new Promise((resolve) => {\n this.wss!.close(() => {\n this.isRunning = false\n this.wss = null\n this.logger.info('Server 已停止')\n resolve()\n })\n })\n }\n\n /**\n * 获取运行状态\n */\n getStatus(): {\n running: boolean\n port: number\n stats: { providers: number; consumers: number; total: number }\n connectedPages: string[]\n } {\n return {\n running: this.isRunning,\n port: this.port,\n stats: this.connectionManager.getStats(),\n connectedPages: this.connectionManager.getConnectedPageUrls(),\n }\n }\n\n /**\n * 获取端口\n */\n getPort(): number {\n return this.port\n }\n\n /**\n * 是否运行中\n */\n isServerRunning(): boolean {\n return this.isRunning\n }\n}\n\n/**\n * 创建服务器实例\n */\nexport function createServer(options?: ServerOptions): MGServer {\n return new MGServer(options)\n}\n","/**\n * 日志模块\n */\n\nimport { appendFileSync, existsSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { SERVER_LOG_FILE } from '../shared/constants.js'\nimport { formatLogTime } from '../shared/utils.js'\n\nexport enum LogLevel {\n DEBUG = 'DEBUG',\n INFO = 'INFO',\n WARN = 'WARN',\n ERROR = 'ERROR',\n}\n\nexport interface LoggerOptions {\n /** 是否输出到控制台 */\n console?: boolean\n /** 是否输出到文件 */\n file?: boolean\n /** 日志文件路径 */\n filePath?: string\n /** 最小日志级别 */\n minLevel?: LogLevel\n}\n\nconst levelPriority: Record<LogLevel, number> = {\n [LogLevel.DEBUG]: 0,\n [LogLevel.INFO]: 1,\n [LogLevel.WARN]: 2,\n [LogLevel.ERROR]: 3,\n}\n\n/**\n * 日志记录器\n */\nexport class Logger {\n private options: Required<LoggerOptions>\n\n constructor(options: LoggerOptions = {}) {\n this.options = {\n console: options.console ?? true,\n file: options.file ?? false,\n filePath: options.filePath ?? SERVER_LOG_FILE,\n minLevel: options.minLevel ?? LogLevel.INFO,\n }\n\n // 确保日志目录存在\n if (this.options.file) {\n const dir = dirname(this.options.filePath)\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n }\n }\n\n /**\n * 记录日志\n */\n private log(level: LogLevel, message: string, ...args: unknown[]): void {\n // 检查日志级别\n if (levelPriority[level] < levelPriority[this.options.minLevel]) {\n return\n }\n\n const timestamp = formatLogTime()\n const formattedMessage = `[${timestamp}] [${level}] ${message}`\n\n // 输出到控制台\n if (this.options.console) {\n const consoleMethod = level === LogLevel.ERROR ? console.error : console.log\n if (args.length > 0) {\n consoleMethod(formattedMessage, ...args)\n } else {\n consoleMethod(formattedMessage)\n }\n }\n\n // 输出到文件\n if (this.options.file) {\n try {\n const fileMessage =\n args.length > 0\n ? `${formattedMessage} ${JSON.stringify(args)}\\n`\n : `${formattedMessage}\\n`\n appendFileSync(this.options.filePath, fileMessage)\n } catch (error) {\n // 文件写入失败时静默处理\n if (this.options.console) {\n console.error('日志文件写入失败:', error)\n }\n }\n }\n }\n\n debug(message: string, ...args: unknown[]): void {\n this.log(LogLevel.DEBUG, message, ...args)\n }\n\n info(message: string, ...args: unknown[]): void {\n this.log(LogLevel.INFO, message, ...args)\n }\n\n warn(message: string, ...args: unknown[]): void {\n this.log(LogLevel.WARN, message, ...args)\n }\n\n error(message: string, ...args: unknown[]): void {\n this.log(LogLevel.ERROR, message, ...args)\n }\n}\n\n/** 创建默认日志记录器 */\nexport function createLogger(options?: LoggerOptions): Logger {\n return new Logger(options)\n}\n","/**\n * 连接管理器\n * 管理 Provider 和 Consumer 的 WebSocket 连接\n */\n\nimport type { WebSocket } from 'ws'\nimport { ConnectionType, HEARTBEAT_TIMEOUT } from '../shared/constants.js'\nimport type { ConnectionInfo } from '../shared/types.js'\nimport { generateId } from '../shared/utils.js'\nimport { Logger } from './logger.js'\n\n/** 带连接信息的 WebSocket */\nexport interface ManagedWebSocket extends WebSocket {\n /** 连接 ID */\n connectionId: string\n /** 连接信息 */\n connectionInfo: ConnectionInfo\n /** 是否存活 */\n isAlive: boolean\n}\n\n/**\n * 连接管理器\n */\nexport class ConnectionManager {\n private logger: Logger\n\n /** Provider 连接(按页面 URL 索引) */\n private providers = new Map<string, ManagedWebSocket>()\n\n /** Consumer 连接 */\n private consumers = new Map<string, ManagedWebSocket>()\n\n /** 所有连接(按 ID 索引) */\n private allConnections = new Map<string, ManagedWebSocket>()\n\n /** 心跳检查定时器 */\n private heartbeatTimer: NodeJS.Timeout | null = null\n\n constructor(logger: Logger) {\n this.logger = logger\n }\n\n /**\n * 启动心跳检查\n */\n startHeartbeatCheck(interval: number = 30000): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n }\n\n this.heartbeatTimer = setInterval(() => {\n this.checkHeartbeats()\n }, interval)\n }\n\n /**\n * 停止心跳检查\n */\n stopHeartbeatCheck(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = null\n }\n }\n\n /**\n * 检查所有连接的心跳\n */\n private checkHeartbeats(): void {\n const now = Date.now()\n\n for (const [id, ws] of this.allConnections) {\n const lastActive = ws.connectionInfo.lastActiveAt.getTime()\n const elapsed = now - lastActive\n\n if (elapsed > HEARTBEAT_TIMEOUT) {\n this.logger.warn(`连接 ${id} 心跳超时,关闭连接`)\n this.removeConnection(ws)\n ws.terminate()\n }\n }\n }\n\n /**\n * 添加连接\n */\n addConnection(\n ws: WebSocket,\n type: ConnectionType,\n pageUrl?: string,\n pageId?: string\n ): ManagedWebSocket {\n const connectionId = generateId()\n const now = new Date()\n\n const connectionInfo: ConnectionInfo = {\n id: connectionId,\n type,\n pageUrl,\n pageId,\n connectedAt: now,\n lastActiveAt: now,\n }\n\n const managedWs = ws as ManagedWebSocket\n managedWs.connectionId = connectionId\n managedWs.connectionInfo = connectionInfo\n managedWs.isAlive = true\n\n this.allConnections.set(connectionId, managedWs)\n\n if (type === ConnectionType.PROVIDER && pageUrl) {\n // 如果已有同页面的连接,先移除旧的\n const existing = this.providers.get(pageUrl)\n if (existing) {\n this.logger.info(`页面 ${pageUrl} 已有连接,替换为新连接`)\n this.removeConnection(existing)\n existing.close()\n }\n this.providers.set(pageUrl, managedWs)\n this.logger.info(`Provider 连接: ${pageUrl}`)\n } else if (type === ConnectionType.CONSUMER) {\n this.consumers.set(connectionId, managedWs)\n this.logger.info(`Consumer 连接: ${connectionId}`)\n }\n\n return managedWs\n }\n\n /**\n * 移除连接\n */\n removeConnection(ws: ManagedWebSocket): void {\n const { connectionId, connectionInfo } = ws\n\n this.allConnections.delete(connectionId)\n\n if (connectionInfo.type === ConnectionType.PROVIDER && connectionInfo.pageUrl) {\n this.providers.delete(connectionInfo.pageUrl)\n this.logger.info(`Provider 断开: ${connectionInfo.pageUrl}`)\n } else if (connectionInfo.type === ConnectionType.CONSUMER) {\n this.consumers.delete(connectionId)\n this.logger.info(`Consumer 断开: ${connectionId}`)\n }\n }\n\n /**\n * 更新连接活跃时间\n */\n updateLastActive(ws: ManagedWebSocket): void {\n ws.connectionInfo.lastActiveAt = new Date()\n ws.isAlive = true\n }\n\n /**\n * 根据页面 URL 查找 Provider\n */\n findProviderByPageUrl(pageUrl: string): ManagedWebSocket | undefined {\n return this.providers.get(pageUrl)\n }\n\n /**\n * 获取第一个可用的 Provider\n */\n getFirstProvider(): ManagedWebSocket | undefined {\n const iterator = this.providers.values()\n const first = iterator.next()\n return first.value\n }\n\n /**\n * 获取所有 Provider 信息\n */\n getAllProviders(): ConnectionInfo[] {\n return Array.from(this.providers.values()).map((ws) => ws.connectionInfo)\n }\n\n /**\n * 获取连接统计\n */\n getStats(): { providers: number; consumers: number; total: number } {\n return {\n providers: this.providers.size,\n consumers: this.consumers.size,\n total: this.allConnections.size,\n }\n }\n\n /**\n * 获取所有已连接的页面 URL\n */\n getConnectedPageUrls(): string[] {\n return Array.from(this.providers.keys())\n }\n\n /**\n * 关闭所有连接\n */\n closeAll(): void {\n this.stopHeartbeatCheck()\n\n for (const ws of this.allConnections.values()) {\n ws.close()\n }\n\n this.providers.clear()\n this.consumers.clear()\n this.allConnections.clear()\n }\n}\n","/**\n * 请求处理器\n * 处理 Consumer 请求,转发给 Provider\n */\n\nimport type { ManagedWebSocket } from './connection-manager.js'\nimport { ConnectionManager } from './connection-manager.js'\nimport { Logger } from './logger.js'\nimport { MessageType, REQUEST_TIMEOUT } from '../shared/constants.js'\nimport { ErrorCode, MGError, ErrorMessages } from '../shared/errors.js'\nimport type { RequestMessage, ResponseMessage } from '../shared/types.js'\nimport { generateId } from '../shared/utils.js'\n\n/** 待处理的请求 */\ninterface PendingRequest {\n /** 请求 ID */\n id: string\n /** Consumer WebSocket */\n consumer: ManagedWebSocket\n /** 超时定时器 */\n timer: NodeJS.Timeout\n /** 请求时间 */\n timestamp: number\n}\n\n/**\n * 请求处理器\n */\nexport class RequestHandler {\n private logger: Logger\n private connectionManager: ConnectionManager\n\n /** 待处理的请求 */\n private pendingRequests = new Map<string, PendingRequest>()\n\n constructor(connectionManager: ConnectionManager, logger: Logger) {\n this.connectionManager = connectionManager\n this.logger = logger\n }\n\n /**\n * 处理 Consumer 请求\n */\n async handleRequest(consumer: ManagedWebSocket, message: RequestMessage): Promise<void> {\n const requestId = message.id || generateId()\n const { type, pageUrl, params } = message\n\n this.logger.info(`收到请求: ${type} (${requestId})`, { pageUrl })\n\n // 查找目标 Provider\n let provider: ManagedWebSocket | undefined\n\n if (pageUrl) {\n provider = this.connectionManager.findProviderByPageUrl(pageUrl)\n if (!provider) {\n this.sendError(consumer, requestId, ErrorCode.PAGE_NOT_FOUND, `未找到页面: ${pageUrl}`)\n return\n }\n } else {\n // 没有指定 pageUrl,使用第一个可用的 Provider\n provider = this.connectionManager.getFirstProvider()\n if (!provider) {\n this.sendError(consumer, requestId, ErrorCode.NO_PAGE_CONNECTED, ErrorMessages[ErrorCode.NO_PAGE_CONNECTED])\n return\n }\n }\n\n // 创建超时定时器\n const timer = setTimeout(() => {\n this.handleTimeout(requestId)\n }, REQUEST_TIMEOUT)\n\n // 保存待处理请求\n this.pendingRequests.set(requestId, {\n id: requestId,\n consumer,\n timer,\n timestamp: Date.now(),\n })\n\n // 转发请求给 Provider\n const forwardMessage: RequestMessage = {\n id: requestId,\n type,\n pageUrl: pageUrl || provider.connectionInfo.pageUrl,\n params,\n timestamp: Date.now(),\n }\n\n try {\n provider.send(JSON.stringify(forwardMessage))\n this.logger.info(`请求转发: ${type} -> ${provider.connectionInfo.pageUrl}`)\n } catch {\n this.cleanupRequest(requestId)\n this.sendError(consumer, requestId, ErrorCode.CONNECTION_FAILED, '转发请求失败')\n }\n }\n\n /**\n * 处理 Provider 响应\n */\n handleResponse(response: ResponseMessage): void {\n const { id } = response\n const pending = this.pendingRequests.get(id)\n\n if (!pending) {\n this.logger.warn(`收到未知请求的响应: ${id}`)\n return\n }\n\n this.cleanupRequest(id)\n\n // 转发响应给 Consumer\n try {\n pending.consumer.send(JSON.stringify(response))\n this.logger.info(\n `响应返回: ${id} (${response.success ? '成功' : '失败'})`\n )\n } catch (error) {\n this.logger.error(`响应转发失败: ${id}`, error)\n }\n }\n\n /**\n * 处理请求超时\n */\n private handleTimeout(requestId: string): void {\n const pending = this.pendingRequests.get(requestId)\n if (!pending) return\n\n this.logger.warn(`请求超时: ${requestId}`)\n this.cleanupRequest(requestId)\n\n this.sendError(pending.consumer, requestId, ErrorCode.REQUEST_TIMEOUT, '请求超时')\n }\n\n /**\n * 清理请求\n */\n private cleanupRequest(requestId: string): void {\n const pending = this.pendingRequests.get(requestId)\n if (pending) {\n clearTimeout(pending.timer)\n this.pendingRequests.delete(requestId)\n }\n }\n\n /**\n * 发送错误响应\n */\n private sendError(\n consumer: ManagedWebSocket,\n requestId: string,\n code: ErrorCode,\n message: string\n ): void {\n const error = new MGError(code, message)\n const response: ResponseMessage = {\n id: requestId,\n type: MessageType.ERROR,\n success: false,\n data: null,\n error: error.toJSON(),\n }\n\n try {\n consumer.send(JSON.stringify(response))\n } catch (err) {\n this.logger.error(`发送错误响应失败: ${requestId}`, err)\n }\n }\n\n /**\n * 清理特定连接的所有待处理请求\n */\n cleanupConnectionRequests(connectionId: string): void {\n for (const [requestId, pending] of this.pendingRequests) {\n if (pending.consumer.connectionId === connectionId) {\n this.cleanupRequest(requestId)\n }\n }\n }\n\n /**\n * 清理所有待处理请求\n */\n cleanupAll(): void {\n for (const [requestId] of this.pendingRequests) {\n this.cleanupRequest(requestId)\n }\n }\n}\n","/**\n * 版本管理模块\n * 提供获取当前 CLI 版本的函数\n * 优先从 VERSION 文件读取版本号\n */\n\nimport { readFileSync, existsSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { dirname, join } from 'node:path'\n\n/** 缓存的版本号 */\nlet cachedVersion: string | null = null\n\n/**\n * 获取当前 CLI 版本号\n * 优先从 VERSION 文件读取,其次从 package.json 读取\n */\nexport function getVersion(): string {\n if (cachedVersion !== null) {\n return cachedVersion\n }\n\n try {\n // 获取当前模块的目录\n const currentFile = fileURLToPath(import.meta.url)\n const currentDir = dirname(currentFile)\n\n // 优先尝试读取 VERSION 文件\n // tsup 打包后: dist/xxx.js -> ../VERSION\n // 源码开发时: src/shared/version.ts -> ../../VERSION\n const versionFilePaths = [\n join(currentDir, '..', 'VERSION'), // dist/xxx.js -> ../VERSION\n join(currentDir, '..', '..', 'VERSION'), // src/shared/version.ts -> ../../VERSION\n ]\n\n for (const versionFilePath of versionFilePaths) {\n if (existsSync(versionFilePath)) {\n const version = readFileSync(versionFilePath, 'utf-8').trim()\n if (version) {\n cachedVersion = version\n return cachedVersion\n }\n }\n }\n\n // 回退: 从 package.json 读取(兼容旧逻辑)\n const packageJsonPaths = [\n join(currentDir, '..', 'package.json'),\n join(currentDir, '..', '..', 'package.json'),\n ]\n\n for (const packageJsonPath of packageJsonPaths) {\n if (existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))\n if (packageJson.name === '@hangox/mg-cli') {\n cachedVersion = (packageJson.version as string) || '0.0.0'\n return cachedVersion\n }\n }\n }\n\n // 如果都找不到,返回默认版本\n cachedVersion = '0.0.0'\n return cachedVersion\n } catch {\n // 如果读取失败,返回默认版本\n cachedVersion = '0.0.0'\n return cachedVersion\n }\n}\n\n/**\n * 比较两个版本是否一致\n * 使用精确匹配,简单可靠\n */\nexport function isVersionMatch(version1: string, version2: string): boolean {\n return version1 === version2\n}\n\n/**\n * 清除缓存的版本号(用于测试)\n */\nexport function clearVersionCache(): void {\n cachedVersion = null\n}\n","/**\n * CLI 客户端\n * 负责连接 Server 发送请求\n */\n\nimport WebSocket from 'ws'\nimport { readServerInfo, parseMgpLink, isProcessRunning } from '../shared/utils.js'\nimport {\n PORT_RANGE_START,\n PORT_RANGE_END,\n PORT_SCAN_TIMEOUT,\n REQUEST_TIMEOUT,\n SERVER_START_TIMEOUT,\n RETRY_INTERVALS,\n MAX_RETRY_COUNT,\n ConnectionType,\n MessageType,\n} from '../shared/constants.js'\nimport { ErrorCode, MGError, ErrorMessages } from '../shared/errors.js'\nimport { startServerDaemon, restartServer } from '../server/daemon.js'\nimport { generateId } from '../shared/utils.js'\nimport { getVersion, isVersionMatch } from '../shared/version.js'\nimport type { RequestMessage, ResponseMessage } from '../shared/types.js'\n\n/** 客户端选项 */\nexport interface ClientOptions {\n /** 禁用自动启动 Server */\n noAutoStart?: boolean\n /** 禁用重试 */\n noRetry?: boolean\n}\n\n/**\n * MG CLI 客户端\n */\nexport class MGClient {\n private ws: WebSocket | null = null\n private options: ClientOptions\n\n constructor(options: ClientOptions = {}) {\n this.options = options\n }\n\n /**\n * 连接到 Server\n */\n async connect(): Promise<void> {\n // 1. 尝试从文件读取端口和版本信息\n const serverInfo = readServerInfo()\n if (serverInfo) {\n // 检查进程是否存在\n if (isProcessRunning(serverInfo.pid)) {\n // 检查版本是否匹配\n const currentVersion = getVersion()\n if (!isVersionMatch(currentVersion, serverInfo.version)) {\n // 版本不一致,需要重启 Server\n console.log(`版本不匹配: CLI ${currentVersion} vs Server ${serverInfo.version}`)\n console.log('正在重启 Server 以对齐版本...')\n try {\n const newInfo = await restartServer(serverInfo.port)\n console.log(`Server 已重启,版本: ${newInfo.version}`)\n await this.waitForServer(newInfo.port)\n return\n } catch (error) {\n throw new MGError(\n ErrorCode.SERVER_START_FAILED,\n `重启 Server 失败: ${error instanceof Error ? error.message : error}`\n )\n }\n }\n\n // 版本一致,尝试连接\n try {\n await this.tryConnect(serverInfo.port)\n return\n } catch {\n // 连接失败,继续扫描\n }\n }\n }\n\n // 2. 端口扫描\n for (let port = PORT_RANGE_START; port <= PORT_RANGE_END; port++) {\n try {\n await this.tryConnect(port)\n return\n } catch {\n // 继续尝试下一个端口\n }\n }\n\n // 3. 自动启动 Server\n if (!this.options.noAutoStart) {\n console.log('Server 未运行,正在自动启动...')\n try {\n const info = await startServerDaemon()\n console.log(`Server 已启动,端口: ${info.port}`)\n\n // 等待 Server 就绪\n await this.waitForServer(info.port)\n return\n } catch (error) {\n throw new MGError(\n ErrorCode.SERVER_START_FAILED,\n `自动启动 Server 失败: ${error instanceof Error ? error.message : error}`\n )\n }\n }\n\n throw new MGError(ErrorCode.CONNECTION_FAILED, ErrorMessages[ErrorCode.CONNECTION_FAILED])\n }\n\n /**\n * 尝试连接指定端口\n */\n private tryConnect(port: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(`ws://localhost:${port}`)\n const timer = setTimeout(() => {\n ws.close()\n reject(new Error('连接超时'))\n }, PORT_SCAN_TIMEOUT)\n\n ws.on('open', () => {\n clearTimeout(timer)\n this.ws = ws\n // 注册为 Consumer\n this.register()\n resolve()\n })\n\n ws.on('error', (error) => {\n clearTimeout(timer)\n reject(error)\n })\n })\n }\n\n /**\n * 等待 Server 就绪\n */\n private async waitForServer(port: number): Promise<void> {\n const startTime = Date.now()\n const interval = 500\n\n while (Date.now() - startTime < SERVER_START_TIMEOUT) {\n try {\n await this.tryConnect(port)\n return\n } catch {\n await new Promise((r) => setTimeout(r, interval))\n }\n }\n\n throw new Error('等待 Server 启动超时')\n }\n\n /**\n * 注册为 Consumer\n */\n private register(): void {\n if (!this.ws) return\n\n const message = {\n type: MessageType.REGISTER,\n data: {\n connectionType: ConnectionType.CONSUMER,\n },\n timestamp: Date.now(),\n }\n\n this.ws.send(JSON.stringify(message))\n }\n\n /**\n * 发送请求并等待响应\n */\n async request<T = unknown>(\n type: MessageType,\n params?: Record<string, unknown>,\n pageUrl?: string\n ): Promise<T> {\n if (!this.ws) {\n throw new MGError(ErrorCode.CONNECTION_FAILED, '未连接到 Server')\n }\n\n const requestId = generateId()\n const message: RequestMessage = {\n id: requestId,\n type,\n params,\n pageUrl,\n timestamp: Date.now(),\n }\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new MGError(ErrorCode.REQUEST_TIMEOUT, ErrorMessages[ErrorCode.REQUEST_TIMEOUT]))\n }, REQUEST_TIMEOUT)\n\n const messageHandler = (data: WebSocket.Data) => {\n try {\n const response = JSON.parse(data.toString()) as ResponseMessage\n if (response.id === requestId) {\n clearTimeout(timer)\n this.ws?.off('message', messageHandler)\n\n if (response.success) {\n resolve(response.data as T)\n } else {\n const error = response.error\n reject(\n new MGError(\n error?.code || ErrorCode.UNKNOWN_ERROR,\n error?.message || '未知错误'\n )\n )\n }\n }\n } catch {\n // 忽略解析错误\n }\n }\n\n this.ws!.on('message', messageHandler)\n this.ws!.send(JSON.stringify(message))\n })\n }\n\n /**\n * 带重试的请求\n */\n async requestWithRetry<T = unknown>(\n type: MessageType,\n params?: Record<string, unknown>,\n pageUrl?: string\n ): Promise<T> {\n if (this.options.noRetry) {\n return this.request<T>(type, params, pageUrl)\n }\n\n let lastError: Error | null = null\n\n for (let attempt = 0; attempt <= MAX_RETRY_COUNT; attempt++) {\n try {\n return await this.request<T>(type, params, pageUrl)\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error))\n\n // 检查是否是可重试的错误\n if (error instanceof MGError) {\n const retryable = [ErrorCode.CONNECTION_LOST, ErrorCode.REQUEST_TIMEOUT]\n if (!retryable.includes(error.code)) {\n throw error\n }\n }\n\n // 最后一次尝试不再等待\n if (attempt < MAX_RETRY_COUNT) {\n const delay = RETRY_INTERVALS[attempt] || RETRY_INTERVALS[RETRY_INTERVALS.length - 1]\n await new Promise((r) => setTimeout(r, delay))\n\n // 重新连接\n try {\n await this.connect()\n } catch {\n // 重连失败,继续重试\n }\n }\n }\n }\n\n throw lastError || new MGError(ErrorCode.UNKNOWN_ERROR, '请求失败')\n }\n\n /**\n * 关闭连接\n */\n close(): void {\n if (this.ws) {\n this.ws.close()\n this.ws = null\n }\n }\n}\n\n/**\n * 解析 mgp:// 链接\n */\nexport { parseMgpLink }\n","/**\n * get_node_by_id 命令\n * 根据节点 ID 获取节点详细信息\n */\n\nimport { Command } from 'commander'\nimport { writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient } from '../client.js'\nimport { trimNodeDefaults } from '../../shared/utils.js'\nimport type { GetNodeParams, NodeInfo } from '../../shared/types.js'\n\ninterface GetNodeByIdOptions {\n nodeId: string\n output: string\n domain?: string\n fileId?: string\n maxDepth?: string\n includeInvisible?: boolean\n raw?: boolean\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/**\n * 创建 get_node_by_id 命令\n */\nexport function createGetNodeByIdCommand(): Command {\n return new Command('get_node_by_id')\n .description('根据节点 ID 获取节点详细信息。数据保存到指定 JSON 文件,返回文件路径和大小信息。如需通过链接获取,请使用 get_node_by_link 命令')\n .requiredOption('--nodeId <id>', '节点 ID,格式如 123:456。可从 MasterGo 浮窗链接中获取')\n .requiredOption('--output <path>', '输出 JSON 文件路径。支持绝对路径或相对路径')\n .option('--domain <domain>', 'MasterGo 域名,默认 mastergo.netease.com。与 --fileId 配合使用', 'mastergo.netease.com')\n .option('--fileId <id>', '文件 ID(纯数字),与 --domain 配合指定目标页面')\n .option('--maxDepth <number>', '遍历深度,默认 1。增加深度会显著增加数据量', '1')\n .option('--includeInvisible', '包含不可见节点(visible: false),默认不包含', false)\n .option('--raw', '保留原始数据,不精简默认值字段', false)\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (options: GetNodeByIdOptions) => {\n await handleGetNodeById(options)\n })\n}\n\n/**\n * 处理 get_node_by_id 命令\n */\nasync function handleGetNodeById(options: GetNodeByIdOptions): Promise<void> {\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n // 构建 pageUrl(用于精确指定目标页面)\n let pageUrl: string | undefined\n if (options.fileId) {\n // 使用 fileId 和 domain 构建 pageUrl\n const domain = options.domain || 'mastergo.netease.com'\n pageUrl = `${domain}/file/${options.fileId}`\n }\n\n // 发送请求\n const params: GetNodeParams = {\n nodeId: options.nodeId,\n maxDepth: parseInt(options.maxDepth || '1', 10),\n includeInvisible: options.includeInvisible || false,\n }\n\n const data = await client.requestWithRetry<NodeInfo>(MessageType.GET_NODE_BY_ID, params, pageUrl)\n\n // 精简数据(除非使用 --raw 选项)\n const outputData = options.raw ? data : trimNodeDefaults(data as Record<string, unknown>)\n\n // 保存到文件\n const outputPath = resolve(options.output)\n const outputDir = dirname(outputPath)\n mkdirSync(outputDir, { recursive: true })\n\n const jsonContent = JSON.stringify(outputData, null, 2)\n writeFileSync(outputPath, jsonContent, 'utf-8')\n\n // 输出结果\n const size = jsonContent.length\n const sizeKB = (size / 1024).toFixed(2)\n console.log(`文件路径: ${outputPath}`)\n console.log(`节点 ID: ${options.nodeId}`)\n console.log(`数据大小: ${size.toLocaleString()} 字符 (约 ${sizeKB} KB)`)\n console.log(`节点深度: ${params.maxDepth}`)\n if (!options.raw) {\n console.log(`数据模式: 精简模式 (使用 --raw 获取完整数据)`)\n }\n } catch (error) {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n process.exit(1)\n } finally {\n client.close()\n }\n}\n","/**\n * get_node_by_link 命令\n * 解析 mgp:// 协议链接并获取节点信息\n */\n\nimport { Command } from 'commander'\nimport { writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient, parseMgpLink } from '../client.js'\nimport { ErrorCode, MGError } from '../../shared/errors.js'\nimport { trimNodeDefaults } from '../../shared/utils.js'\nimport type { GetNodeParams, NodeInfo } from '../../shared/types.js'\n\ninterface GetNodeByLinkOptions {\n link: string\n output: string\n maxDepth?: string\n includeInvisible?: boolean\n raw?: boolean\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/**\n * 创建 get_node_by_link 命令\n */\nexport function createGetNodeByLinkCommand(): Command {\n return new Command('get_node_by_link')\n .description('解析 mgp:// 协议链接并获取节点信息')\n .requiredOption('--link <url>', 'mgp:// 协议链接')\n .requiredOption('--output <path>', '输出 JSON 文件路径')\n .option('--maxDepth <number>', '遍历深度', '1')\n .option('--includeInvisible', '包含不可见节点', false)\n .option('--raw', '保留原始数据,不精简默认值字段', false)\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (options: GetNodeByLinkOptions) => {\n await handleGetNodeByLink(options)\n })\n}\n\n/**\n * 处理 get_node_by_link 命令\n */\nasync function handleGetNodeByLink(options: GetNodeByLinkOptions): Promise<void> {\n // 解析 mgp:// 链接\n const parsed = parseMgpLink(options.link)\n if (!parsed) {\n console.error(`错误 [${ErrorCode.INVALID_LINK}]: 无效的 mgp:// 链接格式`)\n console.error(`提供的链接: ${options.link}`)\n console.error(`期望格式: mgp://[mastergo_page_url]/nodeId`)\n process.exit(1)\n }\n\n const { pageUrl, nodeId } = parsed\n\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n // 发送请求\n const params: GetNodeParams = {\n nodeId,\n maxDepth: parseInt(options.maxDepth || '1', 10),\n includeInvisible: options.includeInvisible || false,\n }\n\n const data = await client.requestWithRetry<NodeInfo>(\n MessageType.GET_NODE_BY_ID,\n params,\n pageUrl\n )\n\n // 精简数据(除非使用 --raw 选项)\n const outputData = options.raw ? data : trimNodeDefaults(data as Record<string, unknown>)\n\n // 保存到文件\n const outputPath = resolve(options.output)\n const outputDir = dirname(outputPath)\n mkdirSync(outputDir, { recursive: true })\n\n const jsonContent = JSON.stringify(outputData, null, 2)\n writeFileSync(outputPath, jsonContent, 'utf-8')\n\n // 输出结果\n const size = jsonContent.length\n const sizeKB = (size / 1024).toFixed(2)\n console.log(`文件路径: ${outputPath}`)\n console.log(`Link: ${options.link}`)\n console.log(`页面 URL: ${pageUrl}`)\n console.log(`节点 ID: ${nodeId}`)\n console.log(`数据大小: ${size.toLocaleString()} 字符 (约 ${sizeKB} KB)`)\n console.log(`节点深度: ${params.maxDepth}`)\n if (!options.raw) {\n console.log(`数据模式: 精简模式 (使用 --raw 获取完整数据)`)\n }\n } catch (error) {\n if (error instanceof MGError) {\n console.error(`错误 [${error.code}]: ${error.message}`)\n } else {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n }\n process.exit(1)\n } finally {\n client.close()\n }\n}\n","/**\n * get_all_nodes 命令\n * 获取当前页面的所有节点树\n */\n\nimport { Command } from 'commander'\nimport { writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient } from '../client.js'\nimport { trimNodeDefaults } from '../../shared/utils.js'\nimport type { GetAllNodesParams, NodeInfo } from '../../shared/types.js'\n\ninterface GetAllNodesOptions {\n output: string\n maxDepth?: string\n includeInvisible?: boolean\n raw?: boolean\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/**\n * 创建 get_all_nodes 命令\n */\nexport function createGetAllNodesCommand(): Command {\n return new Command('get_all_nodes')\n .description('获取当前页面的所有节点树。警告:深度每增加 1,数据量可能呈指数级增长。建议从 maxDepth=1 开始')\n .requiredOption('--output <path>', '输出 JSON 文件路径。支持绝对路径或相对路径')\n .option('--maxDepth <number>', '最大深度,默认 1。深度 2 可能产生 100KB-500KB,深度 3 可能超过 1MB', '1')\n .option('--includeInvisible', '包含不可见节点(visible: false),默认不包含', false)\n .option('--raw', '保留原始数据,不精简默认值字段', false)\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (options: GetAllNodesOptions) => {\n await handleGetAllNodes(options)\n })\n}\n\n/**\n * 处理 get_all_nodes 命令\n */\nasync function handleGetAllNodes(options: GetAllNodesOptions): Promise<void> {\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n // 发送请求\n const params: GetAllNodesParams = {\n maxDepth: parseInt(options.maxDepth || '1', 10),\n includeInvisible: options.includeInvisible || false,\n }\n\n const data = await client.requestWithRetry<NodeInfo[]>(MessageType.GET_ALL_NODES, params)\n\n // 精简数据(除非使用 --raw 选项)\n const outputData = options.raw\n ? data\n : Array.isArray(data)\n ? data.map((node) => trimNodeDefaults(node as Record<string, unknown>))\n : trimNodeDefaults(data as unknown as Record<string, unknown>)\n\n // 保存到文件\n const outputPath = resolve(options.output)\n const outputDir = dirname(outputPath)\n mkdirSync(outputDir, { recursive: true })\n\n const jsonContent = JSON.stringify(outputData, null, 2)\n writeFileSync(outputPath, jsonContent, 'utf-8')\n\n // 输出结果\n const size = jsonContent.length\n const sizeKB = (size / 1024).toFixed(2)\n const nodeCount = Array.isArray(data) ? data.length : 1\n console.log(`文件路径: ${outputPath}`)\n console.log(`节点数量: ${nodeCount}`)\n console.log(`数据大小: ${size.toLocaleString()} 字符 (约 ${sizeKB} KB)`)\n console.log(`节点深度: ${params.maxDepth}`)\n if (!options.raw) {\n console.log(`数据模式: 精简模式 (使用 --raw 获取完整数据)`)\n }\n } catch (error) {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n process.exit(1)\n } finally {\n client.close()\n }\n}\n","/**\n * export_image 命令\n * 导出 MasterGo 节点为图片文件\n */\n\nimport { Command } from 'commander'\nimport { writeFileSync, unlinkSync } from 'node:fs'\nimport { resolve, dirname, extname, basename } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient, parseMgpLink } from '../client.js'\nimport { MGError, ErrorCode } from '../../shared/errors.js'\nimport type { ExportImageParams } from '../../shared/types.js'\nimport { vdConvert } from 'vd-tool'\n\ntype ImageFormat = 'PNG' | 'JPG' | 'SVG' | 'PDF' | 'WEBP' | 'VECTOR'\n\ninterface ExportImageOptions {\n output?: string\n link?: string\n nodeId?: string\n domain?: string\n fileId?: string\n format?: string\n scale?: string\n width?: string\n height?: string\n useAbsoluteBounds?: boolean\n useRenderBounds?: boolean\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/** 导出响应 */\ninterface ExportResponse {\n /** Base64 编码的图片数据 */\n data: string\n /** MIME 类型 */\n mimeType: string\n /** 文件名建议 */\n filename?: string\n}\n\n/**\n * 创建 export_image 命令\n */\nexport function createExportImageCommand(): Command {\n return new Command('export_image')\n .description('导出 MasterGo 节点为图片文件。强烈建议指定 --output,否则保存到临时目录可能被系统清理')\n .option('--output <path>', '输出文件路径。强烈建议指定,否则保存到系统临时目录可能被清理')\n .option('--link <mgp-link>', 'mgp:// 协议链接。与 --nodeId/--domain/--fileId 二选一')\n .option('--nodeId <id>', '节点 ID,格式如 123:456。与 --domain/--fileId 配合使用')\n .option('--domain <domain>', 'MasterGo 域名,默认 mastergo.netease.com', 'mastergo.netease.com')\n .option('--fileId <id>', '文件 ID(纯数字),与 --domain 配合指定目标页面')\n .option('--format <type>', '导出格式:PNG(无损透明)、JPG(有损)、SVG(矢量)、PDF、WEBP、VECTOR(Android VectorDrawable)', 'PNG')\n .option('--scale <number>', '缩放倍率(如 1、2、3)。与 width/height 互斥')\n .option('--width <number>', '固定宽度(像素)。与 scale/height 互斥')\n .option('--height <number>', '固定高度(像素)。与 scale/width 互斥')\n .option('--useAbsoluteBounds', '使用完整尺寸。true: 包含被裁剪部分,false: 只导出可见区域', false)\n .option('--no-use-render-bounds', '不包含特效和外描边。默认包含阴影、外描边等')\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (options: ExportImageOptions) => {\n await handleExportImage(options)\n })\n}\n\n/**\n * 处理 export_image 命令\n */\nasync function handleExportImage(options: ExportImageOptions): Promise<void> {\n // 验证格式\n const format = (options.format?.toUpperCase() || 'PNG') as ImageFormat\n const validFormats: ImageFormat[] = ['PNG', 'JPG', 'SVG', 'PDF', 'WEBP', 'VECTOR']\n if (!validFormats.includes(format)) {\n console.error(`错误: 不支持的格式 \"${options.format}\"`)\n console.error(`支持的格式: ${validFormats.join(', ')}`)\n process.exit(1)\n }\n\n // 验证参数互斥\n const sizeParams = [options.scale, options.width, options.height].filter(Boolean)\n if (sizeParams.length > 1) {\n console.error('错误: scale、width、height 三者互斥,只能指定其中一个')\n process.exit(1)\n }\n\n // 验证 link 和 nodeId/fileId 互斥\n if (options.link && (options.nodeId || options.fileId)) {\n console.error(`错误 [${ErrorCode.INVALID_PARAMS}]: --link 和 --nodeId/--fileId 不能同时使用`)\n process.exit(1)\n }\n\n // 解析目标页面和节点\n let pageUrl: string | undefined\n let nodeId: string | undefined\n\n if (options.link) {\n // 方式1: 通过 mgp:// 链接\n const linkInfo = parseMgpLink(options.link)\n if (!linkInfo) {\n console.error(`错误 [${ErrorCode.INVALID_LINK}]: 无效的 mgp:// 链接格式`)\n console.error(`提供的链接: ${options.link}`)\n console.error(`期望格式: mgp://[mastergo_page_url]?nodeId=xxx`)\n process.exit(1)\n }\n pageUrl = linkInfo.pageUrl\n nodeId = linkInfo.nodeId\n } else {\n // 方式2: 通过 nodeId + domain + fileId\n if (options.nodeId) {\n nodeId = options.nodeId\n }\n if (options.fileId) {\n const domain = options.domain || 'mastergo.netease.com'\n pageUrl = `${domain}/file/${options.fileId}`\n }\n }\n\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n // VECTOR 格式需要先导出 SVG,再转换\n const isVectorFormat = format === 'VECTOR'\n const requestFormat = isVectorFormat ? 'SVG' : format\n\n // 构建请求参数\n const params: ExportImageParams = {\n format: requestFormat as 'PNG' | 'JPG' | 'SVG' | 'PDF' | 'WEBP',\n useAbsoluteBounds: options.useAbsoluteBounds || false,\n useRenderBounds: options.useRenderBounds !== false,\n }\n\n if (nodeId) {\n params.nodeId = nodeId\n }\n if (options.scale) {\n params.scale = parseFloat(options.scale)\n }\n if (options.width) {\n params.width = parseInt(options.width, 10)\n }\n if (options.height) {\n params.height = parseInt(options.height, 10)\n }\n\n // 发送请求(传入 pageUrl 以指定目标页面)\n const response = await client.requestWithRetry<ExportResponse>(\n MessageType.EXPORT_IMAGE,\n params as Record<string, unknown>,\n pageUrl\n )\n\n // 确定输出路径\n const ext = getExtension(format)\n let outputPath: string\n if (options.output) {\n outputPath = resolve(options.output)\n // 如果没有扩展名,添加扩展名\n if (!extname(outputPath)) {\n outputPath = `${outputPath}${ext}`\n }\n } else {\n // 使用临时目录\n const filename = response.filename || `export_${Date.now()}${ext}`\n outputPath = resolve(tmpdir(), filename)\n console.log('警告: 未指定 --output,文件将保存到临时目录,可能会被系统清理')\n }\n\n // 确保目录存在\n const outputDir = dirname(outputPath)\n mkdirSync(outputDir, { recursive: true })\n\n // 解码 Base64 并保存\n const buffer = Buffer.from(response.data, 'base64')\n\n let finalPath = outputPath\n let finalSize = buffer.length\n\n if (isVectorFormat) {\n // VECTOR 格式:先保存 SVG 到临时文件,再转换为 VectorDrawable\n const tempSvgPath = resolve(tmpdir(), `temp_${Date.now()}.svg`)\n writeFileSync(tempSvgPath, buffer)\n\n try {\n // 转换为 VectorDrawable\n const vectorOutputDir = dirname(outputPath)\n const convertedPath = await convertSvgToVector(tempSvgPath, vectorOutputDir)\n\n // vd-tool 生成的文件名是基于原 SVG 文件名的,需要重命名\n const expectedOutputName = basename(tempSvgPath, '.svg') + '.xml'\n const expectedOutputPath = resolve(vectorOutputDir, expectedOutputName)\n\n // 如果生成的文件路径不是我们期望的,重命名它\n if (expectedOutputPath !== outputPath) {\n const { renameSync, existsSync } = await import('node:fs')\n if (existsSync(expectedOutputPath)) {\n renameSync(expectedOutputPath, outputPath)\n } else if (existsSync(convertedPath)) {\n renameSync(convertedPath, outputPath)\n }\n }\n\n // 读取最终文件大小\n const { statSync } = await import('node:fs')\n finalSize = statSync(outputPath).size\n finalPath = outputPath\n } finally {\n // 清理临时 SVG 文件\n try {\n unlinkSync(tempSvgPath)\n } catch {\n // 忽略清理失败\n }\n }\n } else {\n // 其他格式直接保存\n writeFileSync(outputPath, buffer)\n }\n\n // 输出结果\n const sizeKB = (finalSize / 1024).toFixed(2)\n console.log(`文件路径: ${finalPath}`)\n if (options.link) {\n console.log(`Link: ${options.link}`)\n }\n if (nodeId) {\n console.log(`节点 ID: ${nodeId}`)\n } else {\n console.log('节点 ID: (选中的节点)')\n }\n console.log(`导出格式: ${format}${isVectorFormat ? ' (Android VectorDrawable)' : ''}`)\n console.log(`文件大小: ${finalSize.toLocaleString()} 字节 (约 ${sizeKB} KB)`)\n } catch (error) {\n if (error instanceof MGError) {\n console.error(`错误 [${error.code}]: ${error.message}`)\n } else {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n }\n process.exit(1)\n } finally {\n client.close()\n }\n}\n\n/**\n * 获取格式对应的文件扩展名\n */\nfunction getExtension(format: ImageFormat): string {\n const extensions: Record<ImageFormat, string> = {\n PNG: '.png',\n JPG: '.jpg',\n SVG: '.svg',\n PDF: '.pdf',\n WEBP: '.webp',\n VECTOR: '.xml',\n }\n return extensions[format]\n}\n\n/**\n * 将 SVG 文件转换为 Android VectorDrawable XML\n * @param svgPath SVG 文件路径\n * @param outputDir 输出目录\n * @returns 转换后的 XML 文件路径\n */\nasync function convertSvgToVector(svgPath: string, outputDir: string): Promise<string> {\n try {\n const result = await vdConvert(svgPath, {\n outDir: outputDir,\n })\n\n if (result.errors && result.errors.length > 0) {\n throw new Error(`VectorDrawable 转换失败: ${result.errors.join(', ')}`)\n }\n\n if (result.warnings && result.warnings.length > 0) {\n console.log(`警告: ${result.warnings.join(', ')}`)\n }\n\n return result.output\n } catch (error) {\n if (error instanceof Error) {\n // 检查是否是 Java 未安装的错误\n if (error.message.includes('ENOENT') || error.message.includes('java')) {\n throw new Error(\n 'VectorDrawable 转换需要 Java 8 或更高版本。\\n' +\n '请安装 Java: https://adoptium.net/ 或 brew install openjdk'\n )\n }\n throw error\n }\n throw new Error(`VectorDrawable 转换失败: ${error}`)\n }\n}\n","/**\n * execute_code 命令\n * 在 MasterGo 页面执行自定义 JavaScript 代码\n */\n\nimport { Command } from 'commander'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient, parseMgpLink } from '../client.js'\nimport { ErrorCode } from '../../shared/errors.js'\n\ninterface ExecuteCodeOptions {\n link?: string\n domain?: string\n fileId?: string\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/**\n * 创建 execute_code 命令\n */\nexport function createExecuteCodeCommand(): Command {\n return new Command('execute_code')\n .description('在 MasterGo 页面执行自定义 JavaScript 代码。通过 mg 变量访问 MasterGo API,结果会被 JSON 序列化返回')\n .argument('<code>', '要执行的代码。可使用 mg 变量,如 mg.currentPage.name、mg.currentPage.selection')\n .option('--link <url>', 'mgp:// 协议链接,用于指定目标页面。与 --domain/--fileId 二选一')\n .option('--domain <domain>', 'MasterGo 域名,默认 mastergo.netease.com', 'mastergo.netease.com')\n .option('--fileId <id>', '文件 ID(纯数字),与 --domain 配合指定目标页面')\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (code: string, options: ExecuteCodeOptions) => {\n await handleExecuteCode(code, options)\n })\n}\n\n/**\n * 处理 execute_code 命令\n */\nasync function handleExecuteCode(code: string, options: ExecuteCodeOptions): Promise<void> {\n // 验证参数:link 和 fileId 不能同时使用\n if (options.link && options.fileId) {\n console.error(`错误 [${ErrorCode.INVALID_PARAMS}]: --link 和 --fileId 不能同时使用`)\n process.exit(1)\n }\n\n // 解析目标页面 URL\n let pageUrl: string | undefined\n if (options.link) {\n const parsed = parseMgpLink(options.link)\n if (!parsed) {\n console.error(`错误 [${ErrorCode.INVALID_LINK}]: 无效的 mgp:// 链接格式`)\n console.error(`提供的链接: ${options.link}`)\n console.error(`期望格式: mgp://[mastergo_page_url]?nodeId=xxx`)\n process.exit(1)\n }\n pageUrl = parsed.pageUrl\n } else if (options.fileId) {\n const domain = options.domain || 'mastergo.netease.com'\n pageUrl = `${domain}/file/${options.fileId}`\n }\n\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n // 发送请求(传入 pageUrl 以指定目标页面)\n const result = await client.requestWithRetry<unknown>(MessageType.EXECUTE_CODE, { code }, pageUrl)\n\n // 输出结果\n if (result === null || result === undefined) {\n console.log('执行完成(无返回值)')\n } else if (typeof result === 'object') {\n console.log(JSON.stringify(result, null, 2))\n } else {\n console.log(result)\n }\n } catch (error) {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n process.exit(1)\n } finally {\n client.close()\n }\n}\n","/**\n * get_all_pages 命令\n * 获取 MasterGo 文档的所有页面信息\n */\n\nimport { Command } from 'commander'\nimport { writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient } from '../client.js'\nimport { extractFileId } from '../../shared/utils.js'\nimport type { AllPagesInfo } from '../../shared/types.js'\n\ninterface GetAllPagesOptions {\n link?: string\n fileId?: string\n domain?: string\n output?: string\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/**\n * 创建 get_all_pages 命令\n */\nexport function createGetAllPagesCommand(): Command {\n return new Command('get_all_pages')\n .description('获取 MasterGo 文档的所有页面信息。不指定 --output 时保存到系统临时目录')\n .option('--link <url>', '页面链接。支持完整 URL 或 mgp:// 协议')\n .option('--fileId <id>', '文件 ID(纯数字)。从 URL 中 /file/ 后面的数字')\n .option('--domain <domain>', 'MasterGo 域名,默认 mastergo.netease.com。与 --fileId 配合使用', 'mastergo.netease.com')\n .option('--output <path>', '输出 JSON 文件路径。不指定则保存到系统临时目录')\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (options: GetAllPagesOptions) => {\n await handleGetAllPages(options)\n })\n}\n\n/**\n * 处理 get_all_pages 命令\n */\nasync function handleGetAllPages(options: GetAllPagesOptions): Promise<void> {\n // 提取 fileId\n let fileId: string | null = null\n \n if (options.fileId) {\n fileId = options.fileId\n } else if (options.link) {\n fileId = extractFileId(options.link)\n if (!fileId) {\n console.error('错误: 无法从链接中提取 fileId')\n console.error('支持的格式:')\n console.error(' - 完整 URL: https://mastergo.netease.com/file/174875497054651')\n console.error(' - mgp 协议: mgp://mastergo.netease.com/file/174875497054651')\n console.error(' - 纯 fileId: 174875497054651')\n process.exit(1)\n }\n } else {\n // 没有提供参数,获取当前连接页面的所有页面\n console.log('未提供 --link 或 --fileId,将获取当前连接页面的所有页面信息')\n }\n\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n // 构建 pageUrl(如果有 fileId)\n let pageUrl: string | undefined\n if (fileId) {\n // 使用指定的域名或默认域名构建标准化的 pageUrl\n const domain = options.domain || 'mastergo.netease.com'\n pageUrl = `${domain}/file/${fileId}`\n }\n\n // 发送请求\n const data = await client.requestWithRetry<AllPagesInfo>(\n MessageType.GET_ALL_PAGES,\n {},\n pageUrl\n )\n\n // 确定输出路径\n let outputPath: string\n if (options.output) {\n outputPath = resolve(options.output)\n } else {\n // 使用系统临时目录\n const filename = `pages_${fileId || 'current'}_${Date.now()}.json`\n outputPath = resolve(tmpdir(), filename)\n }\n\n // 确保目录存在\n const outputDir = dirname(outputPath)\n mkdirSync(outputDir, { recursive: true })\n\n // 保存到文件\n const jsonContent = JSON.stringify(data, null, 2)\n writeFileSync(outputPath, jsonContent, 'utf-8')\n\n // 输出结果\n const size = jsonContent.length\n const sizeKB = (size / 1024).toFixed(2)\n console.log(`文件路径: ${outputPath}`)\n console.log(`文档名称: ${data.documentName}`)\n console.log(`页面数量: ${data.totalCount}`)\n console.log(`数据大小: ${size.toLocaleString()} 字符 (约 ${sizeKB} KB)`)\n } catch (error) {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n process.exit(1)\n } finally {\n client.close()\n }\n}\n","/**\n * get_node_for_space 命令\n * 获取节点的空间位置信息,用于 AI 理解元素布局\n * 只返回 id、name、x、y、width、height 和 children 字段\n */\n\nimport { Command } from 'commander'\nimport { writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient, parseMgpLink } from '../client.js'\nimport { extractSpaceInfo } from '../../shared/utils.js'\nimport { ErrorCode, MGError } from '../../shared/errors.js'\nimport type { GetNodeParams, NodeInfo } from '../../shared/types.js'\n\ninterface GetNodeForSpaceOptions {\n nodeId?: string\n link?: string\n output: string\n domain?: string\n fileId?: string\n maxDepth?: string\n includeInvisible?: boolean\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/**\n * 创建 get_node_for_space 命令\n */\nexport function createGetNodeForSpaceCommand(): Command {\n return new Command('get_node_for_space')\n .description('获取节点的空间位置信息(id、name、x、y、width、height),用于 AI 理解元素布局。默认获取最深层级')\n .option('--nodeId <id>', '节点 ID,格式如 123:456。与 --link 二选一')\n .option('--link <url>', 'mgp:// 协议链接。与 --nodeId 二选一')\n .requiredOption('--output <path>', '输出 JSON 文件路径')\n .option('--domain <domain>', 'MasterGo 域名,默认 mastergo.netease.com。与 --nodeId 配合使用', 'mastergo.netease.com')\n .option('--fileId <id>', '文件 ID(纯数字),与 --domain 和 --nodeId 配合使用')\n .option('--maxDepth <number>', '遍历深度,默认 99(获取最深层级)', '99')\n .option('--includeInvisible', '包含不可见节点', false)\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (options: GetNodeForSpaceOptions) => {\n await handleGetNodeForSpace(options)\n })\n}\n\n/**\n * 处理 get_node_for_space 命令\n */\nasync function handleGetNodeForSpace(options: GetNodeForSpaceOptions): Promise<void> {\n // 验证参数:必须提供 nodeId 或 link\n if (!options.nodeId && !options.link) {\n console.error(`错误 [${ErrorCode.INVALID_PARAMS}]: 必须提供 --nodeId 或 --link 参数`)\n process.exit(1)\n }\n\n if (options.nodeId && options.link) {\n console.error(`错误 [${ErrorCode.INVALID_PARAMS}]: --nodeId 和 --link 不能同时使用`)\n process.exit(1)\n }\n\n let pageUrl: string | undefined\n let nodeId: string\n\n // 解析参数\n if (options.link) {\n // 通过 mgp:// 链接获取\n const parsed = parseMgpLink(options.link)\n if (!parsed) {\n console.error(`错误 [${ErrorCode.INVALID_LINK}]: 无效的 mgp:// 链接格式`)\n console.error(`提供的链接: ${options.link}`)\n console.error(`期望格式: mgp://[mastergo_page_url]?nodeId=xxx`)\n process.exit(1)\n }\n pageUrl = parsed.pageUrl\n nodeId = parsed.nodeId\n } else {\n // 通过 nodeId 获取\n nodeId = options.nodeId!\n if (options.fileId) {\n const domain = options.domain || 'mastergo.netease.com'\n pageUrl = `${domain}/file/${options.fileId}`\n }\n }\n\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n // 发送请求(使用现有的 GET_NODE_BY_ID 消息类型)\n const params: GetNodeParams = {\n nodeId,\n maxDepth: parseInt(options.maxDepth || '99', 10),\n includeInvisible: options.includeInvisible || false,\n }\n\n const data = await client.requestWithRetry<NodeInfo>(\n MessageType.GET_NODE_BY_ID,\n params,\n pageUrl\n )\n\n // 提取空间信息\n const spaceData = extractSpaceInfo(data)\n\n // 保存到文件\n const outputPath = resolve(options.output)\n const outputDir = dirname(outputPath)\n mkdirSync(outputDir, { recursive: true })\n\n const jsonContent = JSON.stringify(spaceData, null, 2)\n writeFileSync(outputPath, jsonContent, 'utf-8')\n\n // 输出结果\n const size = jsonContent.length\n const sizeKB = (size / 1024).toFixed(2)\n console.log(`文件路径: ${outputPath}`)\n if (options.link) {\n console.log(`Link: ${options.link}`)\n console.log(`页面 URL: ${pageUrl}`)\n }\n console.log(`节点 ID: ${nodeId}`)\n console.log(`数据大小: ${size.toLocaleString()} 字符 (约 ${sizeKB} KB)`)\n console.log(`节点深度: ${params.maxDepth}`)\n console.log(`数据模式: 空间信息 (仅 id, name, x, y, width, height)`)\n } catch (error) {\n if (error instanceof MGError) {\n console.error(`错误 [${error.code}]: ${error.message}`)\n } else {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n }\n process.exit(1)\n } finally {\n client.close()\n }\n}\n"],"mappings":"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","names":["Command","fileURLToPath","dirname","join","existsSync","mkdirSync","dirname","dirname","existsSync","mkdirSync","readFileSync","existsSync","dirname","join","resolve","fileURLToPath","dirname","join","resolve","running","info","WebSocket","resolve","WebSocket","Command","writeFileSync","resolve","dirname","mkdirSync","Command","resolve","dirname","mkdirSync","writeFileSync","Command","writeFileSync","resolve","dirname","mkdirSync","Command","resolve","dirname","mkdirSync","writeFileSync","Command","writeFileSync","resolve","dirname","mkdirSync","Command","resolve","dirname","mkdirSync","writeFileSync","Command","writeFileSync","unlinkSync","resolve","dirname","mkdirSync","Command","resolve","dirname","mkdirSync","writeFileSync","existsSync","unlinkSync","Command","Command","Command","writeFileSync","resolve","dirname","mkdirSync","tmpdir","Command","resolve","tmpdir","dirname","mkdirSync","writeFileSync","Command","writeFileSync","resolve","dirname","mkdirSync","Command","resolve","dirname","mkdirSync","writeFileSync","Command"]}
1
+ {"version":3,"sources":["../src/cli/index.ts","../src/cli/commands/server.ts","../src/server/daemon.ts","../src/shared/utils.ts","../src/shared/constants.ts","../src/shared/errors.ts","../src/server/websocket-server.ts","../src/server/logger.ts","../src/server/connection-manager.ts","../src/server/request-handler.ts","../src/shared/version.ts","../src/cli/client.ts","../src/cli/commands/get-node-by-id.ts","../src/cli/commands/get-node-by-link.ts","../src/cli/commands/get-all-nodes.ts","../src/cli/commands/export-image.ts","../src/cli/commands/execute-code.ts","../src/cli/commands/get-all-pages.ts","../src/cli/commands/get-node-for-space.ts"],"sourcesContent":["/**\n * MG CLI 入口\n */\n\nimport { Command } from 'commander'\nimport { createServerCommand } from './commands/server.js'\nimport { createGetNodeByIdCommand } from './commands/get-node-by-id.js'\nimport { createGetNodeByLinkCommand } from './commands/get-node-by-link.js'\nimport { createGetAllNodesCommand } from './commands/get-all-nodes.js'\nimport { createExportImageCommand } from './commands/export-image.js'\nimport { createExecuteCodeCommand } from './commands/execute-code.js'\nimport { createGetAllPagesCommand } from './commands/get-all-pages.js'\nimport { createGetNodeForSpaceCommand } from './commands/get-node-for-space.js'\n\nconst program = new Command()\n\nprogram\n .name('mg-cli')\n .description('MasterGo CLI 工具 - 用于 Claude Code 与 MasterGo 通信')\n .version('1.0.0')\n\n// 添加 server 命令组\nprogram.addCommand(createServerCommand())\n\n// 添加业务命令\nprogram.addCommand(createGetNodeByIdCommand())\nprogram.addCommand(createGetNodeByLinkCommand())\nprogram.addCommand(createGetAllNodesCommand())\nprogram.addCommand(createExportImageCommand())\nprogram.addCommand(createExecuteCodeCommand())\nprogram.addCommand(createGetAllPagesCommand())\nprogram.addCommand(createGetNodeForSpaceCommand())\n\n// 解析命令行参数\nprogram.parse()\n","/**\n * Server 管理命令\n */\n\nimport { Command } from 'commander'\nimport {\n startServerForeground,\n startServerDaemon,\n stopServer,\n restartServer,\n getServerStatus,\n} from '../../server/daemon.js'\nimport { MGClient } from '../client.js'\nimport { MessageType } from '../../shared/constants.js'\nimport type { ServerStatusResponse } from '../../shared/types.js'\n\n/**\n * 创建 server 命令组\n */\nexport function createServerCommand(): Command {\n const serverCmd = new Command('server')\n .description('Server 管理命令')\n\n // server start\n serverCmd\n .command('start')\n .description('启动 MG Server')\n .option('--port <number>', '指定启动端口', (value) => parseInt(value, 10))\n .option('--foreground', '前台模式运行(不作为守护进程)', false)\n .action(async (options) => {\n try {\n if (options.foreground) {\n await startServerForeground(options.port)\n } else {\n const info = await startServerDaemon(options.port)\n console.log('MG Server 启动成功')\n console.log(`版本: ${info.version}`)\n console.log(`监听端口: ${info.port}`)\n console.log(`进程 PID: ${info.pid}`)\n console.log(`运行模式: 守护进程`)\n }\n } catch (error: any) {\n console.error(`错误: ${error.message}`)\n process.exit(1)\n }\n })\n\n // server stop\n serverCmd\n .command('stop')\n .description('停止 MG Server')\n .action(() => {\n try {\n const { stopped, info } = stopServer()\n\n if (stopped && info) {\n console.log('MG Server 已停止')\n console.log(`PID: ${info.pid}`)\n\n // 计算运行时长\n const uptimeMs = Date.now() - new Date(info.startedAt).getTime()\n const seconds = Math.floor(uptimeMs / 1000)\n const minutes = Math.floor(seconds / 60)\n const hours = Math.floor(minutes / 60)\n\n let uptime = ''\n if (hours > 0) {\n uptime = `${hours} 小时 ${minutes % 60} 分钟`\n } else if (minutes > 0) {\n uptime = `${minutes} 分钟 ${seconds % 60} 秒`\n } else {\n uptime = `${seconds} 秒`\n }\n\n console.log(`运行时长: ${uptime}`)\n } else {\n console.log('MG Server 未运行')\n }\n } catch (error: any) {\n console.error(`错误: ${error.message}`)\n process.exit(1)\n }\n })\n\n // server restart\n serverCmd\n .command('restart')\n .description('重启 MG Server')\n .option('--port <number>', '重启后使用的端口', (value) => parseInt(value, 10))\n .action(async (options) => {\n try {\n const status = getServerStatus()\n\n if (status.running) {\n console.log(`正在停止 MG Server (PID: ${status.pid})...`)\n }\n\n const info = await restartServer(options.port)\n\n console.log('MG Server 已重启')\n console.log(`版本: ${info.version}`)\n console.log(`监听端口: ${info.port}`)\n console.log(`新进程 PID: ${info.pid}`)\n } catch (error: any) {\n console.error(`错误: ${error.message}`)\n process.exit(1)\n }\n })\n\n // server status\n serverCmd\n .command('status')\n .description('查看 Server 运行状态')\n .action(async () => {\n try {\n // 先检查进程是否运行\n const basicStatus = getServerStatus()\n\n if (!basicStatus.running) {\n console.log('MG Server 状态: 未运行 ✗')\n console.log(\"提示: 使用 'mg-cli server start' 启动 Server\")\n return\n }\n\n // 连接 Server 获取详细状态(包括连接的页面)\n try {\n const client = new MGClient({ noAutoStart: true })\n await client.connect()\n\n const status = await client.request<ServerStatusResponse>(\n MessageType.GET_SERVER_STATUS\n )\n\n client.close()\n\n // 显示状态信息\n console.log('MG Server 状态: 运行中 ✓')\n console.log(`版本: ${status.version}`)\n console.log(`监听端口: ${status.port}`)\n console.log(`进程 PID: ${status.pid}`)\n console.log(`启动时间: ${status.startedAt}`)\n console.log(`运行时长: ${status.uptime}`)\n\n // 显示连接统计\n console.log(``)\n console.log(`连接统计:`)\n console.log(` Provider 数量: ${status.stats.providers}`)\n console.log(` Consumer 数量: ${status.stats.consumers}`)\n\n // 显示已连接的页面\n if (status.connectedPages.length > 0) {\n console.log(``)\n console.log(`已连接页面 (${status.connectedPages.length}):`)\n for (const page of status.connectedPages) {\n console.log(` - ${page.pageUrl}`)\n }\n } else {\n console.log(``)\n console.log(`已连接页面: 无`)\n console.log(`提示: 请安装 MG Plugin 浏览器扩展并在 Chrome 中打开 MasterGo 页面`)\n console.log(` 插件地址: https://chromewebstore.google.com/detail/mg-plugin/ddhihanlpcdneicohnglnaliefnkaeja`)\n }\n } catch {\n // 无法连接但进程存在,显示基本信息\n console.log('MG Server 状态: 运行中 ✓')\n if (basicStatus.version) {\n console.log(`版本: ${basicStatus.version}`)\n }\n console.log(`监听端口: ${basicStatus.port}`)\n console.log(`进程 PID: ${basicStatus.pid}`)\n console.log(`启动时间: ${basicStatus.startedAt}`)\n console.log(`运行时长: ${basicStatus.uptime}`)\n console.log(``)\n console.log(`注意: 无法获取详细连接信息`)\n }\n } catch (error: any) {\n console.error(`错误: ${error.message}`)\n process.exit(1)\n }\n })\n\n return serverCmd\n}\n","/**\n * 守护进程管理\n */\n\nimport { spawn, ChildProcess } from 'node:child_process'\nimport { fileURLToPath } from 'node:url'\nimport { dirname, join } from 'node:path'\nimport {\n readServerInfo,\n writeServerInfo,\n deleteServerInfo,\n isProcessRunning,\n killProcess,\n ensureConfigDir,\n getCurrentISOTime,\n formatDuration,\n} from '../shared/utils.js'\nimport { SERVER_START_TIMEOUT, DEFAULT_PORT } from '../shared/constants.js'\nimport { ErrorCode, MGError } from '../shared/errors.js'\nimport type { ServerInfo } from '../shared/types.js'\nimport { createServer } from './websocket-server.js'\nimport { createLogger, LogLevel } from './logger.js'\nimport { getVersion } from '../shared/version.js'\n\n/**\n * 检查 Server 是否在运行\n */\nexport function isServerRunning(): { running: boolean; info: ServerInfo | null } {\n const info = readServerInfo()\n\n if (!info) {\n return { running: false, info: null }\n }\n\n // 检查进程是否存在\n if (!isProcessRunning(info.pid)) {\n // 进程不存在,清理旧文件\n deleteServerInfo()\n return { running: false, info: null }\n }\n\n return { running: true, info }\n}\n\n/**\n * 启动 Server(前台模式)\n */\nexport async function startServerForeground(port?: number): Promise<void> {\n const { running, info } = isServerRunning()\n\n if (running && info) {\n throw new MGError(\n ErrorCode.SERVER_ALREADY_RUNNING,\n `Server 已在运行中 (PID: ${info.pid}, 端口: ${info.port})`\n )\n }\n\n ensureConfigDir()\n\n const logger = createLogger({\n console: true,\n file: true,\n minLevel: LogLevel.INFO,\n })\n\n const server = createServer({\n port: port || DEFAULT_PORT,\n logger,\n })\n\n // 注册信号处理\n const cleanup = async () => {\n console.log('\\n正在停止 Server...')\n await server.stop()\n deleteServerInfo()\n process.exit(0)\n }\n\n process.on('SIGINT', cleanup)\n process.on('SIGTERM', cleanup)\n\n try {\n const actualPort = await server.start()\n\n // 写入 Server 信息(包含版本号)\n writeServerInfo({\n port: actualPort,\n pid: process.pid,\n startedAt: getCurrentISOTime(),\n version: getVersion(),\n })\n\n console.log(`\\nMG Server 启动成功`)\n console.log(`监听端口: ${actualPort}`)\n console.log(`进程 PID: ${process.pid}`)\n console.log(`运行模式: 前台`)\n console.log(`\\n按 Ctrl+C 停止...`)\n } catch (error) {\n logger.error('Server 启动失败:', error)\n throw error\n }\n}\n\n/**\n * 启动 Server(守护进程模式)\n */\nexport async function startServerDaemon(port?: number): Promise<ServerInfo> {\n const { running, info } = isServerRunning()\n\n if (running && info) {\n throw new MGError(\n ErrorCode.SERVER_ALREADY_RUNNING,\n `Server 已在运行中 (PID: ${info.pid}, 端口: ${info.port})`\n )\n }\n\n ensureConfigDir()\n\n // 获取当前模块的路径\n const currentFile = fileURLToPath(import.meta.url)\n const currentDir = dirname(currentFile)\n const serverScript = join(currentDir, 'daemon-runner.js')\n\n // 启动子进程\n const args = ['--foreground']\n if (port) {\n args.push('--port', String(port))\n }\n\n const child: ChildProcess = spawn(process.execPath, [serverScript, ...args], {\n detached: true,\n stdio: 'ignore',\n env: {\n ...process.env,\n MG_DAEMON: '1',\n },\n })\n\n child.unref()\n\n // 等待 Server 启动\n const startTime = Date.now()\n while (Date.now() - startTime < SERVER_START_TIMEOUT) {\n await new Promise((resolve) => setTimeout(resolve, 200))\n\n const { running, info } = isServerRunning()\n if (running && info) {\n return info\n }\n }\n\n throw new MGError(ErrorCode.SERVER_START_FAILED, 'Server 启动超时')\n}\n\n/**\n * 停止 Server\n */\nexport function stopServer(): { stopped: boolean; info: ServerInfo | null } {\n const { running, info } = isServerRunning()\n\n if (!running || !info) {\n return { stopped: false, info: null }\n }\n\n // 终止进程\n const killed = killProcess(info.pid)\n\n if (killed) {\n deleteServerInfo()\n }\n\n return { stopped: killed, info }\n}\n\n/**\n * 重启 Server\n */\nexport async function restartServer(port?: number): Promise<ServerInfo> {\n const { info: oldInfo } = stopServer()\n\n // 等待旧进程完全停止\n await new Promise((resolve) => setTimeout(resolve, 500))\n\n // 启动新 Server\n return startServerDaemon(port || oldInfo?.port)\n}\n\n/**\n * 获取 Server 状态\n */\nexport function getServerStatus(): {\n running: boolean\n port?: number\n pid?: number\n startedAt?: string\n uptime?: string\n version?: string\n} {\n const { running, info } = isServerRunning()\n\n if (!running || !info) {\n return { running: false }\n }\n\n const uptimeMs = Date.now() - new Date(info.startedAt).getTime()\n\n return {\n running: true,\n port: info.port,\n pid: info.pid,\n startedAt: info.startedAt,\n uptime: formatDuration(uptimeMs),\n version: info.version,\n }\n}\n","/**\n * MG Plugin 工具函数\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs'\nimport { dirname, resolve, isAbsolute } from 'node:path'\nimport { CONFIG_DIR, SERVER_INFO_FILE, LOG_DIR } from './constants.js'\nimport type { ServerInfo, SpaceNodeInfo, NodeInfo } from './types.js'\n\n// ==================== 文件操作 ====================\n\n/**\n * 确保目录存在\n */\nexport function ensureDir(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n}\n\n/**\n * 确保配置目录存在\n */\nexport function ensureConfigDir(): void {\n ensureDir(CONFIG_DIR)\n ensureDir(LOG_DIR)\n}\n\n/**\n * 读取 Server 信息文件\n */\nexport function readServerInfo(): ServerInfo | null {\n try {\n if (!existsSync(SERVER_INFO_FILE)) {\n return null\n }\n const content = readFileSync(SERVER_INFO_FILE, 'utf-8')\n return JSON.parse(content) as ServerInfo\n } catch {\n return null\n }\n}\n\n/**\n * 写入 Server 信息文件\n */\nexport function writeServerInfo(info: ServerInfo): void {\n ensureConfigDir()\n writeFileSync(SERVER_INFO_FILE, JSON.stringify(info, null, 2), 'utf-8')\n}\n\n/**\n * 删除 Server 信息文件\n */\nexport function deleteServerInfo(): void {\n try {\n if (existsSync(SERVER_INFO_FILE)) {\n unlinkSync(SERVER_INFO_FILE)\n }\n } catch {\n // 忽略删除错误\n }\n}\n\n/**\n * 解析输出路径(支持相对路径和绝对路径)\n */\nexport function resolveOutputPath(outputPath: string): string {\n if (isAbsolute(outputPath)) {\n return outputPath\n }\n return resolve(process.cwd(), outputPath)\n}\n\n/**\n * 确保输出路径的目录存在\n */\nexport function ensureOutputDir(outputPath: string): void {\n const dir = dirname(outputPath)\n ensureDir(dir)\n}\n\n// ==================== 进程管理 ====================\n\n/**\n * 检查进程是否存在\n */\nexport function isProcessRunning(pid: number): boolean {\n try {\n // 发送信号 0 不会终止进程,但如果进程不存在会抛出错误\n process.kill(pid, 0)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * 终止进程\n */\nexport function killProcess(pid: number): boolean {\n try {\n process.kill(pid, 'SIGTERM')\n return true\n } catch {\n return false\n }\n}\n\n// ==================== URL 处理 ====================\n\n/**\n * 标准化页面 URL\n *\n * 输入: https://mastergo.netease.com/file/174135798361888?fileOpenFrom=home&page_id=0%3A8347\n * 输出: mastergo.netease.com/file/174135798361888\n */\nexport function normalizePageUrl(url: string): string {\n try {\n const urlObj = new URL(url)\n // 返回 host + pathname,去掉 https:// 前缀和查询参数\n return urlObj.host + urlObj.pathname\n } catch {\n // 如果已经是标准化的格式,直接返回\n return url\n }\n}\n\n/**\n * 检查是否是设计页面 URL\n */\nexport function isDesignPageUrl(url: string): boolean {\n // 匹配 /file/数字 格式\n return /\\/file\\/\\d+/.test(url)\n}\n\n/**\n * 解析 mgp:// 链接\n *\n * 支持的格式 (queryParams 格式,nodeId/pageId 需要 URL 编码):\n * - mgp://mastergo.netease.com/file/174135798361888?nodeId=123%3A456 (单个节点)\n * - mgp://mastergo.netease.com/file/174135798361888?nodeId=0%3A8633&nodePath=314%3A13190%2F0%3A8633 (带父节点路径)\n * - mgp://mastergo.netease.com/file/174135798361888?pageId=0%3A1 (页面链接)\n *\n * 输出: { pageUrl: 'mastergo.netease.com/file/174135798361888', nodeId: '0:8633', nodePath: ['314:13190', '0:8633'] }\n * 或 { pageUrl: 'mastergo.netease.com/file/174135798361888', pageId: '0:1' }\n */\nexport function parseMgpLink(link: string): { pageUrl: string; nodeId?: string; pageId?: string; nodePath?: string[] } | null {\n // 检查是否是 mgp:// 协议\n if (!link.startsWith('mgp://')) {\n return null\n }\n\n try {\n // 移除 mgp:// 前缀,构造为可解析的 URL\n const urlPart = link.slice(6) // 移除 'mgp://'\n const questionMarkIndex = urlPart.indexOf('?')\n\n if (questionMarkIndex === -1) {\n // 没有查询参数,无效格式\n return null\n }\n\n const pageUrl = urlPart.slice(0, questionMarkIndex)\n const queryString = urlPart.slice(questionMarkIndex + 1)\n\n // 解析查询参数\n const params = new URLSearchParams(queryString)\n const encodedNodeId = params.get('nodeId')\n const encodedPageId = params.get('pageId')\n\n // nodeId 和 pageId 必须有一个\n if (!encodedNodeId && !encodedPageId) {\n return null\n }\n\n // 不能同时有 nodeId 和 pageId\n if (encodedNodeId && encodedPageId) {\n return null\n }\n\n // 解析 nodeId\n if (encodedNodeId) {\n const nodeId = decodeURIComponent(encodedNodeId)\n\n // 验证 nodeId 格式:支持单个节点 (数字:数字) 或带路径的格式 (数字:数字/数字:数字/...)\n if (!/^(\\d+:\\d+)(\\/\\d+:\\d+)*$/.test(nodeId)) {\n return null\n }\n\n // 解析可选的 nodePath\n const encodedNodePath = params.get('nodePath')\n let nodePath: string[] | undefined\n\n if (encodedNodePath) {\n const decodedNodePath = decodeURIComponent(encodedNodePath)\n nodePath = decodedNodePath.split('/').filter(Boolean)\n // 验证每个路径段的格式\n if (!nodePath.every(segment => /^\\d+:\\d+$/.test(segment))) {\n return null\n }\n }\n\n return {\n pageUrl,\n nodeId,\n nodePath,\n }\n }\n\n // 解析 pageId\n if (encodedPageId) {\n const pageId = decodeURIComponent(encodedPageId)\n\n // 验证 pageId 格式:数字:数字\n if (!/^\\d+:\\d+$/.test(pageId)) {\n return null\n }\n\n return {\n pageUrl,\n pageId,\n }\n }\n\n return null\n } catch {\n return null\n }\n}\n\n/**\n * 生成 mgp:// 链接\n *\n * @param pageUrl 页面 URL(会被标准化)\n * @param nodeId 节点 ID(会被 URL 编码)\n * @param nodePath 可选的父节点路径(会被 URL 编码)\n */\nexport function generateMgpLink(pageUrl: string, nodeId: string, nodePath?: string[]): string {\n const normalizedUrl = normalizePageUrl(pageUrl)\n const encodedNodeId = encodeURIComponent(nodeId)\n\n let link = `mgp://${normalizedUrl}?nodeId=${encodedNodeId}`\n\n if (nodePath && nodePath.length > 0) {\n const encodedNodePath = encodeURIComponent(nodePath.join('/'))\n link += `&nodePath=${encodedNodePath}`\n }\n\n return link\n}\n\n/**\n * 生成 mgp:// 页面链接\n *\n * @param pageUrl 页面 URL(会被标准化)\n * @param pageId 页面 ID(会被 URL 编码)\n */\nexport function generatePageLink(pageUrl: string, pageId: string): string {\n const normalizedUrl = normalizePageUrl(pageUrl)\n const encodedPageId = encodeURIComponent(pageId)\n\n return `mgp://${normalizedUrl}?pageId=${encodedPageId}`\n}\n\n// ==================== 输出格式化 ====================\n\n/**\n * 格式化文件大小\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) {\n return `${bytes} 字节`\n }\n const kb = bytes / 1024\n if (kb < 1024) {\n return `${kb.toFixed(2)} KB`\n }\n const mb = kb / 1024\n return `${mb.toFixed(2)} MB`\n}\n\n/**\n * 格式化时间间隔\n */\nexport function formatDuration(ms: number): string {\n const seconds = Math.floor(ms / 1000)\n const minutes = Math.floor(seconds / 60)\n const hours = Math.floor(minutes / 60)\n const days = Math.floor(hours / 24)\n\n if (days > 0) {\n return `${days} 天 ${hours % 24} 小时`\n }\n if (hours > 0) {\n return `${hours} 小时 ${minutes % 60} 分钟`\n }\n if (minutes > 0) {\n return `${minutes} 分钟 ${seconds % 60} 秒`\n }\n return `${seconds} 秒`\n}\n\n/**\n * 生成唯一 ID\n */\nexport function generateId(): string {\n return `${Date.now()}_${Math.random().toString(36).substring(2, 9)}`\n}\n\n/**\n * 获取当前时间的 ISO 格式字符串\n */\nexport function getCurrentISOTime(): string {\n return new Date().toISOString()\n}\n\n/**\n * 格式化日期时间用于日志\n */\nexport function formatLogTime(date: Date = new Date()): string {\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const day = String(date.getDate()).padStart(2, '0')\n const hours = String(date.getHours()).padStart(2, '0')\n const minutes = String(date.getMinutes()).padStart(2, '0')\n const seconds = String(date.getSeconds()).padStart(2, '0')\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`\n}\n\n// ==================== FileId 提取 ====================\n\n/**\n * 从完整 URL 提取 fileId\n * \n * 支持格式:\n * - https://mastergo.netease.com/file/174875497054651?page_id=321%3A11979\n * - mastergo.netease.com/file/174875497054651\n * \n * @returns fileId 或 null\n */\nexport function extractFileIdFromUrl(url: string): string | null {\n // 匹配 /file/数字 格式\n const match = url.match(/\\/file\\/(\\d+)/)\n return match ? match[1] : null\n}\n\n/**\n * 从 mgp:// 链接提取 fileId\n * \n * 支持格式:\n * - mgp://mastergo.netease.com/file/174875497054651?nodeId=xxx\n * \n * @returns fileId 或 null\n */\nexport function extractFileIdFromMgpLink(link: string): string | null {\n if (!link.startsWith('mgp://')) {\n return null\n }\n return extractFileIdFromUrl(link)\n}\n\n/**\n * 统一处理输入,提取 fileId\n * \n * 支持三种输入格式:\n * 1. 完整 URL: https://mastergo.netease.com/file/174875497054651?page_id=xxx\n * 2. mgp 协议: mgp://mastergo.netease.com/file/174875497054651?nodeId=xxx\n * 3. 纯 fileId: 174875497054651\n * \n * @returns fileId 或 null\n */\nexport function extractFileId(input: string): string | null {\n const trimmed = input.trim()\n \n // 1. 纯数字 fileId\n if (/^\\d+$/.test(trimmed)) {\n return trimmed\n }\n \n // 2. mgp:// 协议\n if (trimmed.startsWith('mgp://')) {\n return extractFileIdFromMgpLink(trimmed)\n }\n \n // 3. 完整 URL (http:// 或 https://)\n if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {\n return extractFileIdFromUrl(trimmed)\n }\n \n // 4. 尝试作为不带协议的 URL 解析\n return extractFileIdFromUrl(trimmed)\n}\n\n// ==================== 节点数据精简 ====================\n\n/**\n * 需要四舍五入的数值字段\n */\nconst NUMERIC_FIELDS = [\n 'x', 'y', 'width', 'height',\n 'rotation',\n 'opacity',\n 'cornerRadius', 'topLeftRadius', 'topRightRadius', 'bottomLeftRadius', 'bottomRightRadius',\n 'strokeWeight', 'strokeTopWeight', 'strokeLeftWeight', 'strokeBottomWeight', 'strokeRightWeight',\n 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft',\n 'itemSpacing', 'crossAxisSpacing',\n 'flexGrow',\n]\n\n/**\n * 四舍五入到一位小数\n * 25.666667938232422 -> 25.7\n */\nexport function roundToOneDecimal(value: number): number {\n return Math.round(value * 10) / 10\n}\n\n/**\n * MasterGo 节点属性默认值\n * 基于官方文档定义,当值等于默认值时可以省略以减少 JSON 体积\n */\nexport const NODE_DEFAULTS: Record<string, unknown> = {\n // Scene Node 属性\n visible: true,\n isVisible: true,\n isLocked: false,\n\n // Blend 属性\n opacity: 1,\n blendMode: 'NORMAL',\n isMask: false,\n isMaskOutline: false,\n isMaskVisible: false,\n effectStyleId: '',\n\n // Geometry 属性\n strokeStyle: 'SOLID',\n strokeWeight: 0,\n strokeTopWeight: 0,\n strokeLeftWeight: 0,\n strokeBottomWeight: 0,\n strokeRightWeight: 0,\n strokeAlign: 'CENTER',\n strokeCap: 'NONE',\n strokeJoin: 'MITER',\n dashCap: 'NONE',\n fillStyleId: '',\n strokeStyleId: '',\n\n // Corner 属性\n cornerSmooth: 0,\n cornerRadius: 0,\n topLeftRadius: 0,\n topRightRadius: 0,\n bottomLeftRadius: 0,\n bottomRightRadius: 0,\n\n // Layout 属性\n rotation: 0,\n flexGrow: 0,\n alignSelf: 'INHERIT',\n layoutPositioning: 'AUTO',\n constrainProportions: false,\n\n // 容器专属属性 (FrameNode)\n flexMode: 'NONE',\n flexWrap: 'NO_WRAP',\n itemSpacing: 0,\n crossAxisSpacing: 0,\n paddingTop: 0,\n paddingRight: 0,\n paddingBottom: 0,\n paddingLeft: 0,\n clipsContent: false,\n itemReverseZIndex: false,\n strokesIncludedInLayout: false,\n\n // 其他属性\n componentPropertyReferences: null,\n}\n\n/**\n * 需要检查是否为空数组的字段\n */\nconst EMPTY_ARRAY_FIELDS = [\n 'fills',\n 'strokes',\n 'effects',\n 'strokeDashes',\n 'exportSettings',\n 'reactions',\n 'attachedConnectors',\n]\n\n/**\n * 检查值是否为空数组\n */\nfunction isEmptyArray(value: unknown): boolean {\n return Array.isArray(value) && value.length === 0\n}\n\n/**\n * 检查两个值是否相等(简单比较)\n */\nfunction isEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (a === null || b === null) return a === b\n if (typeof a !== typeof b) return false\n return false\n}\n\n/**\n * 精简节点数据,移除等于默认值的字段\n * 递归处理 children 数组\n *\n * @param node 原始节点数据\n * @returns 精简后的节点数据\n */\nexport function trimNodeDefaults<T extends Record<string, unknown>>(node: T): T {\n const result: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(node)) {\n // 递归处理 children 数组\n if (key === 'children' && Array.isArray(value)) {\n const trimmedChildren = value.map((child) =>\n typeof child === 'object' && child !== null\n ? trimNodeDefaults(child as Record<string, unknown>)\n : child\n )\n // 只有非空时才保留\n if (trimmedChildren.length > 0) {\n result[key] = trimmedChildren\n }\n continue\n }\n\n // 检查是否是需要检查空数组的字段\n if (EMPTY_ARRAY_FIELDS.includes(key) && isEmptyArray(value)) {\n continue // 跳过空数组\n }\n\n // 对数值字段进行四舍五入\n if (NUMERIC_FIELDS.includes(key) && typeof value === 'number') {\n const roundedValue = roundToOneDecimal(value)\n // 检查四舍五入后是否等于默认值\n if (key in NODE_DEFAULTS && isEqual(roundedValue, NODE_DEFAULTS[key])) {\n continue // 跳过等于默认值的字段\n }\n result[key] = roundedValue\n continue\n }\n\n // 检查是否有默认值定义\n if (key in NODE_DEFAULTS) {\n const defaultValue = NODE_DEFAULTS[key]\n if (isEqual(value, defaultValue)) {\n continue // 跳过等于默认值的字段\n }\n }\n\n // 保留此字段\n result[key] = value\n }\n\n return result as T\n}\n\n// ==================== 空间信息提取 ====================\n\n/**\n * 提取节点的空间信息\n * 只保留 id、name、x、y、width、height 和 children 字段\n * 用于让 AI 以最少的字段理解节点的空间布局关系\n *\n * @param node 原始节点数据\n * @returns 精简的空间节点信息\n */\nexport function extractSpaceInfo(node: NodeInfo): SpaceNodeInfo {\n const result: SpaceNodeInfo = {\n id: node.id,\n name: node.name,\n x: roundToOneDecimal(typeof node.x === 'number' ? node.x : 0),\n y: roundToOneDecimal(typeof node.y === 'number' ? node.y : 0),\n width: roundToOneDecimal(typeof node.width === 'number' ? node.width : 0),\n height: roundToOneDecimal(typeof node.height === 'number' ? node.height : 0),\n }\n\n // 递归处理 children\n if (node.children && Array.isArray(node.children) && node.children.length > 0) {\n result.children = node.children.map((child) => extractSpaceInfo(child))\n }\n\n return result\n}\n","/**\n * MG Plugin 常量定义\n */\n\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\n// ==================== 运行模式 ====================\n\n/** 是否为开发模式(通过环境变量 MG_DEV_MODE=true 设置) */\nexport const IS_DEV_MODE = process.env.MG_DEV_MODE === 'true'\n\n// ==================== 端口配置 ====================\n\n/** 生产环境默认端口 */\nexport const PROD_DEFAULT_PORT = 9527\n\n/** 生产环境端口范围:起始 */\nexport const PROD_PORT_RANGE_START = 9527\n\n/** 生产环境端口范围:结束 */\nexport const PROD_PORT_RANGE_END = 9536\n\n/** 开发环境默认端口 */\nexport const DEV_DEFAULT_PORT = 19527\n\n/** 开发环境端口范围:起始 */\nexport const DEV_PORT_RANGE_START = 19527\n\n/** 开发环境端口范围:结束 */\nexport const DEV_PORT_RANGE_END = 19536\n\n/** 默认端口(根据运行模式自动选择) */\nexport const DEFAULT_PORT = IS_DEV_MODE ? DEV_DEFAULT_PORT : PROD_DEFAULT_PORT\n\n/** 端口范围:起始(根据运行模式自动选择) */\nexport const PORT_RANGE_START = IS_DEV_MODE ? DEV_PORT_RANGE_START : PROD_PORT_RANGE_START\n\n/** 端口范围:结束(根据运行模式自动选择) */\nexport const PORT_RANGE_END = IS_DEV_MODE ? DEV_PORT_RANGE_END : PROD_PORT_RANGE_END\n\n/** 最大尝试端口数 */\nexport const MAX_PORT_ATTEMPTS = 10\n\n/** 端口扫描超时(毫秒) */\nexport const PORT_SCAN_TIMEOUT = 500\n\n// ==================== 路径配置 ====================\n\n/** 配置目录 */\nexport const CONFIG_DIR = join(homedir(), '.mg-plugin')\n\n/** Server 状态文件 */\nexport const SERVER_INFO_FILE = join(CONFIG_DIR, 'server.json')\n\n/** 日志目录 */\nexport const LOG_DIR = join(CONFIG_DIR, 'logs')\n\n/** Server 日志文件 */\nexport const SERVER_LOG_FILE = join(LOG_DIR, 'server.log')\n\n// ==================== 超时配置 ====================\n\n/** 心跳间隔(毫秒)- 30 秒 */\nexport const HEARTBEAT_INTERVAL = 30000\n\n/** 心跳超时(毫秒)- 90 秒(3 次心跳) */\nexport const HEARTBEAT_TIMEOUT = 90000\n\n/** 请求超时(毫秒)- 30 秒 */\nexport const REQUEST_TIMEOUT = 30000\n\n/** Server 启动等待超时(毫秒)- 5 秒 */\nexport const SERVER_START_TIMEOUT = 5000\n\n/** CLI 重试间隔(毫秒) */\nexport const RETRY_INTERVALS = [1000, 2000, 4000]\n\n/** 最大重试次数 */\nexport const MAX_RETRY_COUNT = 3\n\n// ==================== 连接类型 ====================\n\n/** 连接类型 */\nexport enum ConnectionType {\n /** 获取端 (CLI/MCP) */\n CONSUMER = 'consumer',\n /** 提供端 (Injector) */\n PROVIDER = 'provider',\n}\n\n// ==================== 消息类型 ====================\n\n/** WebSocket 消息类型 */\nexport enum MessageType {\n // 系统消息\n PING = 'ping',\n PONG = 'pong',\n REGISTER = 'register',\n REGISTER_ACK = 'register_ack',\n\n // 业务消息\n GET_NODE_BY_ID = 'get_node_by_id',\n GET_PAGE_BY_ID = 'get_page_by_id',\n GET_ALL_NODES = 'get_all_nodes',\n GET_SELECTION = 'get_selection',\n EXPORT_IMAGE = 'export_image',\n EXECUTE_CODE = 'execute_code',\n GET_ALL_PAGES = 'get_all_pages',\n\n // Server 状态消息(本地请求,不转发到 Provider)\n GET_SERVER_STATUS = 'get_server_status',\n\n // 响应\n RESPONSE = 'response',\n ERROR = 'error',\n}\n","/**\n * MG Plugin 错误码定义\n */\n\n/** 错误码枚举 */\nexport enum ErrorCode {\n /** 无法连接到 MG Server */\n CONNECTION_FAILED = 'E001',\n /** 连接超时 */\n CONNECTION_TIMEOUT = 'E002',\n /** 没有 MasterGo 页面连接到 Server */\n NO_PAGE_CONNECTED = 'E003',\n /** 未找到匹配的页面 */\n PAGE_NOT_FOUND = 'E004',\n /** 节点不存在 */\n NODE_NOT_FOUND = 'E005',\n /** 没有选中任何节点 */\n NO_SELECTION = 'E006',\n /** mg 对象不可用 */\n MG_UNAVAILABLE = 'E007',\n /** 导出图片失败 */\n EXPORT_FAILED = 'E008',\n /** 文件写入失败 */\n FILE_WRITE_FAILED = 'E009',\n /** 无效的 mgp:// 链接格式 */\n INVALID_LINK = 'E010',\n /** 参数校验失败 */\n INVALID_PARAMS = 'E011',\n /** 请求超时 */\n REQUEST_TIMEOUT = 'E012',\n /** 所有备选端口均被占用 */\n PORT_EXHAUSTED = 'E013',\n /** 无法发现 Server (端口扫描失败) */\n SERVER_DISCOVERY_FAILED = 'E014',\n /** 自动启动 Server 失败 */\n SERVER_START_FAILED = 'E015',\n /** Server 已在运行中 */\n SERVER_ALREADY_RUNNING = 'E016',\n /** 连接断开 */\n CONNECTION_LOST = 'E017',\n /** 未知错误 */\n UNKNOWN_ERROR = 'E099',\n}\n\n/** 错误码对应的错误名称 */\nexport const ErrorNames: Record<ErrorCode, string> = {\n [ErrorCode.CONNECTION_FAILED]: 'CONNECTION_FAILED',\n [ErrorCode.CONNECTION_TIMEOUT]: 'CONNECTION_TIMEOUT',\n [ErrorCode.NO_PAGE_CONNECTED]: 'NO_PAGE_CONNECTED',\n [ErrorCode.PAGE_NOT_FOUND]: 'PAGE_NOT_FOUND',\n [ErrorCode.NODE_NOT_FOUND]: 'NODE_NOT_FOUND',\n [ErrorCode.NO_SELECTION]: 'NO_SELECTION',\n [ErrorCode.MG_UNAVAILABLE]: 'MG_UNAVAILABLE',\n [ErrorCode.EXPORT_FAILED]: 'EXPORT_FAILED',\n [ErrorCode.FILE_WRITE_FAILED]: 'FILE_WRITE_FAILED',\n [ErrorCode.INVALID_LINK]: 'INVALID_LINK',\n [ErrorCode.INVALID_PARAMS]: 'INVALID_PARAMS',\n [ErrorCode.REQUEST_TIMEOUT]: 'REQUEST_TIMEOUT',\n [ErrorCode.PORT_EXHAUSTED]: 'PORT_EXHAUSTED',\n [ErrorCode.SERVER_DISCOVERY_FAILED]: 'SERVER_DISCOVERY_FAILED',\n [ErrorCode.SERVER_START_FAILED]: 'SERVER_START_FAILED',\n [ErrorCode.SERVER_ALREADY_RUNNING]: 'SERVER_ALREADY_RUNNING',\n [ErrorCode.CONNECTION_LOST]: 'CONNECTION_LOST',\n [ErrorCode.UNKNOWN_ERROR]: 'UNKNOWN_ERROR',\n}\n\n/** 错误码对应的默认消息 */\nexport const ErrorMessages: Record<ErrorCode, string> = {\n [ErrorCode.CONNECTION_FAILED]: '无法连接到 MG Server',\n [ErrorCode.CONNECTION_TIMEOUT]: '连接超时',\n [ErrorCode.NO_PAGE_CONNECTED]: '没有 MasterGo 页面连接到 Server。请安装 MG Plugin 浏览器扩展: https://chromewebstore.google.com/detail/mg-plugin/ddhihanlpcdneicohnglnaliefnkaeja',\n [ErrorCode.PAGE_NOT_FOUND]: '未找到匹配的页面',\n [ErrorCode.NODE_NOT_FOUND]: '节点不存在',\n [ErrorCode.NO_SELECTION]: '没有选中任何节点',\n [ErrorCode.MG_UNAVAILABLE]: 'mg 对象不可用',\n [ErrorCode.EXPORT_FAILED]: '导出图片失败',\n [ErrorCode.FILE_WRITE_FAILED]: '文件写入失败',\n [ErrorCode.INVALID_LINK]: '无效的 mgp:// 链接格式',\n [ErrorCode.INVALID_PARAMS]: '参数校验失败',\n [ErrorCode.REQUEST_TIMEOUT]: '请求超时',\n [ErrorCode.PORT_EXHAUSTED]: '所有备选端口均被占用',\n [ErrorCode.SERVER_DISCOVERY_FAILED]: '无法发现 Server (端口扫描失败)',\n [ErrorCode.SERVER_START_FAILED]: '自动启动 Server 失败',\n [ErrorCode.SERVER_ALREADY_RUNNING]: 'Server 已在运行中',\n [ErrorCode.CONNECTION_LOST]: '连接断开',\n [ErrorCode.UNKNOWN_ERROR]: '未知错误',\n}\n\n/** MG Plugin 错误类 */\nexport class MGError extends Error {\n /** 错误码 */\n code: ErrorCode\n /** 错误名称 */\n errorName: string\n /** 额外详情 */\n details?: Record<string, unknown>\n\n constructor(code: ErrorCode, message?: string, details?: Record<string, unknown>) {\n super(message || ErrorMessages[code])\n this.name = 'MGError'\n this.code = code\n this.errorName = ErrorNames[code]\n this.details = details\n }\n\n /** 转换为 JSON 格式 */\n toJSON() {\n return {\n code: this.code,\n name: this.errorName,\n message: this.message,\n details: this.details,\n }\n }\n\n /** 格式化输出 */\n toString() {\n return `错误 [${this.code}]: ${this.message}`\n }\n}\n\n/**\n * 创建错误\n */\nexport function createError(\n code: ErrorCode,\n message?: string,\n details?: Record<string, unknown>\n): MGError {\n return new MGError(code, message, details)\n}\n","/**\n * WebSocket 服务器\n */\n\nimport { WebSocketServer, WebSocket } from 'ws'\nimport type { IncomingMessage } from 'node:http'\nimport {\n ConnectionType,\n MessageType,\n DEFAULT_PORT,\n PORT_RANGE_START,\n PORT_RANGE_END,\n HEARTBEAT_INTERVAL,\n} from '../shared/constants.js'\nimport { ErrorCode, MGError } from '../shared/errors.js'\nimport type { BaseMessage, RegisterMessage, ResponseMessage, ServerStatusResponse, ConnectedPageInfo } from '../shared/types.js'\nimport { formatDuration } from '../shared/utils.js'\nimport { Logger, createLogger } from './logger.js'\nimport { ConnectionManager, ManagedWebSocket } from './connection-manager.js'\nimport { RequestHandler } from './request-handler.js'\nimport { getVersion } from '../shared/version.js'\n\nexport interface ServerOptions {\n /** 监听端口 */\n port?: number\n /** 日志选项 */\n logger?: Logger\n}\n\n/**\n * MG WebSocket 服务器\n */\nexport class MGServer {\n private wss: WebSocketServer | null = null\n private logger: Logger\n private connectionManager: ConnectionManager\n private requestHandler: RequestHandler\n\n private port: number\n private isRunning = false\n private startedAt: Date | null = null\n\n constructor(options: ServerOptions = {}) {\n this.port = options.port || DEFAULT_PORT\n this.logger = options.logger || createLogger()\n this.connectionManager = new ConnectionManager(this.logger)\n this.requestHandler = new RequestHandler(this.connectionManager, this.logger)\n }\n\n /**\n * 启动服务器\n */\n async start(): Promise<number> {\n if (this.isRunning) {\n throw new MGError(ErrorCode.SERVER_ALREADY_RUNNING, 'Server 已在运行中')\n }\n\n // 尝试绑定端口\n const port = await this.findAvailablePort()\n\n return new Promise((resolve, reject) => {\n this.wss = new WebSocketServer({ port })\n\n this.wss.on('listening', () => {\n this.port = port\n this.isRunning = true\n this.startedAt = new Date()\n this.logger.info(`Server 启动成功,监听端口: ${port}`)\n\n // 启动心跳检查\n this.connectionManager.startHeartbeatCheck(HEARTBEAT_INTERVAL)\n\n resolve(port)\n })\n\n this.wss.on('error', (error: NodeJS.ErrnoException) => {\n this.logger.error('Server 错误:', error)\n reject(error)\n })\n\n this.wss.on('connection', (ws: WebSocket, request: IncomingMessage) => {\n this.handleConnection(ws, request)\n })\n })\n }\n\n /**\n * 查找可用端口\n */\n private async findAvailablePort(): Promise<number> {\n for (let port = PORT_RANGE_START; port <= PORT_RANGE_END; port++) {\n const available = await this.isPortAvailable(port)\n if (available) {\n return port\n }\n this.logger.debug(`端口 ${port} 被占用,尝试下一个`)\n }\n\n throw new MGError(\n ErrorCode.PORT_EXHAUSTED,\n `端口 ${PORT_RANGE_START}-${PORT_RANGE_END} 均被占用`\n )\n }\n\n /**\n * 检查端口是否可用\n */\n private isPortAvailable(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const testServer = new WebSocketServer({ port })\n\n testServer.on('listening', () => {\n testServer.close()\n resolve(true)\n })\n\n testServer.on('error', () => {\n resolve(false)\n })\n })\n }\n\n /**\n * 处理新连接\n */\n private handleConnection(ws: WebSocket, _request: IncomingMessage): void {\n this.logger.info('新连接建立')\n\n // 等待注册消息\n const registerTimeout = setTimeout(() => {\n this.logger.warn('连接注册超时,关闭连接')\n ws.close()\n }, 5000)\n\n ws.on('message', (data) => {\n try {\n const message = JSON.parse(data.toString()) as BaseMessage\n\n // 处理注册消息\n if (message.type === MessageType.REGISTER) {\n clearTimeout(registerTimeout)\n this.handleRegister(ws, message as RegisterMessage)\n return\n }\n\n // 其他消息需要已注册\n const managedWs = ws as ManagedWebSocket\n if (!managedWs.connectionId) {\n this.logger.warn('未注册的连接发送消息,忽略')\n return\n }\n\n this.handleMessage(managedWs, message)\n } catch (error) {\n this.logger.error('消息解析失败:', error)\n }\n })\n\n ws.on('close', () => {\n clearTimeout(registerTimeout)\n const managedWs = ws as ManagedWebSocket\n if (managedWs.connectionId) {\n this.requestHandler.cleanupConnectionRequests(managedWs.connectionId)\n this.connectionManager.removeConnection(managedWs)\n }\n })\n\n ws.on('error', (error) => {\n this.logger.error('WebSocket 错误:', error)\n })\n }\n\n /**\n * 处理注册消息\n */\n private handleRegister(ws: WebSocket, message: RegisterMessage): void {\n const { connectionType, pageUrl, pageId } = message.data\n\n const managedWs = this.connectionManager.addConnection(\n ws,\n connectionType,\n pageUrl,\n pageId\n )\n\n // 发送注册确认(包含 Server 版本信息)\n const ack: ResponseMessage = {\n id: message.id || '',\n type: MessageType.REGISTER_ACK,\n success: true,\n data: {\n connectionId: managedWs.connectionId,\n pageUrl,\n serverVersion: getVersion(),\n },\n }\n\n ws.send(JSON.stringify(ack))\n }\n\n /**\n * 处理消息\n */\n private handleMessage(ws: ManagedWebSocket, message: BaseMessage): void {\n this.connectionManager.updateLastActive(ws)\n\n switch (message.type) {\n case MessageType.PING:\n this.handlePing(ws, message)\n break\n\n case MessageType.GET_SERVER_STATUS:\n // 直接处理 Server 状态查询,不转发给 Provider\n this.handleGetServerStatus(ws, message)\n break\n\n case MessageType.RESPONSE:\n case MessageType.ERROR:\n // Provider 返回的响应\n this.requestHandler.handleResponse(message as ResponseMessage)\n break\n\n default:\n // Consumer 发送的请求\n if (ws.connectionInfo.type === ConnectionType.CONSUMER) {\n this.requestHandler.handleRequest(ws, message as any)\n }\n break\n }\n }\n\n /**\n * 处理心跳\n */\n private handlePing(ws: ManagedWebSocket, message: BaseMessage): void {\n const pong = {\n type: MessageType.PONG,\n timestamp: (message as any).timestamp || Date.now(),\n }\n ws.send(JSON.stringify(pong))\n }\n\n /**\n * 处理 Server 状态查询\n */\n private handleGetServerStatus(ws: ManagedWebSocket, message: BaseMessage): void {\n const providers = this.connectionManager.getAllProviders()\n const stats = this.connectionManager.getStats()\n\n // 构建连接页面信息\n const connectedPages: ConnectedPageInfo[] = providers.map((info) => ({\n pageUrl: info.pageUrl || '',\n connectedAt: info.connectedAt.toISOString(),\n lastActiveAt: info.lastActiveAt.toISOString(),\n }))\n\n const uptimeMs = this.startedAt ? Date.now() - this.startedAt.getTime() : 0\n\n const statusData: ServerStatusResponse = {\n running: this.isRunning,\n port: this.port,\n pid: process.pid,\n startedAt: this.startedAt?.toISOString() || '',\n uptime: formatDuration(uptimeMs),\n version: getVersion(),\n stats,\n connectedPages,\n }\n\n const response: ResponseMessage = {\n id: message.id || '',\n type: MessageType.RESPONSE,\n success: true,\n data: statusData,\n }\n\n ws.send(JSON.stringify(response))\n this.logger.info('返回 Server 状态信息')\n }\n\n /**\n * 停止服务器\n */\n async stop(): Promise<void> {\n if (!this.isRunning || !this.wss) {\n return\n }\n\n this.logger.info('正在停止 Server...')\n\n // 清理所有请求\n this.requestHandler.cleanupAll()\n\n // 关闭所有连接\n this.connectionManager.closeAll()\n\n // 关闭服务器\n return new Promise((resolve) => {\n this.wss!.close(() => {\n this.isRunning = false\n this.wss = null\n this.logger.info('Server 已停止')\n resolve()\n })\n })\n }\n\n /**\n * 获取运行状态\n */\n getStatus(): {\n running: boolean\n port: number\n stats: { providers: number; consumers: number; total: number }\n connectedPages: string[]\n } {\n return {\n running: this.isRunning,\n port: this.port,\n stats: this.connectionManager.getStats(),\n connectedPages: this.connectionManager.getConnectedPageUrls(),\n }\n }\n\n /**\n * 获取端口\n */\n getPort(): number {\n return this.port\n }\n\n /**\n * 是否运行中\n */\n isServerRunning(): boolean {\n return this.isRunning\n }\n}\n\n/**\n * 创建服务器实例\n */\nexport function createServer(options?: ServerOptions): MGServer {\n return new MGServer(options)\n}\n","/**\n * 日志模块\n */\n\nimport { appendFileSync, existsSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { SERVER_LOG_FILE } from '../shared/constants.js'\nimport { formatLogTime } from '../shared/utils.js'\n\nexport enum LogLevel {\n DEBUG = 'DEBUG',\n INFO = 'INFO',\n WARN = 'WARN',\n ERROR = 'ERROR',\n}\n\nexport interface LoggerOptions {\n /** 是否输出到控制台 */\n console?: boolean\n /** 是否输出到文件 */\n file?: boolean\n /** 日志文件路径 */\n filePath?: string\n /** 最小日志级别 */\n minLevel?: LogLevel\n}\n\nconst levelPriority: Record<LogLevel, number> = {\n [LogLevel.DEBUG]: 0,\n [LogLevel.INFO]: 1,\n [LogLevel.WARN]: 2,\n [LogLevel.ERROR]: 3,\n}\n\n/**\n * 日志记录器\n */\nexport class Logger {\n private options: Required<LoggerOptions>\n\n constructor(options: LoggerOptions = {}) {\n this.options = {\n console: options.console ?? true,\n file: options.file ?? false,\n filePath: options.filePath ?? SERVER_LOG_FILE,\n minLevel: options.minLevel ?? LogLevel.INFO,\n }\n\n // 确保日志目录存在\n if (this.options.file) {\n const dir = dirname(this.options.filePath)\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n }\n }\n\n /**\n * 记录日志\n */\n private log(level: LogLevel, message: string, ...args: unknown[]): void {\n // 检查日志级别\n if (levelPriority[level] < levelPriority[this.options.minLevel]) {\n return\n }\n\n const timestamp = formatLogTime()\n const formattedMessage = `[${timestamp}] [${level}] ${message}`\n\n // 输出到控制台\n if (this.options.console) {\n const consoleMethod = level === LogLevel.ERROR ? console.error : console.log\n if (args.length > 0) {\n consoleMethod(formattedMessage, ...args)\n } else {\n consoleMethod(formattedMessage)\n }\n }\n\n // 输出到文件\n if (this.options.file) {\n try {\n const fileMessage =\n args.length > 0\n ? `${formattedMessage} ${JSON.stringify(args)}\\n`\n : `${formattedMessage}\\n`\n appendFileSync(this.options.filePath, fileMessage)\n } catch (error) {\n // 文件写入失败时静默处理\n if (this.options.console) {\n console.error('日志文件写入失败:', error)\n }\n }\n }\n }\n\n debug(message: string, ...args: unknown[]): void {\n this.log(LogLevel.DEBUG, message, ...args)\n }\n\n info(message: string, ...args: unknown[]): void {\n this.log(LogLevel.INFO, message, ...args)\n }\n\n warn(message: string, ...args: unknown[]): void {\n this.log(LogLevel.WARN, message, ...args)\n }\n\n error(message: string, ...args: unknown[]): void {\n this.log(LogLevel.ERROR, message, ...args)\n }\n}\n\n/** 创建默认日志记录器 */\nexport function createLogger(options?: LoggerOptions): Logger {\n return new Logger(options)\n}\n","/**\n * 连接管理器\n * 管理 Provider 和 Consumer 的 WebSocket 连接\n */\n\nimport type { WebSocket } from 'ws'\nimport { ConnectionType, HEARTBEAT_TIMEOUT } from '../shared/constants.js'\nimport type { ConnectionInfo } from '../shared/types.js'\nimport { generateId } from '../shared/utils.js'\nimport { Logger } from './logger.js'\n\n/** 带连接信息的 WebSocket */\nexport interface ManagedWebSocket extends WebSocket {\n /** 连接 ID */\n connectionId: string\n /** 连接信息 */\n connectionInfo: ConnectionInfo\n /** 是否存活 */\n isAlive: boolean\n}\n\n/**\n * 连接管理器\n */\nexport class ConnectionManager {\n private logger: Logger\n\n /** Provider 连接(按页面 URL 索引,支持同 URL 多个连接) */\n private providers = new Map<string, ManagedWebSocket[]>()\n\n /** Consumer 连接 */\n private consumers = new Map<string, ManagedWebSocket>()\n\n /** 所有连接(按 ID 索引) */\n private allConnections = new Map<string, ManagedWebSocket>()\n\n /** 心跳检查定时器 */\n private heartbeatTimer: NodeJS.Timeout | null = null\n\n constructor(logger: Logger) {\n this.logger = logger\n }\n\n /**\n * 启动心跳检查\n */\n startHeartbeatCheck(interval: number = 30000): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n }\n\n this.heartbeatTimer = setInterval(() => {\n this.checkHeartbeats()\n }, interval)\n }\n\n /**\n * 停止心跳检查\n */\n stopHeartbeatCheck(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = null\n }\n }\n\n /**\n * 检查所有连接的心跳\n */\n private checkHeartbeats(): void {\n const now = Date.now()\n\n const entries = Array.from(this.allConnections.entries())\n for (const [id, ws] of entries) {\n const lastActive = ws.connectionInfo.lastActiveAt.getTime()\n const elapsed = now - lastActive\n\n if (elapsed > HEARTBEAT_TIMEOUT) {\n this.logger.warn(`连接 ${id} 心跳超时,关闭连接`)\n this.removeConnection(ws)\n ws.terminate()\n }\n }\n }\n\n /**\n * 添加连接\n */\n addConnection(\n ws: WebSocket,\n type: ConnectionType,\n pageUrl?: string,\n pageId?: string\n ): ManagedWebSocket {\n const connectionId = generateId()\n const now = new Date()\n\n const connectionInfo: ConnectionInfo = {\n id: connectionId,\n type,\n pageUrl,\n pageId,\n connectedAt: now,\n lastActiveAt: now,\n }\n\n const managedWs = ws as ManagedWebSocket\n managedWs.connectionId = connectionId\n managedWs.connectionInfo = connectionInfo\n managedWs.isAlive = true\n\n this.allConnections.set(connectionId, managedWs)\n\n if (type === ConnectionType.PROVIDER && pageUrl) {\n // 获取已有的连接数组,如果没有则创建新数组\n const existing = this.providers.get(pageUrl) || []\n existing.push(managedWs)\n this.providers.set(pageUrl, existing)\n this.logger.info(`Provider 连接: ${pageUrl} (当前该页面连接数: ${existing.length})`)\n } else if (type === ConnectionType.CONSUMER) {\n this.consumers.set(connectionId, managedWs)\n this.logger.info(`Consumer 连接: ${connectionId}`)\n }\n\n return managedWs\n }\n\n /**\n * 移除连接\n */\n removeConnection(ws: ManagedWebSocket): void {\n const { connectionId, connectionInfo } = ws\n\n this.allConnections.delete(connectionId)\n\n if (connectionInfo.type === ConnectionType.PROVIDER && connectionInfo.pageUrl) {\n const connections = this.providers.get(connectionInfo.pageUrl)\n if (connections) {\n const index = connections.findIndex(c => c.connectionId === connectionId)\n if (index !== -1) {\n connections.splice(index, 1)\n // 如果该 URL 没有连接了,删除整个 key\n if (connections.length === 0) {\n this.providers.delete(connectionInfo.pageUrl)\n }\n }\n }\n this.logger.info(`Provider 断开: ${connectionInfo.pageUrl} (连接ID: ${connectionId})`)\n } else if (connectionInfo.type === ConnectionType.CONSUMER) {\n this.consumers.delete(connectionId)\n this.logger.info(`Consumer 断开: ${connectionId}`)\n }\n }\n\n /**\n * 更新连接活跃时间\n */\n updateLastActive(ws: ManagedWebSocket): void {\n ws.connectionInfo.lastActiveAt = new Date()\n ws.isAlive = true\n }\n\n /**\n * 根据页面 URL 查找 Provider(返回第一个可用的连接)\n */\n findProviderByPageUrl(pageUrl: string): ManagedWebSocket | undefined {\n const connections = this.providers.get(pageUrl)\n return connections?.[0]\n }\n\n /**\n * 获取第一个可用的 Provider\n */\n getFirstProvider(): ManagedWebSocket | undefined {\n const allConnections = Array.from(this.providers.values())\n for (const connections of allConnections) {\n if (connections.length > 0) {\n return connections[0]\n }\n }\n return undefined\n }\n\n /**\n * 获取所有 Provider 信息\n */\n getAllProviders(): ConnectionInfo[] {\n const result: ConnectionInfo[] = []\n const allConnections = Array.from(this.providers.values())\n for (const connections of allConnections) {\n for (const ws of connections) {\n result.push(ws.connectionInfo)\n }\n }\n return result\n }\n\n /**\n * 获取连接统计\n */\n getStats(): { providers: number; consumers: number; total: number } {\n // 统计所有 provider 连接数量(不是 URL 数量)\n let providerCount = 0\n const allConnections = Array.from(this.providers.values())\n for (const connections of allConnections) {\n providerCount += connections.length\n }\n return {\n providers: providerCount,\n consumers: this.consumers.size,\n total: this.allConnections.size,\n }\n }\n\n /**\n * 获取所有已连接的页面 URL\n */\n getConnectedPageUrls(): string[] {\n return Array.from(this.providers.keys())\n }\n\n /**\n * 关闭所有连接\n */\n closeAll(): void {\n this.stopHeartbeatCheck()\n\n const allWs = Array.from(this.allConnections.values())\n for (const ws of allWs) {\n ws.close()\n }\n\n this.providers.clear()\n this.consumers.clear()\n this.allConnections.clear()\n }\n}\n","/**\n * 请求处理器\n * 处理 Consumer 请求,转发给 Provider\n */\n\nimport type { ManagedWebSocket } from './connection-manager.js'\nimport { ConnectionManager } from './connection-manager.js'\nimport { Logger } from './logger.js'\nimport { MessageType, REQUEST_TIMEOUT } from '../shared/constants.js'\nimport { ErrorCode, MGError, ErrorMessages } from '../shared/errors.js'\nimport type { RequestMessage, ResponseMessage } from '../shared/types.js'\nimport { generateId } from '../shared/utils.js'\n\n/** 待处理的请求 */\ninterface PendingRequest {\n /** 请求 ID */\n id: string\n /** Consumer WebSocket */\n consumer: ManagedWebSocket\n /** 超时定时器 */\n timer: NodeJS.Timeout\n /** 请求时间 */\n timestamp: number\n}\n\n/**\n * 请求处理器\n */\nexport class RequestHandler {\n private logger: Logger\n private connectionManager: ConnectionManager\n\n /** 待处理的请求 */\n private pendingRequests = new Map<string, PendingRequest>()\n\n constructor(connectionManager: ConnectionManager, logger: Logger) {\n this.connectionManager = connectionManager\n this.logger = logger\n }\n\n /**\n * 处理 Consumer 请求\n */\n async handleRequest(consumer: ManagedWebSocket, message: RequestMessage): Promise<void> {\n const requestId = message.id || generateId()\n const { type, pageUrl, params } = message\n\n this.logger.info(`收到请求: ${type} (${requestId})`, { pageUrl })\n\n // 查找目标 Provider\n let provider: ManagedWebSocket | undefined\n\n if (pageUrl) {\n provider = this.connectionManager.findProviderByPageUrl(pageUrl)\n if (!provider) {\n this.sendError(consumer, requestId, ErrorCode.PAGE_NOT_FOUND, `未找到页面: ${pageUrl}`)\n return\n }\n } else {\n // 没有指定 pageUrl,使用第一个可用的 Provider\n provider = this.connectionManager.getFirstProvider()\n if (!provider) {\n this.sendError(consumer, requestId, ErrorCode.NO_PAGE_CONNECTED, ErrorMessages[ErrorCode.NO_PAGE_CONNECTED])\n return\n }\n }\n\n // 创建超时定时器\n const timer = setTimeout(() => {\n this.handleTimeout(requestId)\n }, REQUEST_TIMEOUT)\n\n // 保存待处理请求\n this.pendingRequests.set(requestId, {\n id: requestId,\n consumer,\n timer,\n timestamp: Date.now(),\n })\n\n // 转发请求给 Provider\n const forwardMessage: RequestMessage = {\n id: requestId,\n type,\n pageUrl: pageUrl || provider.connectionInfo.pageUrl,\n params,\n timestamp: Date.now(),\n }\n\n try {\n provider.send(JSON.stringify(forwardMessage))\n this.logger.info(`请求转发: ${type} -> ${provider.connectionInfo.pageUrl}`)\n } catch {\n this.cleanupRequest(requestId)\n this.sendError(consumer, requestId, ErrorCode.CONNECTION_FAILED, '转发请求失败')\n }\n }\n\n /**\n * 处理 Provider 响应\n */\n handleResponse(response: ResponseMessage): void {\n const { id } = response\n const pending = this.pendingRequests.get(id)\n\n if (!pending) {\n this.logger.warn(`收到未知请求的响应: ${id}`)\n return\n }\n\n this.cleanupRequest(id)\n\n // 转发响应给 Consumer\n try {\n pending.consumer.send(JSON.stringify(response))\n this.logger.info(\n `响应返回: ${id} (${response.success ? '成功' : '失败'})`\n )\n } catch (error) {\n this.logger.error(`响应转发失败: ${id}`, error)\n }\n }\n\n /**\n * 处理请求超时\n */\n private handleTimeout(requestId: string): void {\n const pending = this.pendingRequests.get(requestId)\n if (!pending) return\n\n this.logger.warn(`请求超时: ${requestId}`)\n this.cleanupRequest(requestId)\n\n this.sendError(pending.consumer, requestId, ErrorCode.REQUEST_TIMEOUT, '请求超时')\n }\n\n /**\n * 清理请求\n */\n private cleanupRequest(requestId: string): void {\n const pending = this.pendingRequests.get(requestId)\n if (pending) {\n clearTimeout(pending.timer)\n this.pendingRequests.delete(requestId)\n }\n }\n\n /**\n * 发送错误响应\n */\n private sendError(\n consumer: ManagedWebSocket,\n requestId: string,\n code: ErrorCode,\n message: string\n ): void {\n const error = new MGError(code, message)\n const response: ResponseMessage = {\n id: requestId,\n type: MessageType.ERROR,\n success: false,\n data: null,\n error: error.toJSON(),\n }\n\n try {\n consumer.send(JSON.stringify(response))\n } catch (err) {\n this.logger.error(`发送错误响应失败: ${requestId}`, err)\n }\n }\n\n /**\n * 清理特定连接的所有待处理请求\n */\n cleanupConnectionRequests(connectionId: string): void {\n for (const [requestId, pending] of this.pendingRequests) {\n if (pending.consumer.connectionId === connectionId) {\n this.cleanupRequest(requestId)\n }\n }\n }\n\n /**\n * 清理所有待处理请求\n */\n cleanupAll(): void {\n for (const [requestId] of this.pendingRequests) {\n this.cleanupRequest(requestId)\n }\n }\n}\n","/**\n * 版本管理模块\n * 提供获取当前 CLI 版本的函数\n * 优先从 package.json 读取版本号(本地开发默认 9.9.9)\n * CI 发布时会用 VERSION 文件覆盖 package.json\n */\n\nimport { readFileSync, existsSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { dirname, join } from 'node:path'\n\n/** 缓存的版本号 */\nlet cachedVersion: string | null = null\n\n/**\n * 获取当前 CLI 版本号\n * 优先从 package.json 读取(本地开发默认 9.9.9)\n * CI 发布时会用 VERSION 文件的值覆盖 package.json\n */\nexport function getVersion(): string {\n if (cachedVersion !== null) {\n return cachedVersion\n }\n\n try {\n // 获取当前模块的目录\n const currentFile = fileURLToPath(import.meta.url)\n const currentDir = dirname(currentFile)\n\n // 优先从 package.json 读取版本号\n // 本地开发时 package.json 固定为 9.9.9\n // CI 发布时会用 VERSION 文件覆盖 package.json 后再打包\n const packageJsonPaths = [\n join(currentDir, '..', 'package.json'), // dist/xxx.js -> ../package.json\n join(currentDir, '..', '..', 'package.json'), // src/shared/version.ts -> ../../package.json\n ]\n\n for (const packageJsonPath of packageJsonPaths) {\n if (existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))\n if (packageJson.name === '@hangox/mg-cli') {\n cachedVersion = (packageJson.version as string) || '0.0.0'\n return cachedVersion\n }\n }\n }\n\n // 如果找不到,返回默认版本\n cachedVersion = '0.0.0'\n return cachedVersion\n } catch {\n // 如果读取失败,返回默认版本\n cachedVersion = '0.0.0'\n return cachedVersion\n }\n}\n\n/** 开发版本标识 */\nconst DEV_VERSION = '9.9.9'\n\n/**\n * 比较两个版本是否一致\n * 9.9.9 是开发版本,与任何版本都视为匹配\n */\nexport function isVersionMatch(version1: string, version2: string): boolean {\n // 开发版本 9.9.9 与任何版本都视为匹配\n if (version1 === DEV_VERSION || version2 === DEV_VERSION) {\n return true\n }\n return version1 === version2\n}\n\n/**\n * 清除缓存的版本号(用于测试)\n */\nexport function clearVersionCache(): void {\n cachedVersion = null\n}\n","/**\n * CLI 客户端\n * 负责连接 Server 发送请求\n */\n\nimport WebSocket from 'ws'\nimport { readServerInfo, parseMgpLink, isProcessRunning } from '../shared/utils.js'\nimport {\n PORT_RANGE_START,\n PORT_RANGE_END,\n PORT_SCAN_TIMEOUT,\n REQUEST_TIMEOUT,\n SERVER_START_TIMEOUT,\n RETRY_INTERVALS,\n MAX_RETRY_COUNT,\n ConnectionType,\n MessageType,\n} from '../shared/constants.js'\nimport { ErrorCode, MGError, ErrorMessages } from '../shared/errors.js'\nimport { startServerDaemon } from '../server/daemon.js'\nimport { generateId } from '../shared/utils.js'\nimport { getVersion, isVersionMatch } from '../shared/version.js'\nimport type { RequestMessage, ResponseMessage } from '../shared/types.js'\n\n/** 客户端选项 */\nexport interface ClientOptions {\n /** 禁用自动启动 Server */\n noAutoStart?: boolean\n /** 禁用重试 */\n noRetry?: boolean\n}\n\n/**\n * MG CLI 客户端\n */\nexport class MGClient {\n private ws: WebSocket | null = null\n private options: ClientOptions\n\n constructor(options: ClientOptions = {}) {\n this.options = options\n }\n\n /**\n * 连接到 Server\n */\n async connect(): Promise<void> {\n // 1. 尝试从文件读取端口和版本信息\n const serverInfo = readServerInfo()\n if (serverInfo) {\n // 检查进程是否存在\n if (isProcessRunning(serverInfo.pid)) {\n // 检查版本是否匹配\n const currentVersion = getVersion()\n if (!isVersionMatch(currentVersion, serverInfo.version)) {\n // 版本不一致,只警告不自动重启\n console.warn(`⚠️ 版本不匹配: CLI ${currentVersion} vs Server ${serverInfo.version}`)\n console.warn('提示: 如需对齐版本,请手动运行 `npx -y @hangox/mg-cli@latest server restart`')\n }\n\n // 版本一致,尝试连接\n try {\n await this.tryConnect(serverInfo.port)\n return\n } catch {\n // 连接失败,继续扫描\n }\n }\n }\n\n // 2. 端口扫描\n for (let port = PORT_RANGE_START; port <= PORT_RANGE_END; port++) {\n try {\n await this.tryConnect(port)\n return\n } catch {\n // 继续尝试下一个端口\n }\n }\n\n // 3. 自动启动 Server\n if (!this.options.noAutoStart) {\n console.log('Server 未运行,正在自动启动...')\n try {\n const info = await startServerDaemon()\n console.log(`Server 已启动,端口: ${info.port}`)\n\n // 等待 Server 就绪\n await this.waitForServer(info.port)\n return\n } catch (error) {\n throw new MGError(\n ErrorCode.SERVER_START_FAILED,\n `自动启动 Server 失败: ${error instanceof Error ? error.message : error}`\n )\n }\n }\n\n throw new MGError(ErrorCode.CONNECTION_FAILED, ErrorMessages[ErrorCode.CONNECTION_FAILED])\n }\n\n /**\n * 尝试连接指定端口\n */\n private tryConnect(port: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(`ws://localhost:${port}`)\n const timer = setTimeout(() => {\n ws.close()\n reject(new Error('连接超时'))\n }, PORT_SCAN_TIMEOUT)\n\n ws.on('open', () => {\n clearTimeout(timer)\n this.ws = ws\n // 注册为 Consumer\n this.register()\n resolve()\n })\n\n ws.on('error', (error) => {\n clearTimeout(timer)\n reject(error)\n })\n })\n }\n\n /**\n * 等待 Server 就绪\n */\n private async waitForServer(port: number): Promise<void> {\n const startTime = Date.now()\n const interval = 500\n\n while (Date.now() - startTime < SERVER_START_TIMEOUT) {\n try {\n await this.tryConnect(port)\n return\n } catch {\n await new Promise((r) => setTimeout(r, interval))\n }\n }\n\n throw new Error('等待 Server 启动超时')\n }\n\n /**\n * 注册为 Consumer\n */\n private register(): void {\n if (!this.ws) return\n\n const message = {\n type: MessageType.REGISTER,\n data: {\n connectionType: ConnectionType.CONSUMER,\n },\n timestamp: Date.now(),\n }\n\n this.ws.send(JSON.stringify(message))\n }\n\n /**\n * 发送请求并等待响应\n */\n async request<T = unknown>(\n type: MessageType,\n params?: Record<string, unknown>,\n pageUrl?: string\n ): Promise<T> {\n if (!this.ws) {\n throw new MGError(ErrorCode.CONNECTION_FAILED, '未连接到 Server')\n }\n\n const requestId = generateId()\n const message: RequestMessage = {\n id: requestId,\n type,\n params,\n pageUrl,\n timestamp: Date.now(),\n }\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new MGError(ErrorCode.REQUEST_TIMEOUT, ErrorMessages[ErrorCode.REQUEST_TIMEOUT]))\n }, REQUEST_TIMEOUT)\n\n const messageHandler = (data: WebSocket.Data) => {\n try {\n const response = JSON.parse(data.toString()) as ResponseMessage\n if (response.id === requestId) {\n clearTimeout(timer)\n this.ws?.off('message', messageHandler)\n\n if (response.success) {\n resolve(response.data as T)\n } else {\n const error = response.error\n reject(\n new MGError(\n error?.code || ErrorCode.UNKNOWN_ERROR,\n error?.message || '未知错误'\n )\n )\n }\n }\n } catch {\n // 忽略解析错误\n }\n }\n\n this.ws!.on('message', messageHandler)\n this.ws!.send(JSON.stringify(message))\n })\n }\n\n /**\n * 带重试的请求\n */\n async requestWithRetry<T = unknown>(\n type: MessageType,\n params?: Record<string, unknown>,\n pageUrl?: string\n ): Promise<T> {\n if (this.options.noRetry) {\n return this.request<T>(type, params, pageUrl)\n }\n\n let lastError: Error | null = null\n\n for (let attempt = 0; attempt <= MAX_RETRY_COUNT; attempt++) {\n try {\n return await this.request<T>(type, params, pageUrl)\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error))\n\n // 检查是否是可重试的错误\n if (error instanceof MGError) {\n const retryable = [ErrorCode.CONNECTION_LOST, ErrorCode.REQUEST_TIMEOUT]\n if (!retryable.includes(error.code)) {\n throw error\n }\n }\n\n // 最后一次尝试不再等待\n if (attempt < MAX_RETRY_COUNT) {\n const delay = RETRY_INTERVALS[attempt] || RETRY_INTERVALS[RETRY_INTERVALS.length - 1]\n await new Promise((r) => setTimeout(r, delay))\n\n // 重新连接\n try {\n await this.connect()\n } catch {\n // 重连失败,继续重试\n }\n }\n }\n }\n\n throw lastError || new MGError(ErrorCode.UNKNOWN_ERROR, '请求失败')\n }\n\n /**\n * 关闭连接\n */\n close(): void {\n if (this.ws) {\n this.ws.close()\n this.ws = null\n }\n }\n}\n\n/**\n * 解析 mgp:// 链接\n */\nexport { parseMgpLink }\n","/**\n * get_node_by_id 命令\n * 根据节点 ID 获取节点详细信息\n */\n\nimport { Command } from 'commander'\nimport { writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient } from '../client.js'\nimport { trimNodeDefaults } from '../../shared/utils.js'\nimport type { GetNodeParams, NodeInfo } from '../../shared/types.js'\n\ninterface GetNodeByIdOptions {\n nodeId: string\n output: string\n domain?: string\n fileId?: string\n maxDepth?: string\n includeInvisible?: boolean\n raw?: boolean\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/**\n * 创建 get_node_by_id 命令\n */\nexport function createGetNodeByIdCommand(): Command {\n return new Command('get_node_by_id')\n .description('根据节点 ID 获取节点详细信息。数据保存到指定 JSON 文件,返回文件路径和大小信息。如需通过链接获取,请使用 get_node_by_link 命令')\n .requiredOption('--nodeId <id>', '节点 ID,格式如 123:456。可从 MasterGo 浮窗链接中获取')\n .requiredOption('--output <path>', '输出 JSON 文件路径。支持绝对路径或相对路径')\n .option('--domain <domain>', 'MasterGo 域名,默认 mastergo.netease.com。与 --fileId 配合使用', 'mastergo.netease.com')\n .option('--fileId <id>', '文件 ID(纯数字),与 --domain 配合指定目标页面')\n .option('--maxDepth <number>', '遍历深度,默认 1。增加深度会显著增加数据量', '1')\n .option('--includeInvisible', '包含不可见节点(visible: false),默认不包含', false)\n .option('--raw', '保留原始数据,不精简默认值字段', false)\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (options: GetNodeByIdOptions) => {\n await handleGetNodeById(options)\n })\n}\n\n/**\n * 处理 get_node_by_id 命令\n */\nasync function handleGetNodeById(options: GetNodeByIdOptions): Promise<void> {\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n // 构建 pageUrl(用于精确指定目标页面)\n let pageUrl: string | undefined\n if (options.fileId) {\n // 使用 fileId 和 domain 构建 pageUrl\n const domain = options.domain || 'mastergo.netease.com'\n pageUrl = `${domain}/file/${options.fileId}`\n }\n\n // 发送请求\n const params: GetNodeParams = {\n nodeId: options.nodeId,\n maxDepth: parseInt(options.maxDepth || '1', 10),\n includeInvisible: options.includeInvisible || false,\n }\n\n const data = await client.requestWithRetry<NodeInfo>(MessageType.GET_NODE_BY_ID, params, pageUrl)\n\n // 精简数据(除非使用 --raw 选项)\n const outputData = options.raw ? data : trimNodeDefaults(data as Record<string, unknown>)\n\n // 保存到文件\n const outputPath = resolve(options.output)\n const outputDir = dirname(outputPath)\n mkdirSync(outputDir, { recursive: true })\n\n const jsonContent = JSON.stringify(outputData, null, 2)\n writeFileSync(outputPath, jsonContent, 'utf-8')\n\n // 输出结果\n const size = jsonContent.length\n const sizeKB = (size / 1024).toFixed(2)\n console.log(`文件路径: ${outputPath}`)\n console.log(`节点 ID: ${options.nodeId}`)\n console.log(`数据大小: ${size.toLocaleString()} 字符 (约 ${sizeKB} KB)`)\n console.log(`节点深度: ${params.maxDepth}`)\n if (!options.raw) {\n console.log(`数据模式: 精简模式 (使用 --raw 获取完整数据)`)\n }\n } catch (error) {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n process.exit(1)\n } finally {\n client.close()\n }\n}\n","/**\n * get_node_by_link 命令\n * 解析 mgp:// 协议链接并获取节点信息\n * 支持节点链接 (nodeId) 和页面链接 (pageId)\n */\n\nimport { Command } from 'commander'\nimport { writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient, parseMgpLink } from '../client.js'\nimport { ErrorCode, MGError } from '../../shared/errors.js'\nimport { trimNodeDefaults } from '../../shared/utils.js'\nimport type { GetNodeParams, GetPageParams, NodeInfo } from '../../shared/types.js'\n\ninterface GetNodeByLinkOptions {\n link: string\n output: string\n maxDepth?: string\n includeInvisible?: boolean\n raw?: boolean\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/**\n * 创建 get_node_by_link 命令\n */\nexport function createGetNodeByLinkCommand(): Command {\n return new Command('get_node_by_link')\n .description('解析 mgp:// 协议链接并获取节点/页面信息')\n .requiredOption('--link <url>', 'mgp:// 协议链接(支持 nodeId 和 pageId)')\n .requiredOption('--output <path>', '输出 JSON 文件路径')\n .option('--maxDepth <number>', '遍历深度', '1')\n .option('--includeInvisible', '包含不可见节点', false)\n .option('--raw', '保留原始数据,不精简默认值字段', false)\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (options: GetNodeByLinkOptions) => {\n await handleGetNodeByLink(options)\n })\n}\n\n/**\n * 处理 get_node_by_link 命令\n */\nasync function handleGetNodeByLink(options: GetNodeByLinkOptions): Promise<void> {\n // 解析 mgp:// 链接\n const parsed = parseMgpLink(options.link)\n if (!parsed) {\n console.error(`错误 [${ErrorCode.INVALID_LINK}]: 无效的 mgp:// 链接格式`)\n console.error(`提供的链接: ${options.link}`)\n console.error(`期望格式:`)\n console.error(` 节点链接: mgp://[mastergo_page_url]?nodeId=[节点ID]`)\n console.error(` 页面链接: mgp://[mastergo_page_url]?pageId=[页面ID]`)\n process.exit(1)\n }\n\n const { pageUrl, nodeId, pageId } = parsed\n const isPageLink = !!pageId\n\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n let data: NodeInfo\n\n if (isPageLink) {\n // 页面链接:使用 GET_PAGE_BY_ID\n const params: GetPageParams = {\n pageId: pageId!,\n maxDepth: parseInt(options.maxDepth || '1', 10),\n includeInvisible: options.includeInvisible || false,\n }\n\n data = await client.requestWithRetry<NodeInfo>(\n MessageType.GET_PAGE_BY_ID,\n params,\n pageUrl\n )\n } else {\n // 节点链接:使用 GET_NODE_BY_ID\n const params: GetNodeParams = {\n nodeId: nodeId!,\n maxDepth: parseInt(options.maxDepth || '1', 10),\n includeInvisible: options.includeInvisible || false,\n }\n\n data = await client.requestWithRetry<NodeInfo>(\n MessageType.GET_NODE_BY_ID,\n params,\n pageUrl\n )\n }\n\n // 精简数据(除非使用 --raw 选项)\n const outputData = options.raw ? data : trimNodeDefaults(data as Record<string, unknown>)\n\n // 保存到文件\n const outputPath = resolve(options.output)\n const outputDir = dirname(outputPath)\n mkdirSync(outputDir, { recursive: true })\n\n const jsonContent = JSON.stringify(outputData, null, 2)\n writeFileSync(outputPath, jsonContent, 'utf-8')\n\n // 输出结果\n const size = jsonContent.length\n const sizeKB = (size / 1024).toFixed(2)\n console.log(`文件路径: ${outputPath}`)\n console.log(`Link: ${options.link}`)\n console.log(`页面 URL: ${pageUrl}`)\n if (isPageLink) {\n console.log(`页面 ID: ${pageId}`)\n console.log(`类型: 页面`)\n } else {\n console.log(`节点 ID: ${nodeId}`)\n console.log(`类型: 节点`)\n }\n console.log(`数据大小: ${size.toLocaleString()} 字符 (约 ${sizeKB} KB)`)\n console.log(`遍历深度: ${options.maxDepth || '1'}`)\n if (!options.raw) {\n console.log(`数据模式: 精简模式 (使用 --raw 获取完整数据)`)\n }\n } catch (error) {\n if (error instanceof MGError) {\n console.error(`错误 [${error.code}]: ${error.message}`)\n } else {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n }\n process.exit(1)\n } finally {\n client.close()\n }\n}\n","/**\n * get_all_nodes 命令\n * 获取当前页面的所有节点树\n */\n\nimport { Command } from 'commander'\nimport { writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient } from '../client.js'\nimport { trimNodeDefaults } from '../../shared/utils.js'\nimport type { GetAllNodesParams, NodeInfo } from '../../shared/types.js'\n\ninterface GetAllNodesOptions {\n output: string\n maxDepth?: string\n includeInvisible?: boolean\n raw?: boolean\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/**\n * 创建 get_all_nodes 命令\n */\nexport function createGetAllNodesCommand(): Command {\n return new Command('get_all_nodes')\n .description('获取当前页面的所有节点树。警告:深度每增加 1,数据量可能呈指数级增长。建议从 maxDepth=1 开始')\n .requiredOption('--output <path>', '输出 JSON 文件路径。支持绝对路径或相对路径')\n .option('--maxDepth <number>', '最大深度,默认 1。深度 2 可能产生 100KB-500KB,深度 3 可能超过 1MB', '1')\n .option('--includeInvisible', '包含不可见节点(visible: false),默认不包含', false)\n .option('--raw', '保留原始数据,不精简默认值字段', false)\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (options: GetAllNodesOptions) => {\n await handleGetAllNodes(options)\n })\n}\n\n/**\n * 处理 get_all_nodes 命令\n */\nasync function handleGetAllNodes(options: GetAllNodesOptions): Promise<void> {\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n // 发送请求\n const params: GetAllNodesParams = {\n maxDepth: parseInt(options.maxDepth || '1', 10),\n includeInvisible: options.includeInvisible || false,\n }\n\n const data = await client.requestWithRetry<NodeInfo[]>(MessageType.GET_ALL_NODES, params)\n\n // 精简数据(除非使用 --raw 选项)\n const outputData = options.raw\n ? data\n : Array.isArray(data)\n ? data.map((node) => trimNodeDefaults(node as Record<string, unknown>))\n : trimNodeDefaults(data as unknown as Record<string, unknown>)\n\n // 保存到文件\n const outputPath = resolve(options.output)\n const outputDir = dirname(outputPath)\n mkdirSync(outputDir, { recursive: true })\n\n const jsonContent = JSON.stringify(outputData, null, 2)\n writeFileSync(outputPath, jsonContent, 'utf-8')\n\n // 输出结果\n const size = jsonContent.length\n const sizeKB = (size / 1024).toFixed(2)\n const nodeCount = Array.isArray(data) ? data.length : 1\n console.log(`文件路径: ${outputPath}`)\n console.log(`节点数量: ${nodeCount}`)\n console.log(`数据大小: ${size.toLocaleString()} 字符 (约 ${sizeKB} KB)`)\n console.log(`节点深度: ${params.maxDepth}`)\n if (!options.raw) {\n console.log(`数据模式: 精简模式 (使用 --raw 获取完整数据)`)\n }\n } catch (error) {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n process.exit(1)\n } finally {\n client.close()\n }\n}\n","/**\n * export_image 命令\n * 导出 MasterGo 节点为图片文件\n */\n\nimport { Command } from 'commander'\nimport { writeFileSync, unlinkSync } from 'node:fs'\nimport { resolve, dirname, extname, basename } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient, parseMgpLink } from '../client.js'\nimport { MGError, ErrorCode } from '../../shared/errors.js'\nimport type { ExportImageParams } from '../../shared/types.js'\nimport { vdConvert } from 'vd-tool'\n\ntype ImageFormat = 'PNG' | 'JPG' | 'SVG' | 'PDF' | 'WEBP' | 'VECTOR'\n\ninterface ExportImageOptions {\n output?: string\n link?: string\n nodeId?: string\n domain?: string\n fileId?: string\n format?: string\n scale?: string\n width?: string\n height?: string\n useAbsoluteBounds?: boolean\n useRenderBounds?: boolean\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/** 导出响应 */\ninterface ExportResponse {\n /** Base64 编码的图片数据 */\n data: string\n /** MIME 类型 */\n mimeType: string\n /** 文件名建议 */\n filename?: string\n}\n\n/**\n * 创建 export_image 命令\n */\nexport function createExportImageCommand(): Command {\n return new Command('export_image')\n .description('导出 MasterGo 节点为图片文件。强烈建议指定 --output,否则保存到临时目录可能被系统清理')\n .option('--output <path>', '输出文件路径。强烈建议指定,否则保存到系统临时目录可能被清理')\n .option('--link <mgp-link>', 'mgp:// 协议链接。与 --nodeId/--domain/--fileId 二选一')\n .option('--nodeId <id>', '节点 ID,格式如 123:456。与 --domain/--fileId 配合使用')\n .option('--domain <domain>', 'MasterGo 域名,默认 mastergo.netease.com', 'mastergo.netease.com')\n .option('--fileId <id>', '文件 ID(纯数字),与 --domain 配合指定目标页面')\n .option('--format <type>', '导出格式:PNG(无损透明)、JPG(有损)、SVG(矢量)、PDF、WEBP、VECTOR(Android VectorDrawable)', 'PNG')\n .option('--scale <number>', '缩放倍率(如 1、2、3)。与 width/height 互斥')\n .option('--width <number>', '固定宽度(像素)。与 scale/height 互斥')\n .option('--height <number>', '固定高度(像素)。与 scale/width 互斥')\n .option('--useAbsoluteBounds', '使用完整尺寸。true: 包含被裁剪部分,false: 只导出可见区域', false)\n .option('--no-use-render-bounds', '不包含特效和外描边。默认包含阴影、外描边等')\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (options: ExportImageOptions) => {\n await handleExportImage(options)\n })\n}\n\n/**\n * 处理 export_image 命令\n */\nasync function handleExportImage(options: ExportImageOptions): Promise<void> {\n // 验证格式\n const format = (options.format?.toUpperCase() || 'PNG') as ImageFormat\n const validFormats: ImageFormat[] = ['PNG', 'JPG', 'SVG', 'PDF', 'WEBP', 'VECTOR']\n if (!validFormats.includes(format)) {\n console.error(`错误: 不支持的格式 \"${options.format}\"`)\n console.error(`支持的格式: ${validFormats.join(', ')}`)\n process.exit(1)\n }\n\n // 验证参数互斥\n const sizeParams = [options.scale, options.width, options.height].filter(Boolean)\n if (sizeParams.length > 1) {\n console.error('错误: scale、width、height 三者互斥,只能指定其中一个')\n process.exit(1)\n }\n\n // 验证 link 和 nodeId/fileId 互斥\n if (options.link && (options.nodeId || options.fileId)) {\n console.error(`错误 [${ErrorCode.INVALID_PARAMS}]: --link 和 --nodeId/--fileId 不能同时使用`)\n process.exit(1)\n }\n\n // 解析目标页面和节点\n let pageUrl: string | undefined\n let nodeId: string | undefined\n\n if (options.link) {\n // 方式1: 通过 mgp:// 链接\n const linkInfo = parseMgpLink(options.link)\n if (!linkInfo || !linkInfo.nodeId) {\n console.error(`错误 [${ErrorCode.INVALID_LINK}]: 无效的 mgp:// 链接格式`)\n console.error(`提供的链接: ${options.link}`)\n console.error(`期望格式: mgp://[mastergo_page_url]?nodeId=xxx`)\n console.error(`注意: 此命令不支持页面链接 (pageId),请使用节点链接 (nodeId)`)\n process.exit(1)\n }\n pageUrl = linkInfo.pageUrl\n nodeId = linkInfo.nodeId\n } else {\n // 方式2: 通过 nodeId + domain + fileId\n if (options.nodeId) {\n nodeId = options.nodeId\n }\n if (options.fileId) {\n const domain = options.domain || 'mastergo.netease.com'\n pageUrl = `${domain}/file/${options.fileId}`\n }\n }\n\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n // VECTOR 格式需要先导出 SVG,再转换\n const isVectorFormat = format === 'VECTOR'\n const requestFormat = isVectorFormat ? 'SVG' : format\n\n // 构建请求参数\n const params: ExportImageParams = {\n format: requestFormat as 'PNG' | 'JPG' | 'SVG' | 'PDF' | 'WEBP',\n useAbsoluteBounds: options.useAbsoluteBounds || false,\n useRenderBounds: options.useRenderBounds !== false,\n }\n\n if (nodeId) {\n params.nodeId = nodeId\n }\n if (options.scale) {\n params.scale = parseFloat(options.scale)\n }\n if (options.width) {\n params.width = parseInt(options.width, 10)\n }\n if (options.height) {\n params.height = parseInt(options.height, 10)\n }\n\n // 发送请求(传入 pageUrl 以指定目标页面)\n const response = await client.requestWithRetry<ExportResponse>(\n MessageType.EXPORT_IMAGE,\n params as Record<string, unknown>,\n pageUrl\n )\n\n // 确定输出路径\n const ext = getExtension(format)\n let outputPath: string\n if (options.output) {\n outputPath = resolve(options.output)\n // 如果没有扩展名,添加扩展名\n if (!extname(outputPath)) {\n outputPath = `${outputPath}${ext}`\n }\n } else {\n // 使用临时目录\n const filename = response.filename || `export_${Date.now()}${ext}`\n outputPath = resolve(tmpdir(), filename)\n console.log('警告: 未指定 --output,文件将保存到临时目录,可能会被系统清理')\n }\n\n // 确保目录存在\n const outputDir = dirname(outputPath)\n mkdirSync(outputDir, { recursive: true })\n\n // 解码 Base64 并保存\n const buffer = Buffer.from(response.data, 'base64')\n\n let finalPath = outputPath\n let finalSize = buffer.length\n\n if (isVectorFormat) {\n // VECTOR 格式:先保存 SVG 到临时文件,再转换为 VectorDrawable\n const tempSvgPath = resolve(tmpdir(), `temp_${Date.now()}.svg`)\n writeFileSync(tempSvgPath, buffer)\n\n try {\n // 转换为 VectorDrawable\n const vectorOutputDir = dirname(outputPath)\n const convertedPath = await convertSvgToVector(tempSvgPath, vectorOutputDir)\n\n // vd-tool 生成的文件名是基于原 SVG 文件名的,需要重命名\n const expectedOutputName = basename(tempSvgPath, '.svg') + '.xml'\n const expectedOutputPath = resolve(vectorOutputDir, expectedOutputName)\n\n // 如果生成的文件路径不是我们期望的,重命名它\n if (expectedOutputPath !== outputPath) {\n const { renameSync, existsSync } = await import('node:fs')\n if (existsSync(expectedOutputPath)) {\n renameSync(expectedOutputPath, outputPath)\n } else if (existsSync(convertedPath)) {\n renameSync(convertedPath, outputPath)\n }\n }\n\n // 读取最终文件大小\n const { statSync } = await import('node:fs')\n finalSize = statSync(outputPath).size\n finalPath = outputPath\n } finally {\n // 清理临时 SVG 文件\n try {\n unlinkSync(tempSvgPath)\n } catch {\n // 忽略清理失败\n }\n }\n } else {\n // 其他格式直接保存\n writeFileSync(outputPath, buffer)\n }\n\n // 输出结果\n const sizeKB = (finalSize / 1024).toFixed(2)\n console.log(`文件路径: ${finalPath}`)\n if (options.link) {\n console.log(`Link: ${options.link}`)\n }\n if (nodeId) {\n console.log(`节点 ID: ${nodeId}`)\n } else {\n console.log('节点 ID: (选中的节点)')\n }\n console.log(`导出格式: ${format}${isVectorFormat ? ' (Android VectorDrawable)' : ''}`)\n console.log(`文件大小: ${finalSize.toLocaleString()} 字节 (约 ${sizeKB} KB)`)\n } catch (error) {\n if (error instanceof MGError) {\n console.error(`错误 [${error.code}]: ${error.message}`)\n } else {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n }\n process.exit(1)\n } finally {\n client.close()\n }\n}\n\n/**\n * 获取格式对应的文件扩展名\n */\nfunction getExtension(format: ImageFormat): string {\n const extensions: Record<ImageFormat, string> = {\n PNG: '.png',\n JPG: '.jpg',\n SVG: '.svg',\n PDF: '.pdf',\n WEBP: '.webp',\n VECTOR: '.xml',\n }\n return extensions[format]\n}\n\n/**\n * 将 SVG 文件转换为 Android VectorDrawable XML\n * @param svgPath SVG 文件路径\n * @param outputDir 输出目录\n * @returns 转换后的 XML 文件路径\n */\nasync function convertSvgToVector(svgPath: string, outputDir: string): Promise<string> {\n try {\n const result = await vdConvert(svgPath, {\n outDir: outputDir,\n })\n\n if (result.errors && result.errors.length > 0) {\n throw new Error(`VectorDrawable 转换失败: ${result.errors.join(', ')}`)\n }\n\n if (result.warnings && result.warnings.length > 0) {\n console.log(`警告: ${result.warnings.join(', ')}`)\n }\n\n return result.output\n } catch (error) {\n if (error instanceof Error) {\n // 检查是否是 Java 未安装的错误\n if (error.message.includes('ENOENT') || error.message.includes('java')) {\n throw new Error(\n 'VectorDrawable 转换需要 Java 8 或更高版本。\\n' +\n '请安装 Java: https://adoptium.net/ 或 brew install openjdk'\n )\n }\n throw error\n }\n throw new Error(`VectorDrawable 转换失败: ${error}`)\n }\n}\n","/**\n * execute_code 命令\n * 在 MasterGo 页面执行自定义 JavaScript 代码\n */\n\nimport { Command } from 'commander'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient, parseMgpLink } from '../client.js'\nimport { ErrorCode } from '../../shared/errors.js'\n\ninterface ExecuteCodeOptions {\n link?: string\n domain?: string\n fileId?: string\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/**\n * 创建 execute_code 命令\n */\nexport function createExecuteCodeCommand(): Command {\n return new Command('execute_code')\n .description('在 MasterGo 页面执行自定义 JavaScript 代码。通过 mg 变量访问 MasterGo API,结果会被 JSON 序列化返回')\n .argument('<code>', '要执行的代码。可使用 mg 变量,如 mg.currentPage.name、mg.currentPage.selection')\n .option('--link <url>', 'mgp:// 协议链接,用于指定目标页面。与 --domain/--fileId 二选一')\n .option('--domain <domain>', 'MasterGo 域名,默认 mastergo.netease.com', 'mastergo.netease.com')\n .option('--fileId <id>', '文件 ID(纯数字),与 --domain 配合指定目标页面')\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (code: string, options: ExecuteCodeOptions) => {\n await handleExecuteCode(code, options)\n })\n}\n\n/**\n * 处理 execute_code 命令\n */\nasync function handleExecuteCode(code: string, options: ExecuteCodeOptions): Promise<void> {\n // 验证参数:link 和 fileId 不能同时使用\n if (options.link && options.fileId) {\n console.error(`错误 [${ErrorCode.INVALID_PARAMS}]: --link 和 --fileId 不能同时使用`)\n process.exit(1)\n }\n\n // 解析目标页面 URL\n let pageUrl: string | undefined\n if (options.link) {\n const parsed = parseMgpLink(options.link)\n if (!parsed) {\n console.error(`错误 [${ErrorCode.INVALID_LINK}]: 无效的 mgp:// 链接格式`)\n console.error(`提供的链接: ${options.link}`)\n console.error(`期望格式: mgp://[mastergo_page_url]?nodeId=xxx 或 mgp://[mastergo_page_url]?pageId=xxx`)\n process.exit(1)\n }\n pageUrl = parsed.pageUrl\n } else if (options.fileId) {\n const domain = options.domain || 'mastergo.netease.com'\n pageUrl = `${domain}/file/${options.fileId}`\n }\n\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n // 发送请求(传入 pageUrl 以指定目标页面)\n const result = await client.requestWithRetry<unknown>(MessageType.EXECUTE_CODE, { code }, pageUrl)\n\n // 输出结果\n if (result === null || result === undefined) {\n console.log('执行完成(无返回值)')\n } else if (typeof result === 'object') {\n console.log(JSON.stringify(result, null, 2))\n } else {\n console.log(result)\n }\n } catch (error) {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n process.exit(1)\n } finally {\n client.close()\n }\n}\n","/**\n * get_all_pages 命令\n * 获取 MasterGo 文档的所有页面信息\n */\n\nimport { Command } from 'commander'\nimport { writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient } from '../client.js'\nimport { extractFileId } from '../../shared/utils.js'\nimport type { AllPagesInfo } from '../../shared/types.js'\n\ninterface GetAllPagesOptions {\n link?: string\n fileId?: string\n domain?: string\n output?: string\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/**\n * 创建 get_all_pages 命令\n */\nexport function createGetAllPagesCommand(): Command {\n return new Command('get_all_pages')\n .description('获取 MasterGo 文档的所有页面信息。不指定 --output 时保存到系统临时目录')\n .option('--link <url>', '页面链接。支持完整 URL 或 mgp:// 协议')\n .option('--fileId <id>', '文件 ID(纯数字)。从 URL 中 /file/ 后面的数字')\n .option('--domain <domain>', 'MasterGo 域名,默认 mastergo.netease.com。与 --fileId 配合使用', 'mastergo.netease.com')\n .option('--output <path>', '输出 JSON 文件路径。不指定则保存到系统临时目录')\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (options: GetAllPagesOptions) => {\n await handleGetAllPages(options)\n })\n}\n\n/**\n * 处理 get_all_pages 命令\n */\nasync function handleGetAllPages(options: GetAllPagesOptions): Promise<void> {\n // 提取 fileId\n let fileId: string | null = null\n \n if (options.fileId) {\n fileId = options.fileId\n } else if (options.link) {\n fileId = extractFileId(options.link)\n if (!fileId) {\n console.error('错误: 无法从链接中提取 fileId')\n console.error('支持的格式:')\n console.error(' - 完整 URL: https://mastergo.netease.com/file/174875497054651')\n console.error(' - mgp 协议: mgp://mastergo.netease.com/file/174875497054651')\n console.error(' - 纯 fileId: 174875497054651')\n process.exit(1)\n }\n } else {\n // 没有提供参数,获取当前连接页面的所有页面\n console.log('未提供 --link 或 --fileId,将获取当前连接页面的所有页面信息')\n }\n\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n // 构建 pageUrl(如果有 fileId)\n let pageUrl: string | undefined\n if (fileId) {\n // 使用指定的域名或默认域名构建标准化的 pageUrl\n const domain = options.domain || 'mastergo.netease.com'\n pageUrl = `${domain}/file/${fileId}`\n }\n\n // 发送请求\n const data = await client.requestWithRetry<AllPagesInfo>(\n MessageType.GET_ALL_PAGES,\n {},\n pageUrl\n )\n\n // 确定输出路径\n let outputPath: string\n if (options.output) {\n outputPath = resolve(options.output)\n } else {\n // 使用系统临时目录\n const filename = `pages_${fileId || 'current'}_${Date.now()}.json`\n outputPath = resolve(tmpdir(), filename)\n }\n\n // 确保目录存在\n const outputDir = dirname(outputPath)\n mkdirSync(outputDir, { recursive: true })\n\n // 保存到文件\n const jsonContent = JSON.stringify(data, null, 2)\n writeFileSync(outputPath, jsonContent, 'utf-8')\n\n // 输出结果\n const size = jsonContent.length\n const sizeKB = (size / 1024).toFixed(2)\n console.log(`文件路径: ${outputPath}`)\n console.log(`文档名称: ${data.documentName}`)\n console.log(`页面数量: ${data.totalCount}`)\n console.log(`数据大小: ${size.toLocaleString()} 字符 (约 ${sizeKB} KB)`)\n } catch (error) {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n process.exit(1)\n } finally {\n client.close()\n }\n}\n","/**\n * get_node_for_space 命令\n * 获取节点的空间位置信息,用于 AI 理解元素布局\n * 只返回 id、name、x、y、width、height 和 children 字段\n */\n\nimport { Command } from 'commander'\nimport { writeFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { mkdirSync } from 'node:fs'\nimport { MessageType } from '../../shared/constants.js'\nimport { MGClient, parseMgpLink } from '../client.js'\nimport { extractSpaceInfo } from '../../shared/utils.js'\nimport { ErrorCode, MGError } from '../../shared/errors.js'\nimport type { GetNodeParams, GetPageParams, NodeInfo } from '../../shared/types.js'\n\ninterface GetNodeForSpaceOptions {\n nodeId?: string\n link?: string\n output: string\n domain?: string\n fileId?: string\n maxDepth?: string\n includeInvisible?: boolean\n noAutoStart?: boolean\n noRetry?: boolean\n}\n\n/**\n * 创建 get_node_for_space 命令\n */\nexport function createGetNodeForSpaceCommand(): Command {\n return new Command('get_node_for_space')\n .description('获取节点或页面的空间位置信息(id、name、x、y、width、height),用于 AI 理解元素布局。默认获取最深层级。支持 nodeId 和 pageId 链接')\n .option('--nodeId <id>', '节点 ID,格式如 123:456。与 --link 二选一')\n .option('--link <url>', 'mgp:// 协议链接(支持 nodeId 和 pageId)。与 --nodeId 二选一')\n .requiredOption('--output <path>', '输出 JSON 文件路径')\n .option('--domain <domain>', 'MasterGo 域名,默认 mastergo.netease.com。与 --nodeId 配合使用', 'mastergo.netease.com')\n .option('--fileId <id>', '文件 ID(纯数字),与 --domain 和 --nodeId 配合使用')\n .option('--maxDepth <number>', '遍历深度,默认 99(获取最深层级)', '99')\n .option('--includeInvisible', '包含不可见节点', false)\n .option('--no-auto-start', '禁用自动启动 Server')\n .option('--no-retry', '禁用自动重试')\n .action(async (options: GetNodeForSpaceOptions) => {\n await handleGetNodeForSpace(options)\n })\n}\n\n/**\n * 处理 get_node_for_space 命令\n */\nasync function handleGetNodeForSpace(options: GetNodeForSpaceOptions): Promise<void> {\n // 验证参数:必须提供 nodeId 或 link\n if (!options.nodeId && !options.link) {\n console.error(`错误 [${ErrorCode.INVALID_PARAMS}]: 必须提供 --nodeId 或 --link 参数`)\n process.exit(1)\n }\n\n if (options.nodeId && options.link) {\n console.error(`错误 [${ErrorCode.INVALID_PARAMS}]: --nodeId 和 --link 不能同时使用`)\n process.exit(1)\n }\n\n let pageUrl: string | undefined\n let nodeId: string | undefined\n let pageId: string | undefined\n let isPageMode = false\n\n // 解析参数\n if (options.link) {\n // 通过 mgp:// 链接获取\n const parsed = parseMgpLink(options.link)\n if (!parsed) {\n console.error(`错误 [${ErrorCode.INVALID_LINK}]: 无效的 mgp:// 链接格式`)\n console.error(`提供的链接: ${options.link}`)\n console.error(`期望格式: mgp://[mastergo_page_url]?nodeId=xxx 或 mgp://[mastergo_page_url]?pageId=xxx`)\n process.exit(1)\n }\n\n pageUrl = parsed.pageUrl\n\n if (parsed.pageId) {\n // 页面模式:获取整个页面的空间信息\n isPageMode = true\n pageId = parsed.pageId\n } else if (parsed.nodeId) {\n // 节点模式:获取指定节点的空间信息\n nodeId = parsed.nodeId\n } else {\n console.error(`错误 [${ErrorCode.INVALID_LINK}]: 链接中必须包含 nodeId 或 pageId 参数`)\n console.error(`提供的链接: ${options.link}`)\n process.exit(1)\n }\n } else {\n // 通过 nodeId 获取\n nodeId = options.nodeId!\n if (options.fileId) {\n const domain = options.domain || 'mastergo.netease.com'\n pageUrl = `${domain}/file/${options.fileId}`\n }\n }\n\n const client = new MGClient({\n noAutoStart: options.noAutoStart,\n noRetry: options.noRetry,\n })\n\n try {\n // 连接 Server\n await client.connect()\n\n const maxDepth = parseInt(options.maxDepth || '99', 10)\n const includeInvisible = options.includeInvisible || false\n let data: NodeInfo\n\n if (isPageMode && pageId) {\n // 页面模式:使用 GET_PAGE_BY_ID\n const params: GetPageParams = {\n pageId,\n maxDepth,\n includeInvisible,\n }\n\n data = await client.requestWithRetry<NodeInfo>(\n MessageType.GET_PAGE_BY_ID,\n params,\n pageUrl\n )\n } else {\n // 节点模式:使用 GET_NODE_BY_ID\n const params: GetNodeParams = {\n nodeId: nodeId!,\n maxDepth,\n includeInvisible,\n }\n\n data = await client.requestWithRetry<NodeInfo>(\n MessageType.GET_NODE_BY_ID,\n params,\n pageUrl\n )\n }\n\n // 提取空间信息\n const spaceData = extractSpaceInfo(data)\n\n // 保存到文件\n const outputPath = resolve(options.output)\n const outputDir = dirname(outputPath)\n mkdirSync(outputDir, { recursive: true })\n\n const jsonContent = JSON.stringify(spaceData, null, 2)\n writeFileSync(outputPath, jsonContent, 'utf-8')\n\n // 输出结果\n const size = jsonContent.length\n const sizeKB = (size / 1024).toFixed(2)\n console.log(`文件路径: ${outputPath}`)\n if (options.link) {\n console.log(`Link: ${options.link}`)\n console.log(`页面 URL: ${pageUrl}`)\n }\n if (isPageMode) {\n console.log(`页面 ID: ${pageId}`)\n console.log(`模式: 页面空间信息`)\n } else {\n console.log(`节点 ID: ${nodeId}`)\n console.log(`模式: 节点空间信息`)\n }\n console.log(`数据大小: ${size.toLocaleString()} 字符 (约 ${sizeKB} KB)`)\n console.log(`节点深度: ${maxDepth}`)\n console.log(`数据模式: 空间信息 (仅 id, name, x, y, width, height)`)\n } catch (error) {\n if (error instanceof MGError) {\n console.error(`错误 [${error.code}]: ${error.message}`)\n } else {\n console.error(`错误: ${error instanceof Error ? error.message : error}`)\n }\n process.exit(1)\n } finally {\n client.close()\n }\n}\n"],"mappings":"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","names":["Command","fileURLToPath","dirname","join","existsSync","mkdirSync","dirname","dirname","existsSync","mkdirSync","readFileSync","existsSync","dirname","join","resolve","fileURLToPath","dirname","join","resolve","running","info","WebSocket","resolve","WebSocket","Command","writeFileSync","resolve","dirname","mkdirSync","Command","resolve","dirname","mkdirSync","writeFileSync","Command","writeFileSync","resolve","dirname","mkdirSync","Command","resolve","dirname","mkdirSync","writeFileSync","Command","writeFileSync","resolve","dirname","mkdirSync","Command","resolve","dirname","mkdirSync","writeFileSync","Command","writeFileSync","unlinkSync","resolve","dirname","mkdirSync","Command","resolve","dirname","mkdirSync","writeFileSync","existsSync","unlinkSync","Command","Command","Command","writeFileSync","resolve","dirname","mkdirSync","tmpdir","Command","resolve","tmpdir","dirname","mkdirSync","writeFileSync","Command","writeFileSync","resolve","dirname","mkdirSync","Command","resolve","dirname","mkdirSync","writeFileSync","Command"]}