@hangox/mg-cli 1.0.7 → 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.
@@ -783,4 +783,4 @@ declare class RequestHandler {
783
783
  cleanupAll(): void;
784
784
  }
785
785
 
786
- export { type ServerOptions as $, type PongMessage as A, type BaseMessage as B, CONFIG_DIR as C, DEV_DEFAULT_PORT as D, ErrorCode as E, type ConnectionInfo as F, type GetNodeParams as G, HEARTBEAT_INTERVAL as H, IS_DEV_MODE as I, type GetPageParams as J, type GetAllNodesParams as K, LOG_DIR as L, MAX_PORT_ATTEMPTS as M, type NodeInfo as N, type ExportImageParams as O, PROD_DEFAULT_PORT as P, type OutputFormatter as Q, REQUEST_TIMEOUT as R, type ServerInfo as S, type CliOptions as T, type RGBA as U, type MgPageInfo as V, type AllPagesInfo as W, type ConnectedPageInfo as X, type ServerStatusResponse as Y, MGServer as Z, createServer as _, type SpaceNodeInfo as a, ConnectionManager as a0, type ManagedWebSocket as a1, RequestHandler as a2, Logger as a3, createLogger as a4, LogLevel as a5, type LoggerOptions as a6, PROD_PORT_RANGE_START as b, PROD_PORT_RANGE_END as c, DEV_PORT_RANGE_START as d, DEV_PORT_RANGE_END as e, DEFAULT_PORT as f, PORT_RANGE_START as g, PORT_RANGE_END as h, PORT_SCAN_TIMEOUT as i, SERVER_INFO_FILE as j, SERVER_LOG_FILE as k, HEARTBEAT_TIMEOUT as l, SERVER_START_TIMEOUT as m, RETRY_INTERVALS as n, MAX_RETRY_COUNT as o, ConnectionType as p, MessageType as q, ErrorNames as r, ErrorMessages as s, MGError as t, createError as u, type RequestMessage as v, type ResponseMessage as w, type ErrorInfo as x, type RegisterMessage as y, type PingMessage as z };
786
+ export { type ServerOptions as $, type PongMessage as A, type BaseMessage as B, CONFIG_DIR as C, DEV_DEFAULT_PORT as D, ErrorCode as E, type ConnectionInfo as F, type GetNodeParams as G, HEARTBEAT_INTERVAL as H, IS_DEV_MODE as I, type GetPageParams as J, type GetAllNodesParams as K, LOG_DIR as L, MessageType as M, type NodeInfo as N, type ExportImageParams as O, PROD_DEFAULT_PORT as P, type OutputFormatter as Q, REQUEST_TIMEOUT as R, type ServerInfo as S, type CliOptions as T, type RGBA as U, type MgPageInfo as V, type AllPagesInfo as W, type ConnectedPageInfo as X, type ServerStatusResponse as Y, MGServer as Z, createServer as _, type SpaceNodeInfo as a, ConnectionManager as a0, type ManagedWebSocket as a1, RequestHandler as a2, Logger as a3, createLogger as a4, LogLevel as a5, type LoggerOptions as a6, PROD_PORT_RANGE_START as b, PROD_PORT_RANGE_END as c, DEV_PORT_RANGE_START as d, DEV_PORT_RANGE_END as e, DEFAULT_PORT as f, PORT_RANGE_START as g, PORT_RANGE_END as h, MAX_PORT_ATTEMPTS as i, PORT_SCAN_TIMEOUT as j, SERVER_INFO_FILE as k, SERVER_LOG_FILE as l, HEARTBEAT_TIMEOUT as m, SERVER_START_TIMEOUT as n, RETRY_INTERVALS as o, MAX_RETRY_COUNT as p, ConnectionType as q, ErrorNames as r, ErrorMessages as s, MGError as t, createError as u, type RequestMessage as v, type ResponseMessage as w, type ErrorInfo as x, type RegisterMessage as y, type PingMessage as z };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as ServerInfo, N as NodeInfo, a as SpaceNodeInfo } from './index-DUQN5gSR.js';
2
- export { W as AllPagesInfo, B as BaseMessage, C as CONFIG_DIR, T as CliOptions, X as ConnectedPageInfo, F as ConnectionInfo, a0 as ConnectionManager, p as ConnectionType, f as DEFAULT_PORT, D as DEV_DEFAULT_PORT, e as DEV_PORT_RANGE_END, d as DEV_PORT_RANGE_START, E as ErrorCode, x as ErrorInfo, s as ErrorMessages, r as ErrorNames, O as ExportImageParams, K as GetAllNodesParams, G as GetNodeParams, J as GetPageParams, H as HEARTBEAT_INTERVAL, l as HEARTBEAT_TIMEOUT, I as IS_DEV_MODE, L as LOG_DIR, a5 as LogLevel, a3 as Logger, a6 as LoggerOptions, M as MAX_PORT_ATTEMPTS, o as MAX_RETRY_COUNT, t as MGError, Z as MGServer, a1 as ManagedWebSocket, q as MessageType, V as MgPageInfo, Q as OutputFormatter, h as PORT_RANGE_END, g as PORT_RANGE_START, i as PORT_SCAN_TIMEOUT, P as PROD_DEFAULT_PORT, c as PROD_PORT_RANGE_END, b as PROD_PORT_RANGE_START, z as PingMessage, A as PongMessage, R as REQUEST_TIMEOUT, n as RETRY_INTERVALS, U as RGBA, y as RegisterMessage, a2 as RequestHandler, v as RequestMessage, w as ResponseMessage, j as SERVER_INFO_FILE, k as SERVER_LOG_FILE, m as SERVER_START_TIMEOUT, $ as ServerOptions, Y as ServerStatusResponse, u as createError, a4 as createLogger, _ as createServer } from './index-DUQN5gSR.js';
1
+ import { S as ServerInfo, N as NodeInfo, a as SpaceNodeInfo, M as MessageType } from './index-DmySkKst.js';
2
+ export { W as AllPagesInfo, B as BaseMessage, C as CONFIG_DIR, T as CliOptions, X as ConnectedPageInfo, F as ConnectionInfo, a0 as ConnectionManager, q as ConnectionType, f as DEFAULT_PORT, D as DEV_DEFAULT_PORT, e as DEV_PORT_RANGE_END, d as DEV_PORT_RANGE_START, E as ErrorCode, x as ErrorInfo, s as ErrorMessages, r as ErrorNames, O as ExportImageParams, K as GetAllNodesParams, G as GetNodeParams, J as GetPageParams, H as HEARTBEAT_INTERVAL, m as HEARTBEAT_TIMEOUT, I as IS_DEV_MODE, L as LOG_DIR, a5 as LogLevel, a3 as Logger, a6 as LoggerOptions, i as MAX_PORT_ATTEMPTS, p as MAX_RETRY_COUNT, t as MGError, Z as MGServer, a1 as ManagedWebSocket, V as MgPageInfo, Q as OutputFormatter, h as PORT_RANGE_END, g as PORT_RANGE_START, j as PORT_SCAN_TIMEOUT, P as PROD_DEFAULT_PORT, c as PROD_PORT_RANGE_END, b as PROD_PORT_RANGE_START, z as PingMessage, A as PongMessage, R as REQUEST_TIMEOUT, o as RETRY_INTERVALS, U as RGBA, y as RegisterMessage, a2 as RequestHandler, v as RequestMessage, w as ResponseMessage, k as SERVER_INFO_FILE, l as SERVER_LOG_FILE, n as SERVER_START_TIMEOUT, $ as ServerOptions, Y as ServerStatusResponse, u as createError, a4 as createLogger, _ as createServer } from './index-DmySkKst.js';
3
3
  import 'ws';
