@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.
- package/dist/cli.js +1580 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon-runner.js +794 -0
- package/dist/daemon-runner.js.map +1 -0
- package/dist/index-DNrszrq9.d.ts +568 -0
- package/dist/index.d.ts +129 -0
- package/dist/index.js +950 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +689 -0
- package/dist/server.js.map +1 -0
- package/package.json +5 -1
- package/.eslintrc.cjs +0 -26
- package/CLAUDE.md +0 -43
- package/src/cli/client.ts +0 -266
- package/src/cli/commands/execute-code.ts +0 -59
- package/src/cli/commands/export-image.ts +0 -193
- package/src/cli/commands/get-all-nodes.ts +0 -81
- package/src/cli/commands/get-all-pages.ts +0 -118
- package/src/cli/commands/get-node-by-id.ts +0 -83
- package/src/cli/commands/get-node-by-link.ts +0 -105
- package/src/cli/commands/server.ts +0 -130
- package/src/cli/index.ts +0 -33
- package/src/index.ts +0 -9
- package/src/server/connection-manager.ts +0 -211
- package/src/server/daemon-runner.ts +0 -22
- package/src/server/daemon.ts +0 -211
- package/src/server/index.ts +0 -8
- package/src/server/logger.ts +0 -117
- package/src/server/request-handler.ts +0 -192
- package/src/server/websocket-server.ts +0 -297
- package/src/shared/constants.ts +0 -90
- package/src/shared/errors.ts +0 -131
- package/src/shared/index.ts +0 -8
- package/src/shared/types.ts +0 -227
- package/src/shared/utils.ts +0 -352
- package/tests/unit/shared/constants.test.ts +0 -66
- package/tests/unit/shared/errors.test.ts +0 -82
- package/tests/unit/shared/utils.test.ts +0 -208
- package/tsconfig.json +0 -22
- package/tsup.config.ts +0 -33
- package/vitest.config.ts +0 -22
package/src/server/logger.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 日志模块
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { appendFileSync, existsSync, mkdirSync } from 'node:fs'
|
|
6
|
-
import { dirname } from 'node:path'
|
|
7
|
-
import { SERVER_LOG_FILE } from '../shared/constants.js'
|
|
8
|
-
import { formatLogTime } from '../shared/utils.js'
|
|
9
|
-
|
|
10
|
-
export enum LogLevel {
|
|
11
|
-
DEBUG = 'DEBUG',
|
|
12
|
-
INFO = 'INFO',
|
|
13
|
-
WARN = 'WARN',
|
|
14
|
-
ERROR = 'ERROR',
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface LoggerOptions {
|
|
18
|
-
/** 是否输出到控制台 */
|
|
19
|
-
console?: boolean
|
|
20
|
-
/** 是否输出到文件 */
|
|
21
|
-
file?: boolean
|
|
22
|
-
/** 日志文件路径 */
|
|
23
|
-
filePath?: string
|
|
24
|
-
/** 最小日志级别 */
|
|
25
|
-
minLevel?: LogLevel
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const levelPriority: Record<LogLevel, number> = {
|
|
29
|
-
[LogLevel.DEBUG]: 0,
|
|
30
|
-
[LogLevel.INFO]: 1,
|
|
31
|
-
[LogLevel.WARN]: 2,
|
|
32
|
-
[LogLevel.ERROR]: 3,
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 日志记录器
|
|
37
|
-
*/
|
|
38
|
-
export class Logger {
|
|
39
|
-
private options: Required<LoggerOptions>
|
|
40
|
-
|
|
41
|
-
constructor(options: LoggerOptions = {}) {
|
|
42
|
-
this.options = {
|
|
43
|
-
console: options.console ?? true,
|
|
44
|
-
file: options.file ?? false,
|
|
45
|
-
filePath: options.filePath ?? SERVER_LOG_FILE,
|
|
46
|
-
minLevel: options.minLevel ?? LogLevel.INFO,
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// 确保日志目录存在
|
|
50
|
-
if (this.options.file) {
|
|
51
|
-
const dir = dirname(this.options.filePath)
|
|
52
|
-
if (!existsSync(dir)) {
|
|
53
|
-
mkdirSync(dir, { recursive: true })
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* 记录日志
|
|
60
|
-
*/
|
|
61
|
-
private log(level: LogLevel, message: string, ...args: unknown[]): void {
|
|
62
|
-
// 检查日志级别
|
|
63
|
-
if (levelPriority[level] < levelPriority[this.options.minLevel]) {
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const timestamp = formatLogTime()
|
|
68
|
-
const formattedMessage = `[${timestamp}] [${level}] ${message}`
|
|
69
|
-
|
|
70
|
-
// 输出到控制台
|
|
71
|
-
if (this.options.console) {
|
|
72
|
-
const consoleMethod = level === LogLevel.ERROR ? console.error : console.log
|
|
73
|
-
if (args.length > 0) {
|
|
74
|
-
consoleMethod(formattedMessage, ...args)
|
|
75
|
-
} else {
|
|
76
|
-
consoleMethod(formattedMessage)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 输出到文件
|
|
81
|
-
if (this.options.file) {
|
|
82
|
-
try {
|
|
83
|
-
const fileMessage =
|
|
84
|
-
args.length > 0
|
|
85
|
-
? `${formattedMessage} ${JSON.stringify(args)}\n`
|
|
86
|
-
: `${formattedMessage}\n`
|
|
87
|
-
appendFileSync(this.options.filePath, fileMessage)
|
|
88
|
-
} catch (error) {
|
|
89
|
-
// 文件写入失败时静默处理
|
|
90
|
-
if (this.options.console) {
|
|
91
|
-
console.error('日志文件写入失败:', error)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
debug(message: string, ...args: unknown[]): void {
|
|
98
|
-
this.log(LogLevel.DEBUG, message, ...args)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
info(message: string, ...args: unknown[]): void {
|
|
102
|
-
this.log(LogLevel.INFO, message, ...args)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
warn(message: string, ...args: unknown[]): void {
|
|
106
|
-
this.log(LogLevel.WARN, message, ...args)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
error(message: string, ...args: unknown[]): void {
|
|
110
|
-
this.log(LogLevel.ERROR, message, ...args)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** 创建默认日志记录器 */
|
|
115
|
-
export function createLogger(options?: LoggerOptions): Logger {
|
|
116
|
-
return new Logger(options)
|
|
117
|
-
}
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 请求处理器
|
|
3
|
-
* 处理 Consumer 请求,转发给 Provider
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { ManagedWebSocket } from './connection-manager.js'
|
|
7
|
-
import { ConnectionManager } from './connection-manager.js'
|
|
8
|
-
import { Logger } from './logger.js'
|
|
9
|
-
import { MessageType, REQUEST_TIMEOUT } from '../shared/constants.js'
|
|
10
|
-
import { ErrorCode, MGError } from '../shared/errors.js'
|
|
11
|
-
import type { RequestMessage, ResponseMessage } from '../shared/types.js'
|
|
12
|
-
import { generateId } from '../shared/utils.js'
|
|
13
|
-
|
|
14
|
-
/** 待处理的请求 */
|
|
15
|
-
interface PendingRequest {
|
|
16
|
-
/** 请求 ID */
|
|
17
|
-
id: string
|
|
18
|
-
/** Consumer WebSocket */
|
|
19
|
-
consumer: ManagedWebSocket
|
|
20
|
-
/** 超时定时器 */
|
|
21
|
-
timer: NodeJS.Timeout
|
|
22
|
-
/** 请求时间 */
|
|
23
|
-
timestamp: number
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* 请求处理器
|
|
28
|
-
*/
|
|
29
|
-
export class RequestHandler {
|
|
30
|
-
private logger: Logger
|
|
31
|
-
private connectionManager: ConnectionManager
|
|
32
|
-
|
|
33
|
-
/** 待处理的请求 */
|
|
34
|
-
private pendingRequests = new Map<string, PendingRequest>()
|
|
35
|
-
|
|
36
|
-
constructor(connectionManager: ConnectionManager, logger: Logger) {
|
|
37
|
-
this.connectionManager = connectionManager
|
|
38
|
-
this.logger = logger
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 处理 Consumer 请求
|
|
43
|
-
*/
|
|
44
|
-
async handleRequest(consumer: ManagedWebSocket, message: RequestMessage): Promise<void> {
|
|
45
|
-
const requestId = message.id || generateId()
|
|
46
|
-
const { type, pageUrl, params } = message
|
|
47
|
-
|
|
48
|
-
this.logger.info(`收到请求: ${type} (${requestId})`, { pageUrl })
|
|
49
|
-
|
|
50
|
-
// 查找目标 Provider
|
|
51
|
-
let provider: ManagedWebSocket | undefined
|
|
52
|
-
|
|
53
|
-
if (pageUrl) {
|
|
54
|
-
provider = this.connectionManager.findProviderByPageUrl(pageUrl)
|
|
55
|
-
if (!provider) {
|
|
56
|
-
this.sendError(consumer, requestId, ErrorCode.PAGE_NOT_FOUND, `未找到页面: ${pageUrl}`)
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
// 没有指定 pageUrl,使用第一个可用的 Provider
|
|
61
|
-
provider = this.connectionManager.getFirstProvider()
|
|
62
|
-
if (!provider) {
|
|
63
|
-
this.sendError(consumer, requestId, ErrorCode.NO_PAGE_CONNECTED, '没有页面连接到 Server')
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// 创建超时定时器
|
|
69
|
-
const timer = setTimeout(() => {
|
|
70
|
-
this.handleTimeout(requestId)
|
|
71
|
-
}, REQUEST_TIMEOUT)
|
|
72
|
-
|
|
73
|
-
// 保存待处理请求
|
|
74
|
-
this.pendingRequests.set(requestId, {
|
|
75
|
-
id: requestId,
|
|
76
|
-
consumer,
|
|
77
|
-
timer,
|
|
78
|
-
timestamp: Date.now(),
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
// 转发请求给 Provider
|
|
82
|
-
const forwardMessage: RequestMessage = {
|
|
83
|
-
id: requestId,
|
|
84
|
-
type,
|
|
85
|
-
pageUrl: pageUrl || provider.connectionInfo.pageUrl,
|
|
86
|
-
params,
|
|
87
|
-
timestamp: Date.now(),
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
provider.send(JSON.stringify(forwardMessage))
|
|
92
|
-
this.logger.info(`请求转发: ${type} -> ${provider.connectionInfo.pageUrl}`)
|
|
93
|
-
} catch {
|
|
94
|
-
this.cleanupRequest(requestId)
|
|
95
|
-
this.sendError(consumer, requestId, ErrorCode.CONNECTION_FAILED, '转发请求失败')
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* 处理 Provider 响应
|
|
101
|
-
*/
|
|
102
|
-
handleResponse(response: ResponseMessage): void {
|
|
103
|
-
const { id } = response
|
|
104
|
-
const pending = this.pendingRequests.get(id)
|
|
105
|
-
|
|
106
|
-
if (!pending) {
|
|
107
|
-
this.logger.warn(`收到未知请求的响应: ${id}`)
|
|
108
|
-
return
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
this.cleanupRequest(id)
|
|
112
|
-
|
|
113
|
-
// 转发响应给 Consumer
|
|
114
|
-
try {
|
|
115
|
-
pending.consumer.send(JSON.stringify(response))
|
|
116
|
-
this.logger.info(
|
|
117
|
-
`响应返回: ${id} (${response.success ? '成功' : '失败'})`
|
|
118
|
-
)
|
|
119
|
-
} catch (error) {
|
|
120
|
-
this.logger.error(`响应转发失败: ${id}`, error)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* 处理请求超时
|
|
126
|
-
*/
|
|
127
|
-
private handleTimeout(requestId: string): void {
|
|
128
|
-
const pending = this.pendingRequests.get(requestId)
|
|
129
|
-
if (!pending) return
|
|
130
|
-
|
|
131
|
-
this.logger.warn(`请求超时: ${requestId}`)
|
|
132
|
-
this.cleanupRequest(requestId)
|
|
133
|
-
|
|
134
|
-
this.sendError(pending.consumer, requestId, ErrorCode.REQUEST_TIMEOUT, '请求超时')
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* 清理请求
|
|
139
|
-
*/
|
|
140
|
-
private cleanupRequest(requestId: string): void {
|
|
141
|
-
const pending = this.pendingRequests.get(requestId)
|
|
142
|
-
if (pending) {
|
|
143
|
-
clearTimeout(pending.timer)
|
|
144
|
-
this.pendingRequests.delete(requestId)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* 发送错误响应
|
|
150
|
-
*/
|
|
151
|
-
private sendError(
|
|
152
|
-
consumer: ManagedWebSocket,
|
|
153
|
-
requestId: string,
|
|
154
|
-
code: ErrorCode,
|
|
155
|
-
message: string
|
|
156
|
-
): void {
|
|
157
|
-
const error = new MGError(code, message)
|
|
158
|
-
const response: ResponseMessage = {
|
|
159
|
-
id: requestId,
|
|
160
|
-
type: MessageType.ERROR,
|
|
161
|
-
success: false,
|
|
162
|
-
data: null,
|
|
163
|
-
error: error.toJSON(),
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
try {
|
|
167
|
-
consumer.send(JSON.stringify(response))
|
|
168
|
-
} catch (err) {
|
|
169
|
-
this.logger.error(`发送错误响应失败: ${requestId}`, err)
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* 清理特定连接的所有待处理请求
|
|
175
|
-
*/
|
|
176
|
-
cleanupConnectionRequests(connectionId: string): void {
|
|
177
|
-
for (const [requestId, pending] of this.pendingRequests) {
|
|
178
|
-
if (pending.consumer.connectionId === connectionId) {
|
|
179
|
-
this.cleanupRequest(requestId)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* 清理所有待处理请求
|
|
186
|
-
*/
|
|
187
|
-
cleanupAll(): void {
|
|
188
|
-
for (const [requestId] of this.pendingRequests) {
|
|
189
|
-
this.cleanupRequest(requestId)
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WebSocket 服务器
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { WebSocketServer, WebSocket } from 'ws'
|
|
6
|
-
import type { IncomingMessage } from 'node:http'
|
|
7
|
-
import {
|
|
8
|
-
ConnectionType,
|
|
9
|
-
MessageType,
|
|
10
|
-
DEFAULT_PORT,
|
|
11
|
-
PORT_RANGE_START,
|
|
12
|
-
PORT_RANGE_END,
|
|
13
|
-
HEARTBEAT_INTERVAL,
|
|
14
|
-
} from '../shared/constants.js'
|
|
15
|
-
import { ErrorCode, MGError } from '../shared/errors.js'
|
|
16
|
-
import type { BaseMessage, RegisterMessage, ResponseMessage } from '../shared/types.js'
|
|
17
|
-
import { Logger, createLogger } from './logger.js'
|
|
18
|
-
import { ConnectionManager, ManagedWebSocket } from './connection-manager.js'
|
|
19
|
-
import { RequestHandler } from './request-handler.js'
|
|
20
|
-
|
|
21
|
-
export interface ServerOptions {
|
|
22
|
-
/** 监听端口 */
|
|
23
|
-
port?: number
|
|
24
|
-
/** 日志选项 */
|
|
25
|
-
logger?: Logger
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* MG WebSocket 服务器
|
|
30
|
-
*/
|
|
31
|
-
export class MGServer {
|
|
32
|
-
private wss: WebSocketServer | null = null
|
|
33
|
-
private logger: Logger
|
|
34
|
-
private connectionManager: ConnectionManager
|
|
35
|
-
private requestHandler: RequestHandler
|
|
36
|
-
|
|
37
|
-
private port: number
|
|
38
|
-
private isRunning = false
|
|
39
|
-
|
|
40
|
-
constructor(options: ServerOptions = {}) {
|
|
41
|
-
this.port = options.port || DEFAULT_PORT
|
|
42
|
-
this.logger = options.logger || createLogger()
|
|
43
|
-
this.connectionManager = new ConnectionManager(this.logger)
|
|
44
|
-
this.requestHandler = new RequestHandler(this.connectionManager, this.logger)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* 启动服务器
|
|
49
|
-
*/
|
|
50
|
-
async start(): Promise<number> {
|
|
51
|
-
if (this.isRunning) {
|
|
52
|
-
throw new MGError(ErrorCode.SERVER_ALREADY_RUNNING, 'Server 已在运行中')
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// 尝试绑定端口
|
|
56
|
-
const port = await this.findAvailablePort()
|
|
57
|
-
|
|
58
|
-
return new Promise((resolve, reject) => {
|
|
59
|
-
this.wss = new WebSocketServer({ port })
|
|
60
|
-
|
|
61
|
-
this.wss.on('listening', () => {
|
|
62
|
-
this.port = port
|
|
63
|
-
this.isRunning = true
|
|
64
|
-
this.logger.info(`Server 启动成功,监听端口: ${port}`)
|
|
65
|
-
|
|
66
|
-
// 启动心跳检查
|
|
67
|
-
this.connectionManager.startHeartbeatCheck(HEARTBEAT_INTERVAL)
|
|
68
|
-
|
|
69
|
-
resolve(port)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
this.wss.on('error', (error: NodeJS.ErrnoException) => {
|
|
73
|
-
this.logger.error('Server 错误:', error)
|
|
74
|
-
reject(error)
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
this.wss.on('connection', (ws: WebSocket, request: IncomingMessage) => {
|
|
78
|
-
this.handleConnection(ws, request)
|
|
79
|
-
})
|
|
80
|
-
})
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* 查找可用端口
|
|
85
|
-
*/
|
|
86
|
-
private async findAvailablePort(): Promise<number> {
|
|
87
|
-
for (let port = PORT_RANGE_START; port <= PORT_RANGE_END; port++) {
|
|
88
|
-
const available = await this.isPortAvailable(port)
|
|
89
|
-
if (available) {
|
|
90
|
-
return port
|
|
91
|
-
}
|
|
92
|
-
this.logger.debug(`端口 ${port} 被占用,尝试下一个`)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
throw new MGError(
|
|
96
|
-
ErrorCode.PORT_EXHAUSTED,
|
|
97
|
-
`端口 ${PORT_RANGE_START}-${PORT_RANGE_END} 均被占用`
|
|
98
|
-
)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* 检查端口是否可用
|
|
103
|
-
*/
|
|
104
|
-
private isPortAvailable(port: number): Promise<boolean> {
|
|
105
|
-
return new Promise((resolve) => {
|
|
106
|
-
const testServer = new WebSocketServer({ port })
|
|
107
|
-
|
|
108
|
-
testServer.on('listening', () => {
|
|
109
|
-
testServer.close()
|
|
110
|
-
resolve(true)
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
testServer.on('error', () => {
|
|
114
|
-
resolve(false)
|
|
115
|
-
})
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* 处理新连接
|
|
121
|
-
*/
|
|
122
|
-
private handleConnection(ws: WebSocket, _request: IncomingMessage): void {
|
|
123
|
-
this.logger.info('新连接建立')
|
|
124
|
-
|
|
125
|
-
// 等待注册消息
|
|
126
|
-
const registerTimeout = setTimeout(() => {
|
|
127
|
-
this.logger.warn('连接注册超时,关闭连接')
|
|
128
|
-
ws.close()
|
|
129
|
-
}, 5000)
|
|
130
|
-
|
|
131
|
-
ws.on('message', (data) => {
|
|
132
|
-
try {
|
|
133
|
-
const message = JSON.parse(data.toString()) as BaseMessage
|
|
134
|
-
|
|
135
|
-
// 处理注册消息
|
|
136
|
-
if (message.type === MessageType.REGISTER) {
|
|
137
|
-
clearTimeout(registerTimeout)
|
|
138
|
-
this.handleRegister(ws, message as RegisterMessage)
|
|
139
|
-
return
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// 其他消息需要已注册
|
|
143
|
-
const managedWs = ws as ManagedWebSocket
|
|
144
|
-
if (!managedWs.connectionId) {
|
|
145
|
-
this.logger.warn('未注册的连接发送消息,忽略')
|
|
146
|
-
return
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
this.handleMessage(managedWs, message)
|
|
150
|
-
} catch (error) {
|
|
151
|
-
this.logger.error('消息解析失败:', error)
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
ws.on('close', () => {
|
|
156
|
-
clearTimeout(registerTimeout)
|
|
157
|
-
const managedWs = ws as ManagedWebSocket
|
|
158
|
-
if (managedWs.connectionId) {
|
|
159
|
-
this.requestHandler.cleanupConnectionRequests(managedWs.connectionId)
|
|
160
|
-
this.connectionManager.removeConnection(managedWs)
|
|
161
|
-
}
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
ws.on('error', (error) => {
|
|
165
|
-
this.logger.error('WebSocket 错误:', error)
|
|
166
|
-
})
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* 处理注册消息
|
|
171
|
-
*/
|
|
172
|
-
private handleRegister(ws: WebSocket, message: RegisterMessage): void {
|
|
173
|
-
const { connectionType, pageUrl, pageId } = message.data
|
|
174
|
-
|
|
175
|
-
const managedWs = this.connectionManager.addConnection(
|
|
176
|
-
ws,
|
|
177
|
-
connectionType,
|
|
178
|
-
pageUrl,
|
|
179
|
-
pageId
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
// 发送注册确认
|
|
183
|
-
const ack: ResponseMessage = {
|
|
184
|
-
id: message.id || '',
|
|
185
|
-
type: MessageType.REGISTER_ACK,
|
|
186
|
-
success: true,
|
|
187
|
-
data: {
|
|
188
|
-
connectionId: managedWs.connectionId,
|
|
189
|
-
pageUrl,
|
|
190
|
-
},
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
ws.send(JSON.stringify(ack))
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* 处理消息
|
|
198
|
-
*/
|
|
199
|
-
private handleMessage(ws: ManagedWebSocket, message: BaseMessage): void {
|
|
200
|
-
this.connectionManager.updateLastActive(ws)
|
|
201
|
-
|
|
202
|
-
switch (message.type) {
|
|
203
|
-
case MessageType.PING:
|
|
204
|
-
this.handlePing(ws, message)
|
|
205
|
-
break
|
|
206
|
-
|
|
207
|
-
case MessageType.RESPONSE:
|
|
208
|
-
case MessageType.ERROR:
|
|
209
|
-
// Provider 返回的响应
|
|
210
|
-
this.requestHandler.handleResponse(message as ResponseMessage)
|
|
211
|
-
break
|
|
212
|
-
|
|
213
|
-
default:
|
|
214
|
-
// Consumer 发送的请求
|
|
215
|
-
if (ws.connectionInfo.type === ConnectionType.CONSUMER) {
|
|
216
|
-
this.requestHandler.handleRequest(ws, message as any)
|
|
217
|
-
}
|
|
218
|
-
break
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* 处理心跳
|
|
224
|
-
*/
|
|
225
|
-
private handlePing(ws: ManagedWebSocket, message: BaseMessage): void {
|
|
226
|
-
const pong = {
|
|
227
|
-
type: MessageType.PONG,
|
|
228
|
-
timestamp: (message as any).timestamp || Date.now(),
|
|
229
|
-
}
|
|
230
|
-
ws.send(JSON.stringify(pong))
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* 停止服务器
|
|
235
|
-
*/
|
|
236
|
-
async stop(): Promise<void> {
|
|
237
|
-
if (!this.isRunning || !this.wss) {
|
|
238
|
-
return
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
this.logger.info('正在停止 Server...')
|
|
242
|
-
|
|
243
|
-
// 清理所有请求
|
|
244
|
-
this.requestHandler.cleanupAll()
|
|
245
|
-
|
|
246
|
-
// 关闭所有连接
|
|
247
|
-
this.connectionManager.closeAll()
|
|
248
|
-
|
|
249
|
-
// 关闭服务器
|
|
250
|
-
return new Promise((resolve) => {
|
|
251
|
-
this.wss!.close(() => {
|
|
252
|
-
this.isRunning = false
|
|
253
|
-
this.wss = null
|
|
254
|
-
this.logger.info('Server 已停止')
|
|
255
|
-
resolve()
|
|
256
|
-
})
|
|
257
|
-
})
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* 获取运行状态
|
|
262
|
-
*/
|
|
263
|
-
getStatus(): {
|
|
264
|
-
running: boolean
|
|
265
|
-
port: number
|
|
266
|
-
stats: { providers: number; consumers: number; total: number }
|
|
267
|
-
connectedPages: string[]
|
|
268
|
-
} {
|
|
269
|
-
return {
|
|
270
|
-
running: this.isRunning,
|
|
271
|
-
port: this.port,
|
|
272
|
-
stats: this.connectionManager.getStats(),
|
|
273
|
-
connectedPages: this.connectionManager.getConnectedPageUrls(),
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* 获取端口
|
|
279
|
-
*/
|
|
280
|
-
getPort(): number {
|
|
281
|
-
return this.port
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* 是否运行中
|
|
286
|
-
*/
|
|
287
|
-
isServerRunning(): boolean {
|
|
288
|
-
return this.isRunning
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* 创建服务器实例
|
|
294
|
-
*/
|
|
295
|
-
export function createServer(options?: ServerOptions): MGServer {
|
|
296
|
-
return new MGServer(options)
|
|
297
|
-
}
|
package/src/shared/constants.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MG Plugin 常量定义
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { homedir } from 'node:os'
|
|
6
|
-
import { join } from 'node:path'
|
|
7
|
-
|
|
8
|
-
// ==================== 端口配置 ====================
|
|
9
|
-
|
|
10
|
-
/** 默认端口 */
|
|
11
|
-
export const DEFAULT_PORT = 9527
|
|
12
|
-
|
|
13
|
-
/** 端口范围:起始 */
|
|
14
|
-
export const PORT_RANGE_START = 9527
|
|
15
|
-
|
|
16
|
-
/** 端口范围:结束 */
|
|
17
|
-
export const PORT_RANGE_END = 9536
|
|
18
|
-
|
|
19
|
-
/** 最大尝试端口数 */
|
|
20
|
-
export const MAX_PORT_ATTEMPTS = 10
|
|
21
|
-
|
|
22
|
-
/** 端口扫描超时(毫秒) */
|
|
23
|
-
export const PORT_SCAN_TIMEOUT = 500
|
|
24
|
-
|
|
25
|
-
// ==================== 路径配置 ====================
|
|
26
|
-
|
|
27
|
-
/** 配置目录 */
|
|
28
|
-
export const CONFIG_DIR = join(homedir(), '.mg-plugin')
|
|
29
|
-
|
|
30
|
-
/** Server 状态文件 */
|
|
31
|
-
export const SERVER_INFO_FILE = join(CONFIG_DIR, 'server.json')
|
|
32
|
-
|
|
33
|
-
/** 日志目录 */
|
|
34
|
-
export const LOG_DIR = join(CONFIG_DIR, 'logs')
|
|
35
|
-
|
|
36
|
-
/** Server 日志文件 */
|
|
37
|
-
export const SERVER_LOG_FILE = join(LOG_DIR, 'server.log')
|
|
38
|
-
|
|
39
|
-
// ==================== 超时配置 ====================
|
|
40
|
-
|
|
41
|
-
/** 心跳间隔(毫秒)- 30 秒 */
|
|
42
|
-
export const HEARTBEAT_INTERVAL = 30000
|
|
43
|
-
|
|
44
|
-
/** 心跳超时(毫秒)- 90 秒(3 次心跳) */
|
|
45
|
-
export const HEARTBEAT_TIMEOUT = 90000
|
|
46
|
-
|
|
47
|
-
/** 请求超时(毫秒)- 30 秒 */
|
|
48
|
-
export const REQUEST_TIMEOUT = 30000
|
|
49
|
-
|
|
50
|
-
/** Server 启动等待超时(毫秒)- 5 秒 */
|
|
51
|
-
export const SERVER_START_TIMEOUT = 5000
|
|
52
|
-
|
|
53
|
-
/** CLI 重试间隔(毫秒) */
|
|
54
|
-
export const RETRY_INTERVALS = [1000, 2000, 4000]
|
|
55
|
-
|
|
56
|
-
/** 最大重试次数 */
|
|
57
|
-
export const MAX_RETRY_COUNT = 3
|
|
58
|
-
|
|
59
|
-
// ==================== 连接类型 ====================
|
|
60
|
-
|
|
61
|
-
/** 连接类型 */
|
|
62
|
-
export enum ConnectionType {
|
|
63
|
-
/** 获取端 (CLI/MCP) */
|
|
64
|
-
CONSUMER = 'consumer',
|
|
65
|
-
/** 提供端 (Injector) */
|
|
66
|
-
PROVIDER = 'provider',
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// ==================== 消息类型 ====================
|
|
70
|
-
|
|
71
|
-
/** WebSocket 消息类型 */
|
|
72
|
-
export enum MessageType {
|
|
73
|
-
// 系统消息
|
|
74
|
-
PING = 'ping',
|
|
75
|
-
PONG = 'pong',
|
|
76
|
-
REGISTER = 'register',
|
|
77
|
-
REGISTER_ACK = 'register_ack',
|
|
78
|
-
|
|
79
|
-
// 业务消息
|
|
80
|
-
GET_NODE_BY_ID = 'get_node_by_id',
|
|
81
|
-
GET_ALL_NODES = 'get_all_nodes',
|
|
82
|
-
GET_SELECTION = 'get_selection',
|
|
83
|
-
EXPORT_IMAGE = 'export_image',
|
|
84
|
-
EXECUTE_CODE = 'execute_code',
|
|
85
|
-
GET_ALL_PAGES = 'get_all_pages',
|
|
86
|
-
|
|
87
|
-
// 响应
|
|
88
|
-
RESPONSE = 'response',
|
|
89
|
-
ERROR = 'error',
|
|
90
|
-
}
|