@hangox/mg-cli 1.0.0 → 1.0.2

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.
Files changed (42) hide show
  1. package/dist/cli.js +1580 -0
  2. package/dist/cli.js.map +1 -0
  3. package/dist/daemon-runner.js +794 -0
  4. package/dist/daemon-runner.js.map +1 -0
  5. package/dist/index-DNrszrq9.d.ts +568 -0
  6. package/dist/index.d.ts +129 -0
  7. package/dist/index.js +950 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/server.d.ts +2 -0
  10. package/dist/server.js +689 -0
  11. package/dist/server.js.map +1 -0
  12. package/package.json +5 -1
  13. package/.eslintrc.cjs +0 -26
  14. package/CLAUDE.md +0 -43
  15. package/src/cli/client.ts +0 -266
  16. package/src/cli/commands/execute-code.ts +0 -59
  17. package/src/cli/commands/export-image.ts +0 -193
  18. package/src/cli/commands/get-all-nodes.ts +0 -81
  19. package/src/cli/commands/get-all-pages.ts +0 -118
  20. package/src/cli/commands/get-node-by-id.ts +0 -83
  21. package/src/cli/commands/get-node-by-link.ts +0 -105
  22. package/src/cli/commands/server.ts +0 -130
  23. package/src/cli/index.ts +0 -33
  24. package/src/index.ts +0 -9
  25. package/src/server/connection-manager.ts +0 -211
  26. package/src/server/daemon-runner.ts +0 -22
  27. package/src/server/daemon.ts +0 -211
  28. package/src/server/index.ts +0 -8
  29. package/src/server/logger.ts +0 -117
  30. package/src/server/request-handler.ts +0 -192
  31. package/src/server/websocket-server.ts +0 -297
  32. package/src/shared/constants.ts +0 -90
  33. package/src/shared/errors.ts +0 -131
  34. package/src/shared/index.ts +0 -8
  35. package/src/shared/types.ts +0 -227
  36. package/src/shared/utils.ts +0 -352
  37. package/tests/unit/shared/constants.test.ts +0 -66
  38. package/tests/unit/shared/errors.test.ts +0 -82
  39. package/tests/unit/shared/utils.test.ts +0 -208
  40. package/tsconfig.json +0 -22
  41. package/tsup.config.ts +0 -33
  42. package/vitest.config.ts +0 -22