4
4
 
5
5
  /**
@@ -163,4 +163,53 @@ declare function trimNodeDefaults<T extends Record<string, unknown>>(node: T): T
163
163
  */
164
164
  declare function extractSpaceInfo(node: NodeInfo): SpaceNodeInfo;
165
165
 
166
- export { NODE_DEFAULTS, NodeInfo, ServerInfo, SpaceNodeInfo, deleteServerInfo, ensureConfigDir, ensureDir, ensureOutputDir, extractFileId, extractFileIdFromMgpLink, extractFileIdFromUrl, extractSpaceInfo, formatDuration, formatFileSize, formatLogTime, generateId, generateMgpLink, generatePageLink, getCurrentISOTime, isDesignPageUrl, isProcessRunning, killProcess, normalizePageUrl, parseMgpLink, readServerInfo, resolveOutputPath, roundToOneDecimal, trimNodeDefaults, writeServerInfo };
166
+ /**
167
+ * CLI 客户端
168
+ * 负责连接 Server 发送请求
169
+ */
170
+
171
+ /** 客户端选项 */
172
+ interface ClientOptions {
173
+ /** 禁用自动启动 Server */
174
+ noAutoStart?: boolean;
175
+ /** 禁用重试 */
176
+ noRetry?: boolean;
177
+ }
178
+ /**
179
+ * MG CLI 客户端
180
+ */
181
+ declare class MGClient {
182
+ private ws;
183
+ private options;
184
+ constructor(options?: ClientOptions);
185
+ /**
186
+ * 连接到 Server
187
+ */
188
+ connect(): Promise<void>;
189
+ /**
190
+ * 尝试连接指定端口
191
+ */
192
+ private tryConnect;
193
+ /**
194
+ * 等待 Server 就绪
195
+ */
196
+ private waitForServer;
197
+ /**
198
+ * 注册为 Consumer
199
+ */
200
+ private register;
201
+ /**
202
+ * 发送请求并等待响应
203
+ */
204
+ request<T = unknown>(type: MessageType, params?: Record<string, unknown>, pageUrl?: string): Promise<T>;
205
+ /**
206
+ * 带重试的请求
207
+ */
208
+ requestWithRetry<T = unknown>(type: MessageType, params?: Record<string, unknown>, pageUrl?: string): Promise<T>;
209
+ /**
210
+ * 关闭连接
211
+ */
212
+ close(): void;
213
+ }
214
+
215
+ export { type ClientOptions, MGClient, MessageType, NODE_DEFAULTS, NodeInfo, ServerInfo, SpaceNodeInfo, deleteServerInfo, ensureConfigDir, ensureDir, ensureOutputDir, extractFileId, extractFileIdFromMgpLink, extractFileIdFromUrl, extractSpaceInfo, formatDuration, formatFileSize, formatLogTime, generateId, generateMgpLink, generatePageLink, getCurrentISOTime, isDesignPageUrl, isProcessRunning, killProcess, normalizePageUrl, parseMgpLink, readServerInfo, resolveOutputPath, roundToOneDecimal, trimNodeDefaults, writeServerInfo };
package/dist/index.js CHANGED
@@ -922,6 +922,13 @@ function getVersion() {
922
922
  return cachedVersion;
923
923
  }
