@bundy-lmw/hive-server 1.0.1 → 1.0.3
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/bootstrap.d.ts +3 -4
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +17 -24
- package/dist/bootstrap.js.map +1 -1
- package/dist/cli/commands/skill/index.d.ts +10 -0
- package/dist/cli/commands/skill/index.d.ts.map +1 -0
- package/dist/cli/commands/skill/index.js +174 -0
- package/dist/cli/commands/skill/index.js.map +1 -0
- package/dist/cli/commands/skill/installer.d.ts +75 -0
- package/dist/cli/commands/skill/installer.d.ts.map +1 -0
- package/dist/cli/commands/skill/installer.js +347 -0
- package/dist/cli/commands/skill/installer.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +3 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/gateway/http.d.ts +2 -1
- package/dist/gateway/http.d.ts.map +1 -1
- package/dist/gateway/http.js +17 -3
- package/dist/gateway/http.js.map +1 -1
- package/dist/gateway/ws/admin-handler.d.ts +24 -47
- package/dist/gateway/ws/admin-handler.d.ts.map +1 -1
- package/dist/gateway/ws/admin-handler.js +76 -465
- package/dist/gateway/ws/admin-handler.js.map +1 -1
- package/dist/gateway/ws/chat-handler.d.ts +42 -0
- package/dist/gateway/ws/chat-handler.d.ts.map +1 -0
- package/dist/gateway/ws/chat-handler.js +258 -0
- package/dist/gateway/ws/chat-handler.js.map +1 -0
- package/dist/gateway/ws/config-store.d.ts +16 -0
- package/dist/gateway/ws/config-store.d.ts.map +1 -0
- package/dist/gateway/ws/config-store.js +68 -0
- package/dist/gateway/ws/config-store.js.map +1 -0
- package/dist/gateway/ws/data-types.d.ts +3 -0
- package/dist/gateway/ws/data-types.d.ts.map +1 -1
- package/dist/gateway/ws/handler-context.d.ts +41 -0
- package/dist/gateway/ws/handler-context.d.ts.map +1 -0
- package/dist/gateway/ws/handler-context.js +8 -0
- package/dist/gateway/ws/handler-context.js.map +1 -0
- package/dist/gateway/ws/handlers/base.d.ts +14 -0
- package/dist/gateway/ws/handlers/base.d.ts.map +1 -0
- package/dist/gateway/ws/handlers/base.js +13 -0
- package/dist/gateway/ws/handlers/base.js.map +1 -0
- package/dist/gateway/ws/handlers/config-handler.d.ts +14 -0
- package/dist/gateway/ws/handlers/config-handler.d.ts.map +1 -0
- package/dist/gateway/ws/handlers/config-handler.js +53 -0
- package/dist/gateway/ws/handlers/config-handler.js.map +1 -0
- package/dist/gateway/ws/handlers/index.d.ts +10 -0
- package/dist/gateway/ws/handlers/index.d.ts.map +1 -0
- package/dist/gateway/ws/handlers/index.js +10 -0
- package/dist/gateway/ws/handlers/index.js.map +1 -0
- package/dist/gateway/ws/handlers/log-handler.d.ts +20 -0
- package/dist/gateway/ws/handlers/log-handler.d.ts.map +1 -0
- package/dist/gateway/ws/handlers/log-handler.js +65 -0
- package/dist/gateway/ws/handlers/log-handler.js.map +1 -0
- package/dist/gateway/ws/handlers/plugin-handler.d.ts +29 -0
- package/dist/gateway/ws/handlers/plugin-handler.d.ts.map +1 -0
- package/dist/gateway/ws/handlers/plugin-handler.js +197 -0
- package/dist/gateway/ws/handlers/plugin-handler.js.map +1 -0
- package/dist/gateway/ws/handlers/session-handler.d.ts +14 -0
- package/dist/gateway/ws/handlers/session-handler.d.ts.map +1 -0
- package/dist/gateway/ws/handlers/session-handler.js +36 -0
- package/dist/gateway/ws/handlers/session-handler.js.map +1 -0
- package/dist/gateway/ws/handlers/status-handler.d.ts +23 -0
- package/dist/gateway/ws/handlers/status-handler.d.ts.map +1 -0
- package/dist/gateway/ws/handlers/status-handler.js +136 -0
- package/dist/gateway/ws/handlers/status-handler.js.map +1 -0
- package/dist/gateway/ws/log-buffer.d.ts +1 -0
- package/dist/gateway/ws/log-buffer.d.ts.map +1 -1
- package/dist/gateway/ws/log-buffer.js +8 -0
- package/dist/gateway/ws/log-buffer.js.map +1 -1
- package/dist/gateway/ws/types.d.ts +1 -1
- package/dist/gateway/ws/types.d.ts.map +1 -1
- package/dist/gateway/ws/types.js.map +1 -1
- package/dist/graceful-shutdown.d.ts +13 -0
- package/dist/graceful-shutdown.d.ts.map +1 -0
- package/dist/graceful-shutdown.js +65 -0
- package/dist/graceful-shutdown.js.map +1 -0
- package/dist/logging/file-logger.d.ts +43 -0
- package/dist/logging/file-logger.d.ts.map +1 -0
- package/dist/logging/file-logger.js +134 -0
- package/dist/logging/file-logger.js.map +1 -0
- package/dist/logging/hive-logger.d.ts +26 -0
- package/dist/logging/hive-logger.d.ts.map +1 -0
- package/dist/logging/hive-logger.js +251 -0
- package/dist/logging/hive-logger.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +68 -19
- package/dist/main.js.map +1 -1
- package/dist/plugin-manager/installer.d.ts +1 -1
- package/dist/plugin-manager/installer.d.ts.map +1 -1
- package/dist/plugin-manager/installer.js +84 -17
- package/dist/plugin-manager/installer.js.map +1 -1
- package/dist/plugin-manager/searcher.js +1 -1
- package/dist/plugin-manager/searcher.js.map +1 -1
- package/dist/plugin-manager/types.d.ts +1 -0
- package/dist/plugin-manager/types.d.ts.map +1 -1
- package/dist/plugins.d.ts +18 -0
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +50 -22
- package/dist/plugins.js.map +1 -1
- package/package.json +6 -3
|
@@ -1,72 +1,77 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Admin WebSocket Handler
|
|
2
|
+
* Admin WebSocket Handler (Router)
|
|
3
3
|
*
|
|
4
|
-
* 处理 /ws/admin
|
|
5
|
-
*
|
|
6
|
-
* -
|
|
4
|
+
* 处理 /ws/admin 端点的管理协议消息。
|
|
5
|
+
* 业务逻辑委托给 Domain Handler,本类仅负责:
|
|
6
|
+
* - WS 连接管理
|
|
7
|
+
* - 消息路由(dispatch)
|
|
8
|
+
* - HandlerContext 构建
|
|
9
|
+
* - 生命周期管理
|
|
7
10
|
*/
|
|
8
11
|
import { EventEmitter } from 'node:events';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { createSuccessResponse, createErrorResponse, createEvent } from './types.js';
|
|
13
|
-
import { HIVE_HOME } from '../../config.js';
|
|
14
|
-
import { LogBuffer } from './log-buffer.js';
|
|
12
|
+
import { createErrorResponse, createEvent } from './types.js';
|
|
13
|
+
import { ConfigStore } from './config-store.js';
|
|
14
|
+
import { ConfigHandler, StatusHandler, PluginHandler, LogHandler, SessionHandler, } from './handlers/index.js';
|
|
15
15
|
// ============================================
|
|
16
|
-
// AdminWsHandler
|
|
16
|
+
// AdminWsHandler (Router)
|
|
17
17
|
// ============================================
|
|
18
18
|
export class AdminWsHandler extends EventEmitter {
|
|
19
19
|
clients = new Set();
|
|
20
20
|
logBuffer;
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
hiveLogger = null;
|
|
22
|
+
configStore;
|
|
23
23
|
server = null;
|
|
24
|
-
httpServer = null;
|
|
25
24
|
startTime;
|
|
26
25
|
handlers;
|
|
27
|
-
|
|
26
|
+
statusHandler;
|
|
27
|
+
pluginHandler;
|
|
28
|
+
constructor(hiveLogger, logBuffer) {
|
|
28
29
|
super();
|
|
29
|
-
this.logBuffer =
|
|
30
|
-
this.
|
|
30
|
+
this.logBuffer = logBuffer;
|
|
31
|
+
this.hiveLogger = hiveLogger;
|
|
32
|
+
this.configStore = new ConfigStore();
|
|
31
33
|
this.startTime = Date.now();
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
[
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
['log.unsubscribe', this.handleLogUnsubscribe.bind(this)],
|
|
49
|
-
['session.list', this.handleSessionList.bind(this)],
|
|
50
|
-
['session.get', this.handleSessionGet.bind(this)],
|
|
51
|
-
['session.delete', this.handleSessionDelete.bind(this)],
|
|
52
|
-
]);
|
|
53
|
-
// 拦截 console 输出
|
|
54
|
-
this.interceptConsole();
|
|
34
|
+
const ctx = this.createContext();
|
|
35
|
+
this.statusHandler = new StatusHandler(ctx, this.startTime);
|
|
36
|
+
this.pluginHandler = new PluginHandler(ctx);
|
|
37
|
+
const domains = [
|
|
38
|
+
new ConfigHandler(ctx),
|
|
39
|
+
this.statusHandler,
|
|
40
|
+
this.pluginHandler,
|
|
41
|
+
new LogHandler(ctx),
|
|
42
|
+
new SessionHandler(ctx),
|
|
43
|
+
];
|
|
44
|
+
this.handlers = new Map();
|
|
45
|
+
for (const domain of domains) {
|
|
46
|
+
for (const [method, handler] of domain.register()) {
|
|
47
|
+
this.handlers.set(method, handler);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
55
50
|
}
|
|
56
51
|
// ============================================
|
|
57
52
|
// 生命周期
|
|
58
53
|
// ============================================
|
|
59
|
-
/** 注入 Server 实例(在 start 后调用) */
|
|
60
54
|
setServer(server) {
|
|
61
55
|
this.server = server;
|
|
62
56
|
}
|
|
63
|
-
/** 注入 HttpServer 实例 */
|
|
64
57
|
setHttpServer(httpServer) {
|
|
65
|
-
this.httpServer
|
|
58
|
+
this.statusHandler.setHttpServer(httpServer);
|
|
59
|
+
}
|
|
60
|
+
setPlugins(plugins) {
|
|
61
|
+
this.pluginHandler.setPlugins(plugins);
|
|
62
|
+
}
|
|
63
|
+
getHiveLogger() {
|
|
64
|
+
return this.hiveLogger;
|
|
66
65
|
}
|
|
67
|
-
/**
|
|
66
|
+
/** Push a log entry to subscribed admin clients — called by main.ts subscriber */
|
|
67
|
+
pushLog(entry) {
|
|
68
|
+
this.broadcastLog(entry);
|
|
69
|
+
}
|
|
70
|
+
// ============================================
|
|
71
|
+
// 连接管理
|
|
72
|
+
// ============================================
|
|
68
73
|
handleConnection(ws) {
|
|
69
|
-
const client = { ws, logSubscribed: false };
|
|
74
|
+
const client = { ws, logSubscribed: false, threadIds: new Set() };
|
|
70
75
|
this.clients.add(client);
|
|
71
76
|
ws.on('message', (raw) => {
|
|
72
77
|
const data = raw.toString();
|
|
@@ -74,7 +79,7 @@ export class AdminWsHandler extends EventEmitter {
|
|
|
74
79
|
if (!msg)
|
|
75
80
|
return;
|
|
76
81
|
if (msg.type === 'req') {
|
|
77
|
-
this.handleRequest(msg).then(response => {
|
|
82
|
+
this.handleRequest(msg, client).then(response => {
|
|
78
83
|
ws.send(JSON.stringify(response));
|
|
79
84
|
});
|
|
80
85
|
}
|
|
@@ -86,8 +91,7 @@ export class AdminWsHandler extends EventEmitter {
|
|
|
86
91
|
this.clients.delete(client);
|
|
87
92
|
});
|
|
88
93
|
}
|
|
89
|
-
|
|
90
|
-
closeAll() {
|
|
94
|
+
async closeAll() {
|
|
91
95
|
this.broadcastEvent('server.shutting_down', { reason: 'shutdown' });
|
|
92
96
|
for (const client of this.clients) {
|
|
93
97
|
client.ws.close();
|
|
@@ -95,7 +99,7 @@ export class AdminWsHandler extends EventEmitter {
|
|
|
95
99
|
this.clients.clear();
|
|
96
100
|
}
|
|
97
101
|
// ============================================
|
|
98
|
-
//
|
|
102
|
+
// 消息路由
|
|
99
103
|
// ============================================
|
|
100
104
|
parseMessage(data) {
|
|
101
105
|
try {
|
|
@@ -110,252 +114,34 @@ export class AdminWsHandler extends EventEmitter {
|
|
|
110
114
|
return null;
|
|
111
115
|
}
|
|
112
116
|
}
|
|
113
|
-
async handleRequest(req) {
|
|
117
|
+
async handleRequest(req, client) {
|
|
114
118
|
const handler = this.handlers.get(req.method);
|
|
115
119
|
if (!handler) {
|
|
116
120
|
return createErrorResponse(req.id, 'NOT_FOUND', `Unknown method: ${req.method}`);
|
|
117
121
|
}
|
|
118
122
|
try {
|
|
119
|
-
return await handler(req.params, req.id);
|
|
123
|
+
return await handler(req.params, req.id, client.ws);
|
|
120
124
|
}
|
|
121
125
|
catch (error) {
|
|
122
126
|
return createErrorResponse(req.id, 'INTERNAL', error instanceof Error ? error.message : 'Unknown error');
|
|
123
127
|
}
|
|
124
128
|
}
|
|
125
129
|
// ============================================
|
|
126
|
-
//
|
|
127
|
-
// ============================================
|
|
128
|
-
handleConfigGet(_params, id) {
|
|
129
|
-
const config = this.loadConfig();
|
|
130
|
-
return createSuccessResponse(id, this.sensitizeConfig(config));
|
|
131
|
-
}
|
|
132
|
-
handleConfigUpdate(params, id) {
|
|
133
|
-
const updates = params;
|
|
134
|
-
const config = this.loadConfig();
|
|
135
|
-
if (updates.server)
|
|
136
|
-
Object.assign(config.server, updates.server);
|
|
137
|
-
if (updates.auth)
|
|
138
|
-
Object.assign(config.auth, updates.auth);
|
|
139
|
-
if (updates.provider)
|
|
140
|
-
Object.assign(config.provider, updates.provider);
|
|
141
|
-
if (updates.heartbeat)
|
|
142
|
-
Object.assign(config.heartbeat, updates.heartbeat);
|
|
143
|
-
this.saveConfig(config);
|
|
144
|
-
this.configCache = null; // 清除缓存
|
|
145
|
-
const changedKeys = Object.keys(updates);
|
|
146
|
-
this.broadcastEvent('config.changed', { keys: changedKeys });
|
|
147
|
-
return createSuccessResponse(id, { success: true });
|
|
148
|
-
}
|
|
149
|
-
handleGetProviderPresets(_params, id) {
|
|
150
|
-
if (!this.server) {
|
|
151
|
-
return createErrorResponse(id, 'INTERNAL', 'Server not initialized');
|
|
152
|
-
}
|
|
153
|
-
try {
|
|
154
|
-
const presets = this.server.agent.listPresets();
|
|
155
|
-
return createSuccessResponse(id, presets);
|
|
156
|
-
}
|
|
157
|
-
catch {
|
|
158
|
-
return createSuccessResponse(id, []);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
// ============================================
|
|
162
|
-
// Status Handlers
|
|
130
|
+
// HandlerContext
|
|
163
131
|
// ============================================
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
providerReady,
|
|
177
|
-
currentProvider: currentProvider?.id ?? null,
|
|
178
|
-
activePlugins: this.getActivePluginIds(),
|
|
179
|
-
},
|
|
180
|
-
system: {
|
|
181
|
-
memory: {
|
|
182
|
-
rss: process.memoryUsage().rss,
|
|
183
|
-
heapUsed: process.memoryUsage().heapUsed,
|
|
184
|
-
heapTotal: process.memoryUsage().heapTotal,
|
|
185
|
-
},
|
|
186
|
-
nodeVersion: process.version,
|
|
187
|
-
platform: `${process.platform} ${process.arch}`,
|
|
188
|
-
},
|
|
132
|
+
createContext() {
|
|
133
|
+
return {
|
|
134
|
+
broadcastEvent: (event, data) => this.broadcastEvent(event, data),
|
|
135
|
+
broadcastLog: (entry) => this.broadcastLog(entry),
|
|
136
|
+
loadConfig: () => this.configStore.load(),
|
|
137
|
+
saveConfig: (config) => this.configStore.save(config),
|
|
138
|
+
sensitizeConfig: (config) => this.configStore.sensitize(config),
|
|
139
|
+
getServer: () => this.server,
|
|
140
|
+
getLogBuffer: () => this.logBuffer,
|
|
141
|
+
getHiveLogger: () => this.hiveLogger,
|
|
142
|
+
getClients: () => this.clients,
|
|
143
|
+
findClientByWs: (ws) => this.findClientByWs(ws),
|
|
189
144
|
};
|
|
190
|
-
return createSuccessResponse(id, status);
|
|
191
|
-
}
|
|
192
|
-
handleServerRestart(_params, id) {
|
|
193
|
-
const response = createSuccessResponse(id, { success: true });
|
|
194
|
-
// 先广播事件,再退出
|
|
195
|
-
this.broadcastEvent('server.shutting_down', { reason: 'restart' });
|
|
196
|
-
setTimeout(() => {
|
|
197
|
-
process.exit(0);
|
|
198
|
-
}, 300);
|
|
199
|
-
return response;
|
|
200
|
-
}
|
|
201
|
-
handleGetProviders(_params, id) {
|
|
202
|
-
if (!this.server) {
|
|
203
|
-
return createErrorResponse(id, 'INTERNAL', 'Server not initialized');
|
|
204
|
-
}
|
|
205
|
-
const providers = this.server.agent.listProviders();
|
|
206
|
-
return createSuccessResponse(id, providers);
|
|
207
|
-
}
|
|
208
|
-
async handleProviderList(_params, id) {
|
|
209
|
-
if (!this.server) {
|
|
210
|
-
return createErrorResponse(id, 'INTERNAL', 'Server not initialized');
|
|
211
|
-
}
|
|
212
|
-
try {
|
|
213
|
-
const providers = await this.server.agent.listAllProviders();
|
|
214
|
-
return createSuccessResponse(id, providers.map(p => ({
|
|
215
|
-
id: p.id,
|
|
216
|
-
name: p.name,
|
|
217
|
-
logo: p.logo,
|
|
218
|
-
type: p.type,
|
|
219
|
-
defaultModel: p.models.length > 0 ? p.models[0].id : undefined,
|
|
220
|
-
modelCount: p.models.length,
|
|
221
|
-
})));
|
|
222
|
-
}
|
|
223
|
-
catch (error) {
|
|
224
|
-
return createErrorResponse(id, 'INTERNAL', error instanceof Error ? error.message : 'Failed to list providers');
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
async handleProviderGetModels(params, id) {
|
|
228
|
-
const { providerId } = params;
|
|
229
|
-
if (!providerId) {
|
|
230
|
-
return createErrorResponse(id, 'VALIDATION', 'providerId is required');
|
|
231
|
-
}
|
|
232
|
-
if (!this.server) {
|
|
233
|
-
return createErrorResponse(id, 'INTERNAL', 'Server not initialized');
|
|
234
|
-
}
|
|
235
|
-
try {
|
|
236
|
-
const models = await this.server.agent.listProviderModels(providerId);
|
|
237
|
-
return createSuccessResponse(id, models.map(m => ({
|
|
238
|
-
id: m.id,
|
|
239
|
-
name: m.name,
|
|
240
|
-
family: m.family,
|
|
241
|
-
contextWindow: m.contextWindow,
|
|
242
|
-
maxOutputTokens: m.maxOutputTokens,
|
|
243
|
-
})));
|
|
244
|
-
}
|
|
245
|
-
catch (error) {
|
|
246
|
-
return createErrorResponse(id, 'INTERNAL', error instanceof Error ? error.message : 'Failed to get models');
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
// ============================================
|
|
250
|
-
// Plugin Handlers
|
|
251
|
-
// ============================================
|
|
252
|
-
handlePluginList(_params, id) {
|
|
253
|
-
if (!this.server) {
|
|
254
|
-
return createSuccessResponse(id, []);
|
|
255
|
-
}
|
|
256
|
-
// 从已加载的插件获取信息
|
|
257
|
-
// 注意: 需要从 plugins.ts 的加载结果获取
|
|
258
|
-
const plugins = [];
|
|
259
|
-
return createSuccessResponse(id, plugins);
|
|
260
|
-
}
|
|
261
|
-
handlePluginInstall(params, id) {
|
|
262
|
-
const { source } = params;
|
|
263
|
-
if (!source || typeof source !== 'string') {
|
|
264
|
-
return createErrorResponse(id, 'VALIDATION', 'source is required');
|
|
265
|
-
}
|
|
266
|
-
try {
|
|
267
|
-
const pluginsDir = join(HIVE_HOME, 'plugins');
|
|
268
|
-
if (!existsSync(pluginsDir)) {
|
|
269
|
-
mkdirSync(pluginsDir, { recursive: true });
|
|
270
|
-
}
|
|
271
|
-
if (source.startsWith('https://') || source.startsWith('git+')) {
|
|
272
|
-
// Git install
|
|
273
|
-
this.installFromGit(source, pluginsDir);
|
|
274
|
-
}
|
|
275
|
-
else if (source.startsWith('.') || source.startsWith('/')) {
|
|
276
|
-
// Local path - copy
|
|
277
|
-
this.installFromLocal(source, pluginsDir);
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
// npm install
|
|
281
|
-
this.installFromNpm(source, pluginsDir);
|
|
282
|
-
}
|
|
283
|
-
this.broadcastEvent('plugin.installed', { id: source, name: source, version: 'latest' });
|
|
284
|
-
return createSuccessResponse(id, { id: source, name: source, version: 'latest', enabled: true });
|
|
285
|
-
}
|
|
286
|
-
catch (error) {
|
|
287
|
-
return createErrorResponse(id, 'INTERNAL', error instanceof Error ? error.message : 'Installation failed');
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
handlePluginUninstall(params, id) {
|
|
291
|
-
const { id: pluginId } = params;
|
|
292
|
-
if (!pluginId) {
|
|
293
|
-
return createErrorResponse(id, 'VALIDATION', 'id is required');
|
|
294
|
-
}
|
|
295
|
-
try {
|
|
296
|
-
const pluginDir = join(HIVE_HOME, 'plugins', pluginId);
|
|
297
|
-
if (existsSync(pluginDir)) {
|
|
298
|
-
execSync(`rm -rf "${pluginDir}"`, { stdio: 'pipe' });
|
|
299
|
-
}
|
|
300
|
-
this.broadcastEvent('plugin.uninstalled', { id: pluginId });
|
|
301
|
-
return createSuccessResponse(id, { success: true });
|
|
302
|
-
}
|
|
303
|
-
catch (error) {
|
|
304
|
-
return createErrorResponse(id, 'INTERNAL', error instanceof Error ? error.message : 'Uninstall failed');
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
handlePluginUpdateConfig(params, id) {
|
|
308
|
-
const { id: pluginId, config } = params;
|
|
309
|
-
if (!pluginId || !config) {
|
|
310
|
-
return createErrorResponse(id, 'VALIDATION', 'id and config are required');
|
|
311
|
-
}
|
|
312
|
-
try {
|
|
313
|
-
// TODO: 更新 hive.config.json 中对应插件的配置
|
|
314
|
-
return createSuccessResponse(id, { success: true });
|
|
315
|
-
}
|
|
316
|
-
catch (error) {
|
|
317
|
-
return createErrorResponse(id, 'INTERNAL', error instanceof Error ? error.message : 'Config update failed');
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
// ============================================
|
|
321
|
-
// Log Handlers
|
|
322
|
-
// ============================================
|
|
323
|
-
handleLogGetHistory(params, id) {
|
|
324
|
-
const options = params;
|
|
325
|
-
const entries = this.logBuffer.query(options);
|
|
326
|
-
return createSuccessResponse(id, entries);
|
|
327
|
-
}
|
|
328
|
-
handleLogSubscribe(_params, id, _ws) {
|
|
329
|
-
// 标记当前客户端为 log subscriber
|
|
330
|
-
// 由于 handler 签名限制,通过 event 方式处理
|
|
331
|
-
this.emit('log:subscribe');
|
|
332
|
-
return createSuccessResponse(id, { success: true });
|
|
333
|
-
}
|
|
334
|
-
handleLogUnsubscribe(_params, id) {
|
|
335
|
-
this.emit('log:unsubscribe');
|
|
336
|
-
return createSuccessResponse(id, { success: true });
|
|
337
|
-
}
|
|
338
|
-
// ============================================
|
|
339
|
-
// Session Handlers
|
|
340
|
-
// ============================================
|
|
341
|
-
handleSessionList(_params, id) {
|
|
342
|
-
// Session 数据由 core 的 SessionCapability 管理
|
|
343
|
-
// 暂时返回空列表,后续通过 server 实例获取
|
|
344
|
-
return createSuccessResponse(id, []);
|
|
345
|
-
}
|
|
346
|
-
handleSessionGet(params, id) {
|
|
347
|
-
const { id: sessionId } = params;
|
|
348
|
-
if (!sessionId) {
|
|
349
|
-
return createErrorResponse(id, 'VALIDATION', 'id is required');
|
|
350
|
-
}
|
|
351
|
-
return createSuccessResponse(id, null);
|
|
352
|
-
}
|
|
353
|
-
handleSessionDelete(params, id) {
|
|
354
|
-
const { id: sessionId } = params;
|
|
355
|
-
if (!sessionId) {
|
|
356
|
-
return createErrorResponse(id, 'VALIDATION', 'id is required');
|
|
357
|
-
}
|
|
358
|
-
return createSuccessResponse(id, { success: true });
|
|
359
145
|
}
|
|
360
146
|
// ============================================
|
|
361
147
|
// 事件广播
|
|
@@ -369,7 +155,6 @@ export class AdminWsHandler extends EventEmitter {
|
|
|
369
155
|
}
|
|
370
156
|
}
|
|
371
157
|
}
|
|
372
|
-
/** 推送日志到所有已订阅的客户端 */
|
|
373
158
|
broadcastLog(entry) {
|
|
374
159
|
const msg = createEvent('log', entry);
|
|
375
160
|
const payload = JSON.stringify(msg);
|
|
@@ -380,194 +165,20 @@ export class AdminWsHandler extends EventEmitter {
|
|
|
380
165
|
}
|
|
381
166
|
}
|
|
382
167
|
// ============================================
|
|
383
|
-
//
|
|
384
|
-
// ============================================
|
|
385
|
-
interceptConsole() {
|
|
386
|
-
const origLog = console.log;
|
|
387
|
-
const origWarn = console.warn;
|
|
388
|
-
const origError = console.error;
|
|
389
|
-
const origDebug = console.debug;
|
|
390
|
-
const extractSource = (args) => {
|
|
391
|
-
const first = String(args[0] ?? '');
|
|
392
|
-
// 匹配 [server], [plugin:feishu], [agent] 等来源标记
|
|
393
|
-
const match = first.match(/^\[([^\]]+)\]/);
|
|
394
|
-
return match ? match[1] : 'server';
|
|
395
|
-
};
|
|
396
|
-
const formatMessage = (args) => {
|
|
397
|
-
return args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ');
|
|
398
|
-
};
|
|
399
|
-
console.log = (...args) => {
|
|
400
|
-
origLog.apply(console, args);
|
|
401
|
-
const entry = this.logBuffer.add('info', extractSource(args), formatMessage(args));
|
|
402
|
-
this.broadcastLog(entry);
|
|
403
|
-
};
|
|
404
|
-
console.warn = (...args) => {
|
|
405
|
-
origWarn.apply(console, args);
|
|
406
|
-
const entry = this.logBuffer.add('warn', extractSource(args), formatMessage(args));
|
|
407
|
-
this.broadcastLog(entry);
|
|
408
|
-
};
|
|
409
|
-
console.error = (...args) => {
|
|
410
|
-
origError.apply(console, args);
|
|
411
|
-
const entry = this.logBuffer.add('error', extractSource(args), formatMessage(args));
|
|
412
|
-
this.broadcastLog(entry);
|
|
413
|
-
};
|
|
414
|
-
console.debug = (...args) => {
|
|
415
|
-
origDebug.apply(console, args);
|
|
416
|
-
const entry = this.logBuffer.add('debug', extractSource(args), formatMessage(args));
|
|
417
|
-
this.broadcastLog(entry);
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
// ============================================
|
|
421
|
-
// 配置读写
|
|
168
|
+
// 辅助
|
|
422
169
|
// ============================================
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
server: { port: 4450, host: '127.0.0.1', logLevel: 'info' },
|
|
428
|
-
auth: { enabled: false, apiKey: '' },
|
|
429
|
-
provider: { id: 'glm', apiKey: '', model: undefined },
|
|
430
|
-
heartbeat: { enabled: false, intervalMs: 300000 },
|
|
431
|
-
};
|
|
432
|
-
if (!existsSync(this.configPath)) {
|
|
433
|
-
this.configCache = defaults;
|
|
434
|
-
return defaults;
|
|
435
|
-
}
|
|
436
|
-
try {
|
|
437
|
-
const raw = JSON.parse(readFileSync(this.configPath, 'utf-8'));
|
|
438
|
-
const config = {
|
|
439
|
-
server: { ...defaults.server, ...raw.server },
|
|
440
|
-
auth: { ...defaults.auth, ...raw.auth },
|
|
441
|
-
provider: { ...defaults.provider, ...raw.provider },
|
|
442
|
-
heartbeat: { ...defaults.heartbeat, ...raw.heartbeat },
|
|
443
|
-
};
|
|
444
|
-
this.configCache = config;
|
|
445
|
-
return config;
|
|
446
|
-
}
|
|
447
|
-
catch {
|
|
448
|
-
this.configCache = defaults;
|
|
449
|
-
return defaults;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
saveConfig(config) {
|
|
453
|
-
const dir = resolve(this.configPath, '..');
|
|
454
|
-
if (!existsSync(dir)) {
|
|
455
|
-
mkdirSync(dir, { recursive: true });
|
|
456
|
-
}
|
|
457
|
-
writeFileSync(this.configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
458
|
-
this.configCache = config;
|
|
459
|
-
}
|
|
460
|
-
/** 脱敏配置中的敏感字段 */
|
|
461
|
-
sensitizeConfig(config) {
|
|
462
|
-
return {
|
|
463
|
-
...config,
|
|
464
|
-
auth: {
|
|
465
|
-
...config.auth,
|
|
466
|
-
apiKey: this.sensitizeApiKey(config.auth.apiKey),
|
|
467
|
-
},
|
|
468
|
-
provider: {
|
|
469
|
-
...config.provider,
|
|
470
|
-
apiKey: this.sensitizeApiKey(config.provider.apiKey),
|
|
471
|
-
},
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
sensitizeApiKey(key) {
|
|
475
|
-
if (!key || key.length <= 3)
|
|
476
|
-
return '***';
|
|
477
|
-
return `***${key.slice(-3)}`;
|
|
478
|
-
}
|
|
479
|
-
// ============================================
|
|
480
|
-
// 辅助方法
|
|
481
|
-
// ============================================
|
|
482
|
-
isProviderReady() {
|
|
483
|
-
const provider = this.server?.agent.currentProvider;
|
|
484
|
-
return !!provider?.apiKey && provider.apiKey.length > 0;
|
|
485
|
-
}
|
|
486
|
-
getPort() {
|
|
487
|
-
if (this.httpServer) {
|
|
488
|
-
const addr = this.httpServer.address();
|
|
489
|
-
if (typeof addr === 'object' && addr)
|
|
490
|
-
return addr.port;
|
|
491
|
-
}
|
|
492
|
-
return 4450;
|
|
493
|
-
}
|
|
494
|
-
getVersion() {
|
|
495
|
-
try {
|
|
496
|
-
const pkgPath = resolve(HIVE_HOME, 'package.json');
|
|
497
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
498
|
-
return pkg.version ?? '0.0.0';
|
|
499
|
-
}
|
|
500
|
-
catch {
|
|
501
|
-
return '0.0.0';
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
getActivePluginIds() {
|
|
505
|
-
// 暂时返回空列表
|
|
506
|
-
return [];
|
|
507
|
-
}
|
|
508
|
-
// ============================================
|
|
509
|
-
// 插件安装
|
|
510
|
-
// ============================================
|
|
511
|
-
installFromNpm(source, pluginsDir) {
|
|
512
|
-
execSync(`npm install --production --prefix "${pluginsDir}" "${source}"`, {
|
|
513
|
-
stdio: 'pipe',
|
|
514
|
-
timeout: 60_000,
|
|
515
|
-
});
|
|
516
|
-
}
|
|
517
|
-
installFromGit(url, pluginsDir) {
|
|
518
|
-
const tmpDir = resolve(pluginsDir, '.tmp-install');
|
|
519
|
-
if (existsSync(tmpDir)) {
|
|
520
|
-
execSync(`rm -rf "${tmpDir}"`, { stdio: 'pipe' });
|
|
521
|
-
}
|
|
522
|
-
execSync(`git clone --depth 1 "${url}" "${tmpDir}"`, {
|
|
523
|
-
stdio: 'pipe',
|
|
524
|
-
timeout: 60_000,
|
|
525
|
-
});
|
|
526
|
-
// 验证 package.json
|
|
527
|
-
const pkgPath = resolve(tmpDir, 'package.json');
|
|
528
|
-
if (!existsSync(pkgPath)) {
|
|
529
|
-
execSync(`rm -rf "${tmpDir}"`, { stdio: 'pipe' });
|
|
530
|
-
throw new Error('Invalid plugin: no package.json found');
|
|
531
|
-
}
|
|
532
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
533
|
-
if (!pkg.hive?.plugin) {
|
|
534
|
-
execSync(`rm -rf "${tmpDir}"`, { stdio: 'pipe' });
|
|
535
|
-
throw new Error('Invalid plugin: missing hive.plugin in package.json');
|
|
536
|
-
}
|
|
537
|
-
// 安装依赖
|
|
538
|
-
if (existsSync(resolve(tmpDir, 'package.json'))) {
|
|
539
|
-
execSync(`cd "${tmpDir}" && npm install --production`, {
|
|
540
|
-
stdio: 'pipe',
|
|
541
|
-
timeout: 60_000,
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
// 移动到 plugins 目录
|
|
545
|
-
const pluginName = pkg.name ?? url.split('/').pop() ?? 'unknown';
|
|
546
|
-
const targetDir = resolve(pluginsDir, pluginName);
|
|
547
|
-
execSync(`mv "${tmpDir}" "${targetDir}"`, { stdio: 'pipe' });
|
|
548
|
-
}
|
|
549
|
-
installFromLocal(source, pluginsDir) {
|
|
550
|
-
const resolvedSource = resolve(source);
|
|
551
|
-
if (!existsSync(resolvedSource)) {
|
|
552
|
-
throw new Error(`Path not found: ${source}`);
|
|
553
|
-
}
|
|
554
|
-
const pkgPath = resolve(resolvedSource, 'package.json');
|
|
555
|
-
if (!existsSync(pkgPath)) {
|
|
556
|
-
throw new Error('Invalid plugin: no package.json found');
|
|
557
|
-
}
|
|
558
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
559
|
-
if (!pkg.hive?.plugin) {
|
|
560
|
-
throw new Error('Invalid plugin: missing hive.plugin in package.json');
|
|
170
|
+
findClientByWs(ws) {
|
|
171
|
+
for (const client of this.clients) {
|
|
172
|
+
if (client.ws === ws)
|
|
173
|
+
return client;
|
|
561
174
|
}
|
|
562
|
-
|
|
563
|
-
const targetDir = resolve(pluginsDir, pluginName);
|
|
564
|
-
execSync(`cp -r "${resolvedSource}" "${targetDir}"`, { stdio: 'pipe' });
|
|
175
|
+
return undefined;
|
|
565
176
|
}
|
|
566
177
|
}
|
|
567
178
|
// ============================================
|
|
568
179
|
// 工厂函数
|
|
569
180
|
// ============================================
|
|
570
|
-
export function createAdminWsHandler() {
|
|
571
|
-
return new AdminWsHandler();
|
|
181
|
+
export function createAdminWsHandler(hiveLogger, logBuffer) {
|
|
182
|
+
return new AdminWsHandler(hiveLogger, logBuffer);
|
|
572
183
|
}
|
|
573
184
|
//# sourceMappingURL=admin-handler.js.map
|