@@ -1,66 +0,0 @@
1
- /**
2
- * 常量测试
3
- */
4
-
5
- import { describe, it, expect } from 'vitest'
6
- import {
7
- DEFAULT_PORT,
8
- PORT_RANGE_START,
9
- PORT_RANGE_END,
10
- MAX_PORT_ATTEMPTS,
11
- HEARTBEAT_INTERVAL,
12
- HEARTBEAT_TIMEOUT,
13
- ConnectionType,
14
- MessageType,
15
- } from '../../../src/shared/constants.js'
16
-
17
- describe('端口配置', () => {
18
- it('应该有正确的默认端口', () => {
19
- expect(DEFAULT_PORT).toBe(9527)
20
- })
21
-
22
- it('应该有正确的端口范围', () => {
23
- expect(PORT_RANGE_START).toBe(9527)
24
- expect(PORT_RANGE_END).toBe(9536)
25
- })
26
-
27
- it('端口范围应该正好是 10 个', () => {
28
- expect(PORT_RANGE_END - PORT_RANGE_START + 1).toBe(MAX_PORT_ATTEMPTS)
29
- })
30
- })
31
-
32
- describe('心跳配置', () => {
33
- it('应该有正确的心跳间隔', () => {
34
- expect(HEARTBEAT_INTERVAL).toBe(30000) // 30 秒
35
- })
36
-
37
- it('应该有正确的心跳超时', () => {
38
- expect(HEARTBEAT_TIMEOUT).toBe(90000) // 90 秒 = 3 次心跳
39
- })
40
-
41
- it('心跳超时应该是间隔的 3 倍', () => {
42
- expect(HEARTBEAT_TIMEOUT).toBe(HEARTBEAT_INTERVAL * 3)
43
- })
44
- })
45
-
46
- describe('ConnectionType', () => {
47
- it('应该有 CONSUMER 和 PROVIDER 两种类型', () => {
48
- expect(ConnectionType.CONSUMER).toBe('consumer')
49
- expect(ConnectionType.PROVIDER).toBe('provider')
50
- })
51
- })
52
-
53
- describe('MessageType', () => {
54
- it('应该包含系统消息类型', () => {
55
- expect(MessageType.PING).toBe('ping')
56
- expect(MessageType.PONG).toBe('pong')
57
- expect(MessageType.REGISTER).toBe('register')
58
- })
59
-
60
- it('应该包含业务消息类型', () => {
61
- expect(MessageType.GET_NODE_BY_ID).toBe('get_node_by_id')
62
- expect(MessageType.GET_ALL_NODES).toBe('get_all_nodes')
63
- expect(MessageType.EXPORT_IMAGE).toBe('export_image')
64
- expect(MessageType.EXECUTE_CODE).toBe('execute_code')
65
- })
66
- })
@@ -1,82 +0,0 @@
1
- /**
2
- * 错误处理测试
3
- */
4
-
5
- import { describe, it, expect } from 'vitest'
6
- import {
7
- ErrorCode,
8
- ErrorNames,
9
- ErrorMessages,
10
- MGError,
11
- createError,
12
- } from '../../../src/shared/errors.js'
13
-
14
- describe('ErrorCode', () => {
15
- it('应该包含所有预定义错误码', () => {
16
- expect(ErrorCode.CONNECTION_FAILED).toBe('E001')
17
- expect(ErrorCode.NODE_NOT_FOUND).toBe('E005')
18
- expect(ErrorCode.INVALID_LINK).toBe('E010')
19
- expect(ErrorCode.SERVER_ALREADY_RUNNING).toBe('E016')
20
- })
21
- })
22
-
23
- describe('ErrorNames', () => {
24
- it('应该为每个错误码提供名称', () => {
25
- expect(ErrorNames[ErrorCode.CONNECTION_FAILED]).toBe('CONNECTION_FAILED')
26
- expect(ErrorNames[ErrorCode.NODE_NOT_FOUND]).toBe('NODE_NOT_FOUND')
27
- })
28
- })
29
-
30
- describe('ErrorMessages', () => {
31
- it('应该为每个错误码提供默认消息', () => {
32
- expect(ErrorMessages[ErrorCode.CONNECTION_FAILED]).toBe('无法连接到 MG Server')
33
- expect(ErrorMessages[ErrorCode.NODE_NOT_FOUND]).toBe('节点不存在')
34
- })
35
- })
36
-
37
- describe('MGError', () => {
38
- it('应该创建带有默认消息的错误', () => {
39
- const error = new MGError(ErrorCode.NODE_NOT_FOUND)
40
- expect(error.code).toBe('E005')
41
- expect(error.errorName).toBe('NODE_NOT_FOUND')
42
- expect(error.message).toBe('节点不存在')
43
- })
44
-
45
- it('应该创建带有自定义消息的错误', () => {
46
- const error = new MGError(ErrorCode.NODE_NOT_FOUND, '节点 123:456 不存在')
47
- expect(error.message).toBe('节点 123:456 不存在')
48
- })
49
-
50
- it('应该支持 details', () => {
51
- const error = new MGError(ErrorCode.NODE_NOT_FOUND, '节点不存在', {
52
- nodeId: '123:456',
53
- })
54
- expect(error.details).toEqual({ nodeId: '123:456' })
55
- })
56
-
57
- it('应该正确序列化为 JSON', () => {
58
- const error = new MGError(ErrorCode.NODE_NOT_FOUND, '节点不存在', {
59
- nodeId: '123:456',
60
- })
61
- const json = error.toJSON()
62
- expect(json).toEqual({
63
- code: 'E005',
64
- name: 'NODE_NOT_FOUND',
65
- message: '节点不存在',
66
- details: { nodeId: '123:456' },
67
- })
68
- })
69
-
70
- it('应该正确转换为字符串', () => {
71
- const error = new MGError(ErrorCode.NODE_NOT_FOUND)
72
- expect(error.toString()).toBe('错误 [E005]: 节点不存在')
73
- })
74
- })
75
-
76
- describe('createError', () => {
77
- it('应该创建 MGError 实例', () => {
78
- const error = createError(ErrorCode.INVALID_LINK)
79
- expect(error).toBeInstanceOf(MGError)
80
- expect(error.code).toBe('E010')
81
- })
82
- })
@@ -1,208 +0,0 @@
1
- /**
2
- * 工具函数测试
3
- */
4
-
5
- import { describe, it, expect } from 'vitest'
6
- import {
7
- normalizePageUrl,
8
- isDesignPageUrl,
9
- parseMgpLink,
10
- generateMgpLink,
11
- formatFileSize,
12
- formatDuration,
13
- generateId,
14
- extractFileId,
15
- extractFileIdFromUrl,
16
- extractFileIdFromMgpLink,
17
- } from '../../../src/shared/utils.js'
18
-
19
- describe('normalizePageUrl', () => {
20
- it('应该标准化完整 URL', () => {
21
- const url =
22
- 'https://mastergo.netease.com/file/174135798361888?fileOpenFrom=home&page_id=0%3A8347'
23
- const result = normalizePageUrl(url)
24
- expect(result).toBe('mastergo.netease.com/file/174135798361888')
25
- })
26
-
27
- it('应该处理不带查询参数的 URL', () => {
28
- const url = 'https://mastergo.netease.com/file/174135798361888'
29
- const result = normalizePageUrl(url)
30
- expect(result).toBe('mastergo.netease.com/file/174135798361888')
31
- })
32
-
33
- it('应该处理已标准化的 URL', () => {
34
- const url = 'mastergo.netease.com/file/174135798361888'
35
- const result = normalizePageUrl(url)
36
- expect(result).toBe('mastergo.netease.com/file/174135798361888')
37
- })
38
-
39
- it('应该处理 mastergo.com 域名', () => {
40
- const url = 'https://mastergo.com/file/123456'
41
- const result = normalizePageUrl(url)
42
- expect(result).toBe('mastergo.com/file/123456')
43
- })
44
- })
45
-
46
- describe('isDesignPageUrl', () => {
47
- it('应该识别设计页面 URL', () => {
48
- expect(isDesignPageUrl('/file/174135798361888')).toBe(true)
49
- expect(isDesignPageUrl('mastergo.com/file/123456')).toBe(true)
50
- })
51
-
52
- it('应该拒绝非设计页面 URL', () => {
53
- expect(isDesignPageUrl('/files/home')).toBe(false)
54
- expect(isDesignPageUrl('/files/recent')).toBe(false)
55
- expect(isDesignPageUrl('/files/trash')).toBe(false)
56
- })
57
- })
58
-
59
- describe('parseMgpLink', () => {
60
- it('应该解析有效的 mgp:// 链接', () => {
61
- const link = 'mgp://mastergo.netease.com/file/174135798361888?nodeId=123%3A456'
62
- const result = parseMgpLink(link)
63
- expect(result).toEqual({
64
- pageUrl: 'mastergo.netease.com/file/174135798361888',
65
- nodeId: '123:456',
66
- })
67
- })
68
-
69
- it('应该解析 mastergo.com 域名的链接', () => {
70
- const link = 'mgp://mastergo.com/file/123456?nodeId=789%3A101'
71
- const result = parseMgpLink(link)
72
- expect(result).toEqual({
73
- pageUrl: 'mastergo.com/file/123456',
74
- nodeId: '789:101',
75
- })
76
- })
77
-
78
- it('应该解析带父节点路径的链接', () => {
79
- const link = 'mgp://mastergo.netease.com/file/174875497054651?nodeId=0%3A8633&nodePath=314%3A13190%2F0%3A8633'
80
- const result = parseMgpLink(link)
81
- expect(result).toEqual({
82
- pageUrl: 'mastergo.netease.com/file/174875497054651',
83
- nodeId: '0:8633',
84
- nodePath: ['314:13190', '0:8633'],
85
- })
86
- })
87
-
88
- it('应该解析多层父节点路径的链接', () => {
89
- const link = 'mgp://mastergo.netease.com/file/123?nodeId=3%3A3&nodePath=1%3A1%2F2%3A2%2F3%3A3'
90
- const result = parseMgpLink(link)
91
- expect(result).toEqual({
92
- pageUrl: 'mastergo.netease.com/file/123',
93
- nodeId: '3:3',
94
- nodePath: ['1:1', '2:2', '3:3'],
95
- })
96
- })
97
-
98
- it('应该解析 nodeId 包含路径格式的链接', () => {
99
- // 用户直接复制的链接,nodeId 中包含路径
100
- const link = 'mgp://mastergo.netease.com/file/174875497054651?nodeId=314%3A24807%2F0%3A3443'
101
- const result = parseMgpLink(link)
102
- expect(result).toEqual({
103
- pageUrl: 'mastergo.netease.com/file/174875497054651',
104
- nodeId: '314:24807/0:3443',
105
- })
106
- })
107
-
108
- it('应该拒绝无效的链接', () => {
109
- // 不是 mgp:// 协议
110
- expect(parseMgpLink('https://mastergo.com/file/123?nodeId=1%3A2')).toBeNull()
111
- // 没有 nodeId 参数
112
- expect(parseMgpLink('mgp://mastergo.com/file/123')).toBeNull()
113
- // nodeId 格式不对(没有编码)
114
- expect(parseMgpLink('mgp://mastergo.com/file/123?nodeId=invalid')).toBeNull()
115
- // 完全无效
116
- expect(parseMgpLink('invalid')).toBeNull()
117
- // 没有查询参数
118
- expect(parseMgpLink('mgp://custom.domain.com/design/abc123')).toBeNull()
119
- })
120
- })
121
-
122
- describe('generateMgpLink', () => {
123
- it('应该生成正确的 mgp:// 链接', () => {
124
- const pageUrl = 'https://mastergo.netease.com/file/174135798361888'
125
- const nodeId = '123:456'
126
- const result = generateMgpLink(pageUrl, nodeId)
127
- expect(result).toBe('mgp://mastergo.netease.com/file/174135798361888?nodeId=123%3A456')
128
- })
129
-
130
- it('应该生成带 nodePath 的链接', () => {
131
- const pageUrl = 'https://mastergo.netease.com/file/123'
132
- const nodeId = '3:3'
133
- const nodePath = ['1:1', '2:2', '3:3']
134
- const result = generateMgpLink(pageUrl, nodeId, nodePath)
135
- expect(result).toBe('mgp://mastergo.netease.com/file/123?nodeId=3%3A3&nodePath=1%3A1%2F2%3A2%2F3%3A3')
136
- })
137
- })
138
-
139
- describe('formatFileSize', () => {
140
- it('应该格式化字节大小', () => {
141
- expect(formatFileSize(500)).toBe('500 字节')
142
- expect(formatFileSize(1024)).toBe('1.00 KB')
143
- expect(formatFileSize(1536)).toBe('1.50 KB')
144
- expect(formatFileSize(1048576)).toBe('1.00 MB')
145
- })
146
- })
147
-
148
- describe('formatDuration', () => {
149
- it('应该格式化时间间隔', () => {
150
- expect(formatDuration(5000)).toBe('5 秒')
151
- expect(formatDuration(65000)).toBe('1 分钟 5 秒')
152
- expect(formatDuration(3665000)).toBe('1 小时 1 分钟')
153
- expect(formatDuration(90000000)).toBe('1 天 1 小时')
154
- })
155
- })
156
-
157
- describe('generateId', () => {
158
- it('应该生成唯一 ID', () => {
159
- const id1 = generateId()
160
- const id2 = generateId()
161
- expect(id1).not.toBe(id2)
162
- expect(id1).toMatch(/^\d+_[a-z0-9]+$/)
163
- })
164
- })
165
-
166
- describe('extractFileIdFromUrl', () => {
167
- it('应该从完整 URL 提取 fileId', () => {
168
- expect(extractFileIdFromUrl('https://mastergo.netease.com/file/174875497054651?page_id=321%3A11979')).toBe('174875497054651')
169
- expect(extractFileIdFromUrl('https://mastergo.netease.com/file/123456')).toBe('123456')
170
- expect(extractFileIdFromUrl('mastergo.netease.com/file/789')).toBe('789')
171
- })
172
-
173
- it('应该处理无效 URL', () => {
174
- expect(extractFileIdFromUrl('https://mastergo.netease.com/files/home')).toBeNull()
175
- expect(extractFileIdFromUrl('invalid')).toBeNull()
176
- })
177
- })
178
-
179
- describe('extractFileIdFromMgpLink', () => {
180
- it('应该从 mgp:// 链接提取 fileId', () => {
181
- expect(extractFileIdFromMgpLink('mgp://mastergo.netease.com/file/174875497054651?nodeId=xxx')).toBe('174875497054651')
182
- })
183
-
184
- it('应该拒绝非 mgp:// 链接', () => {
185
- expect(extractFileIdFromMgpLink('https://mastergo.netease.com/file/123')).toBeNull()
186
- })
187
- })
188
-
189
- describe('extractFileId', () => {
190
- it('应该处理纯数字 fileId', () => {
191
- expect(extractFileId('174875497054651')).toBe('174875497054651')
192
- expect(extractFileId(' 123456 ')).toBe('123456')
193
- })
194
-
195
- it('应该处理完整 URL', () => {
196
- expect(extractFileId('https://mastergo.netease.com/file/174875497054651?page_id=xxx')).toBe('174875497054651')
197
- })
198
-
199
- it('应该处理 mgp:// 协议', () => {
200
- expect(extractFileId('mgp://mastergo.netease.com/file/174875497054651?nodeId=xxx')).toBe('174875497054651')
201
- })
202
-
203
- it('应该处理无效输入', () => {
204
- expect(extractFileId('invalid')).toBeNull()
205
- expect(extractFileId('https://example.com')).toBeNull()
206
- })
207
- // 需要先导入
208
- })
package/tsconfig.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "lib": ["ES2022"],
7
- "outDir": "./dist",
8
- "rootDir": "./src",
9
- "strict": true,
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "forceConsistentCasingInFileNames": true,
13
- "declaration": true,
14
- "declarationMap": true,
15
- "sourceMap": true,
16
- "resolveJsonModule": true,
17
- "isolatedModules": true,
18
- "noEmit": true
19
- },
20
- "include": ["src/**/*"],
21
- "exclude": ["node_modules", "dist", "tests"]
22
- }
package/tsup.config.ts DELETED
@@ -1,33 +0,0 @@
1
- import { defineConfig } from 'tsup'
2
-
3
- export default defineConfig([
4
- // 主模块(不需要 shebang)
5
- {
6
- entry: {
7
- index: 'src/index.ts',
8
- server: 'src/server/index.ts',
9
- },
10
- format: ['esm'],
11
- dts: true,
12
- clean: true,
13
- sourcemap: true,
14
- target: 'node20',
15
- splitting: false,
16
- },
17
- // CLI 入口(需要 shebang)
18
- {
19
- entry: {
20
- cli: 'src/cli/index.ts',
21
- 'daemon-runner': 'src/server/daemon-runner.ts',
22
- },
23
- format: ['esm'],
24
- dts: false,
25
- clean: false, // 不清理,避免覆盖上面的输出
26
- sourcemap: true,
27
- target: 'node20',
28
- splitting: false,
29
- banner: {
30
- js: '#!/usr/bin/env node',
31
- },
32
- },
33
- ])
package/vitest.config.ts DELETED
@@ -1,22 +0,0 @@
1
- import { defineConfig } from 'vitest/config'
2
-
3
- export default defineConfig({
4
- test: {
5
- globals: true,
6
- environment: 'node',
7
- include: ['tests/**/*.test.ts'],
8
- coverage: {
9
- provider: 'v8',
10
- reporter: ['text', 'json', 'html'],
11
- exclude: ['tests/**', 'dist/**', '*.config.*'],
12
- thresholds: {
13
- lines: 80,
14
- functions: 80,
15
- branches: 75,
16
- statements: 80,
17
- },
18
- },
19
- testTimeout: 10000,
20
- hookTimeout: 10000,
21
- },
22
- })