924
924
  }
925
+ var DEV_VERSION = "9.9.9";
926
+ function isVersionMatch(version1, version2) {
927
+ if (version1 === DEV_VERSION || version2 === DEV_VERSION) {
928
+ return true;
929
+ }
930
+ return version1 === version2;
931
+ }
925
932
 
926
933
  // src/server/websocket-server.ts
927
934
  var MGServer = class {
@@ -1168,6 +1175,247 @@ var MGServer = class {
1168
1175
  function createServer(options) {
1169
1176
  return new MGServer(options);
1170
1177
  }
1178
+
1179
+ // src/cli/client.ts
1180
+ import WebSocket2 from "ws";
1181
+
1182
+ // src/server/daemon.ts
1183
+ import { spawn } from "child_process";
1184
+ import { fileURLToPath as fileURLToPath2 } from "url";
1185
+ import { dirname as dirname4, join as join3 } from "path";
1186
+ function isServerRunning() {
1187
+ const info = readServerInfo();
1188
+ if (!info) {
1189
+ return { running: false, info: null };
1190
+ }
1191
+ if (!isProcessRunning(info.pid)) {
1192
+ deleteServerInfo();
1193
+ return { running: false, info: null };
1194
+ }
1195
+ return { running: true, info };
1196
+ }
1197
+ async function startServerDaemon(port) {
1198
+ const { running, info } = isServerRunning();
1199
+ if (running && info) {
1200
+ throw new MGError(
1201
+ "E016" /* SERVER_ALREADY_RUNNING */,
1202
+ `Server \u5DF2\u5728\u8FD0\u884C\u4E2D (PID: ${info.pid}, \u7AEF\u53E3: ${info.port})`
1203
+ );
1204
+ }
1205
+ ensureConfigDir();
1206
+ const currentFile = fileURLToPath2(import.meta.url);
1207
+ const currentDir = dirname4(currentFile);
1208
+ const serverScript = join3(currentDir, "daemon-runner.js");
1209
+ const args = ["--foreground"];
1210
+ if (port) {
1211
+ args.push("--port", String(port));
1212
+ }
1213
+ const child = spawn(process.execPath, [serverScript, ...args], {
1214
+ detached: true,
1215
+ stdio: "ignore",
1216
+ env: {
1217
+ ...process.env,
1218
+ MG_DAEMON: "1"
1219
+ }
1220
+ });
1221
+ child.unref();
1222
+ const startTime = Date.now();
1223
+ while (Date.now() - startTime < SERVER_START_TIMEOUT) {
1224
+ await new Promise((resolve2) => setTimeout(resolve2, 200));
1225
+ const { running: running2, info: info2 } = isServerRunning();
1226
+ if (running2 && info2) {
1227
+ return info2;
1228
+ }
1229
+ }
1230
+ throw new MGError("E015" /* SERVER_START_FAILED */, "Server \u542F\u52A8\u8D85\u65F6");
1231
+ }
1232
+
1233
+ // src/cli/client.ts
1234
+ var MGClient = class {
1235
+ ws = null;
1236
+ options;
1237
+ constructor(options = {}) {
1238
+ this.options = options;
1239
+ }
1240
+ /**
1241
+ * 连接到 Server
1242
+ */
1243
+ async connect() {
1244
+ const serverInfo = readServerInfo();
1245
+ if (serverInfo) {
1246
+ if (isProcessRunning(serverInfo.pid)) {
1247
+ const currentVersion = getVersion();
1248
+ if (!isVersionMatch(currentVersion, serverInfo.version)) {
1249
+ console.warn(`\u26A0\uFE0F \u7248\u672C\u4E0D\u5339\u914D: CLI ${currentVersion} vs Server ${serverInfo.version}`);
1250
+ console.warn("\u63D0\u793A: \u5982\u9700\u5BF9\u9F50\u7248\u672C\uFF0C\u8BF7\u624B\u52A8\u8FD0\u884C `npx -y @hangox/mg-cli@latest server restart`");
1251
+ }
1252
+ try {
1253
+ await this.tryConnect(serverInfo.port);
1254
+ return;
1255
+ } catch {
1256
+ }
1257
+ }
1258
+ }
1259
+ for (let port = PORT_RANGE_START; port <= PORT_RANGE_END; port++) {
1260
+ try {
1261
+ await this.tryConnect(port);
1262
+ return;
1263
+ } catch {
1264
+ }
1265
+ }
1266
+ if (!this.options.noAutoStart) {
1267
+ console.log("Server \u672A\u8FD0\u884C\uFF0C\u6B63\u5728\u81EA\u52A8\u542F\u52A8...");
1268
+ try {
1269
+ const info = await startServerDaemon();
1270
+ console.log(`Server \u5DF2\u542F\u52A8\uFF0C\u7AEF\u53E3: ${info.port}`);
1271
+ await this.waitForServer(info.port);
1272
+ return;
1273
+ } catch (error) {
1274
+ throw new MGError(
1275
+ "E015" /* SERVER_START_FAILED */,
1276
+ `\u81EA\u52A8\u542F\u52A8 Server \u5931\u8D25: ${error instanceof Error ? error.message : error}`
1277
+ );
1278
+ }
1279
+ }
1280
+ throw new MGError("E001" /* CONNECTION_FAILED */, ErrorMessages["E001" /* CONNECTION_FAILED */]);
1281
+ }
1282
+ /**
1283
+ * 尝试连接指定端口
1284
+ */
1285
+ tryConnect(port) {
1286
+ return new Promise((resolve2, reject) => {
1287
+ const ws = new WebSocket2(`ws://localhost:${port}`);
1288
+ const timer = setTimeout(() => {
1289
+ ws.close();
1290
+ reject(new Error("\u8FDE\u63A5\u8D85\u65F6"));
1291
+ }, PORT_SCAN_TIMEOUT);
1292
+ ws.on("open", () => {
1293
+ clearTimeout(timer);
1294
+ this.ws = ws;
1295
+ this.register();
1296
+ resolve2();
1297
+ });
1298
+ ws.on("error", (error) => {
1299
+ clearTimeout(timer);
1300
+ reject(error);
1301
+ });
1302
+ });
1303
+ }
1304
+ /**
1305
+ * 等待 Server 就绪
1306
+ */
1307
+ async waitForServer(port) {
1308
+ const startTime = Date.now();
1309
+ const interval = 500;
1310
+ while (Date.now() - startTime < SERVER_START_TIMEOUT) {
1311
+ try {
1312
+ await this.tryConnect(port);
1313
+ return;
1314
+ } catch {
1315
+ await new Promise((r) => setTimeout(r, interval));
1316
+ }
1317
+ }
1318
+ throw new Error("\u7B49\u5F85 Server \u542F\u52A8\u8D85\u65F6");
1319
+ }
1320
+ /**
1321
+ * 注册为 Consumer
1322
+ */
1323
+ register() {
1324
+ if (!this.ws) return;
1325
+ const message = {
1326
+ type: "register" /* REGISTER */,
1327
+ data: {
1328
+ connectionType: "consumer" /* CONSUMER */
1329
+ },
1330
+ timestamp: Date.now()
1331
+ };
1332
+ this.ws.send(JSON.stringify(message));
1333
+ }
1334
+ /**
1335
+ * 发送请求并等待响应
1336
+ */
1337
+ async request(type, params, pageUrl) {
1338
+ if (!this.ws) {
1339
+ throw new MGError("E001" /* CONNECTION_FAILED */, "\u672A\u8FDE\u63A5\u5230 Server");
1340
+ }
1341
+ const requestId = generateId();
1342
+ const message = {
1343
+ id: requestId,
1344
+ type,
1345
+ params,
1346
+ pageUrl,
1347
+ timestamp: Date.now()
1348
+ };
1349
+ return new Promise((resolve2, reject) => {
1350
+ const timer = setTimeout(() => {
1351
+ reject(new MGError("E012" /* REQUEST_TIMEOUT */, ErrorMessages["E012" /* REQUEST_TIMEOUT */]));
1352
+ }, REQUEST_TIMEOUT);
1353
+ const messageHandler = (data) => {
1354
+ try {
1355
+ const response = JSON.parse(data.toString());
1356
+ if (response.id === requestId) {
1357
+ clearTimeout(timer);
1358
+ this.ws?.off("message", messageHandler);
1359
+ if (response.success) {
1360
+ resolve2(response.data);
1361
+ } else {
1362
+ const error = response.error;
1363
+ reject(
1364
+ new MGError(
1365
+ error?.code || "E099" /* UNKNOWN_ERROR */,
1366
+ error?.message || "\u672A\u77E5\u9519\u8BEF"
1367
+ )
1368
+ );
1369
+ }
1370
+ }
1371
+ } catch {
1372
+ }
1373
+ };
1374
+ this.ws.on("message", messageHandler);
1375
+ this.ws.send(JSON.stringify(message));
1376
+ });
1377
+ }
1378
+ /**
1379
+ * 带重试的请求
1380
+ */
1381
+ async requestWithRetry(type, params, pageUrl) {
1382
+ if (this.options.noRetry) {
1383
+ return this.request(type, params, pageUrl);
1384
+ }
1385
+ let lastError = null;
1386
+ for (let attempt = 0; attempt <= MAX_RETRY_COUNT; attempt++) {
1387
+ try {
1388
+ return await this.request(type, params, pageUrl);
1389
+ } catch (error) {
1390
+ lastError = error instanceof Error ? error : new Error(String(error));
1391
+ if (error instanceof MGError) {
1392
+ const retryable = ["E017" /* CONNECTION_LOST */, "E012" /* REQUEST_TIMEOUT */];
1393
+ if (!retryable.includes(error.code)) {
1394
+ throw error;
1395
+ }
1396
+ }
1397
+ if (attempt < MAX_RETRY_COUNT) {
1398
+ const delay = RETRY_INTERVALS[attempt] || RETRY_INTERVALS[RETRY_INTERVALS.length - 1];
1399
+ await new Promise((r) => setTimeout(r, delay));
1400
+ try {
1401
+ await this.connect();
1402
+ } catch {
1403
+ }
1404
+ }
1405
+ }
1406
+ }
1407
+ throw lastError || new MGError("E099" /* UNKNOWN_ERROR */, "\u8BF7\u6C42\u5931\u8D25");
1408
+ }
1409
+ /**
1410
+ * 关闭连接
1411
+ */
1412
+ close() {
1413
+ if (this.ws) {
1414
+ this.ws.close();
1415
+ this.ws = null;
1416
+ }
1417
+ }
1418
+ };
1171
1419
  export {
1172
1420
  CONFIG_DIR,
1173
1421
  ConnectionManager,
@@ -1187,6 +1435,7 @@ export {
1187
1435
  Logger,
1188
1436
  MAX_PORT_ATTEMPTS,
1189
1437
  MAX_RETRY_COUNT,
1438
+ MGClient,
1190
1439
  MGError,
1191
1440
  MGServer,
1192
1441
  MessageType,