@bundy-lmw/hive-server 1.0.1
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/LICENSE +21 -0
- package/README.md +211 -0
- package/dist/bootstrap.d.ts +36 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +86 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +125 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config.d.ts +93 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +156 -0
- package/dist/config.js.map +1 -0
- package/dist/gateway/auth.d.ts +16 -0
- package/dist/gateway/auth.d.ts.map +1 -0
- package/dist/gateway/auth.js +50 -0
- package/dist/gateway/auth.js.map +1 -0
- package/dist/gateway/http.d.ts +9 -0
- package/dist/gateway/http.d.ts.map +1 -0
- package/dist/gateway/http.js +178 -0
- package/dist/gateway/http.js.map +1 -0
- package/dist/gateway/websocket.d.ts +18 -0
- package/dist/gateway/websocket.d.ts.map +1 -0
- package/dist/gateway/websocket.js +197 -0
- package/dist/gateway/websocket.js.map +1 -0
- package/dist/gateway/ws/admin-handler.d.ts +68 -0
- package/dist/gateway/ws/admin-handler.d.ts.map +1 -0
- package/dist/gateway/ws/admin-handler.js +573 -0
- package/dist/gateway/ws/admin-handler.js.map +1 -0
- package/dist/gateway/ws/data-types.d.ts +130 -0
- package/dist/gateway/ws/data-types.d.ts.map +1 -0
- package/dist/gateway/ws/data-types.js +7 -0
- package/dist/gateway/ws/data-types.js.map +1 -0
- package/dist/gateway/ws/log-buffer.d.ts +29 -0
- package/dist/gateway/ws/log-buffer.d.ts.map +1 -0
- package/dist/gateway/ws/log-buffer.js +58 -0
- package/dist/gateway/ws/log-buffer.js.map +1 -0
- package/dist/gateway/ws/types.d.ts +67 -0
- package/dist/gateway/ws/types.d.ts.map +1 -0
- package/dist/gateway/ws/types.js +68 -0
- package/dist/gateway/ws/types.js.map +1 -0
- package/dist/heartbeat-scheduler.d.ts +43 -0
- package/dist/heartbeat-scheduler.d.ts.map +1 -0
- package/dist/heartbeat-scheduler.js +118 -0
- package/dist/heartbeat-scheduler.js.map +1 -0
- package/dist/main.d.ts +23 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +120 -0
- package/dist/main.js.map +1 -0
- package/dist/plugin-manager/cli.d.ts +6 -0
- package/dist/plugin-manager/cli.d.ts.map +1 -0
- package/dist/plugin-manager/cli.js +115 -0
- package/dist/plugin-manager/cli.js.map +1 -0
- package/dist/plugin-manager/constants.d.ts +24 -0
- package/dist/plugin-manager/constants.d.ts.map +1 -0
- package/dist/plugin-manager/constants.js +58 -0
- package/dist/plugin-manager/constants.js.map +1 -0
- package/dist/plugin-manager/index.d.ts +11 -0
- package/dist/plugin-manager/index.d.ts.map +1 -0
- package/dist/plugin-manager/index.js +10 -0
- package/dist/plugin-manager/index.js.map +1 -0
- package/dist/plugin-manager/installer.d.ts +19 -0
- package/dist/plugin-manager/installer.d.ts.map +1 -0
- package/dist/plugin-manager/installer.js +256 -0
- package/dist/plugin-manager/installer.js.map +1 -0
- package/dist/plugin-manager/manager.d.ts +39 -0
- package/dist/plugin-manager/manager.d.ts.map +1 -0
- package/dist/plugin-manager/manager.js +171 -0
- package/dist/plugin-manager/manager.js.map +1 -0
- package/dist/plugin-manager/registry.d.ts +31 -0
- package/dist/plugin-manager/registry.d.ts.map +1 -0
- package/dist/plugin-manager/registry.js +63 -0
- package/dist/plugin-manager/registry.js.map +1 -0
- package/dist/plugin-manager/searcher.d.ts +18 -0
- package/dist/plugin-manager/searcher.d.ts.map +1 -0
- package/dist/plugin-manager/searcher.js +50 -0
- package/dist/plugin-manager/searcher.js.map +1 -0
- package/dist/plugin-manager/types.d.ts +71 -0
- package/dist/plugin-manager/types.d.ts.map +1 -0
- package/dist/plugin-manager/types.js +5 -0
- package/dist/plugin-manager/types.js.map +1 -0
- package/dist/plugins.d.ts +18 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +198 -0
- package/dist/plugins.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../src/gateway/websocket.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin WebSocket Handler
|
|
3
|
+
*
|
|
4
|
+
* 处理 /ws/admin 端点的所有管理协议消息:
|
|
5
|
+
* - req/res: 配置管理、服务状态、插件管理、日志查询
|
|
6
|
+
* - event: 日志流推送、状态变更通知
|
|
7
|
+
*/
|
|
8
|
+
import { EventEmitter } from 'node:events';
|
|
9
|
+
import type { WebSocket } from 'ws';
|
|
10
|
+
import type { Server as HttpServer } from 'node:http';
|
|
11
|
+
import type { Server } from '@bundy-lmw/hive-core';
|
|
12
|
+
export declare class AdminWsHandler extends EventEmitter {
|
|
13
|
+
private clients;
|
|
14
|
+
private logBuffer;
|
|
15
|
+
private configCache;
|
|
16
|
+
private configPath;
|
|
17
|
+
private server;
|
|
18
|
+
private httpServer;
|
|
19
|
+
private startTime;
|
|
20
|
+
private handlers;
|
|
21
|
+
constructor();
|
|
22
|
+
/** 注入 Server 实例(在 start 后调用) */
|
|
23
|
+
setServer(server: Server): void;
|
|
24
|
+
/** 注入 HttpServer 实例 */
|
|
25
|
+
setHttpServer(httpServer: HttpServer): void;
|
|
26
|
+
/** 处理新的 WS 连接 */
|
|
27
|
+
handleConnection(ws: WebSocket): void;
|
|
28
|
+
/** 关闭所有连接(graceful shutdown 时调用) */
|
|
29
|
+
closeAll(): void;
|
|
30
|
+
private parseMessage;
|
|
31
|
+
private handleRequest;
|
|
32
|
+
private handleConfigGet;
|
|
33
|
+
private handleConfigUpdate;
|
|
34
|
+
private handleGetProviderPresets;
|
|
35
|
+
private handleStatusGet;
|
|
36
|
+
private handleServerRestart;
|
|
37
|
+
private handleGetProviders;
|
|
38
|
+
private handleProviderList;
|
|
39
|
+
private handleProviderGetModels;
|
|
40
|
+
private handlePluginList;
|
|
41
|
+
private handlePluginInstall;
|
|
42
|
+
private handlePluginUninstall;
|
|
43
|
+
private handlePluginUpdateConfig;
|
|
44
|
+
private handleLogGetHistory;
|
|
45
|
+
private handleLogSubscribe;
|
|
46
|
+
private handleLogUnsubscribe;
|
|
47
|
+
private handleSessionList;
|
|
48
|
+
private handleSessionGet;
|
|
49
|
+
private handleSessionDelete;
|
|
50
|
+
private broadcastEvent;
|
|
51
|
+
/** 推送日志到所有已订阅的客户端 */
|
|
52
|
+
private broadcastLog;
|
|
53
|
+
private interceptConsole;
|
|
54
|
+
private loadConfig;
|
|
55
|
+
private saveConfig;
|
|
56
|
+
/** 脱敏配置中的敏感字段 */
|
|
57
|
+
private sensitizeConfig;
|
|
58
|
+
private sensitizeApiKey;
|
|
59
|
+
private isProviderReady;
|
|
60
|
+
private getPort;
|
|
61
|
+
private getVersion;
|
|
62
|
+
private getActivePluginIds;
|
|
63
|
+
private installFromNpm;
|
|
64
|
+
private installFromGit;
|
|
65
|
+
private installFromLocal;
|
|
66
|
+
}
|
|
67
|
+
export declare function createAdminWsHandler(): AdminWsHandler;
|
|
68
|
+
//# sourceMappingURL=admin-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-handler.d.ts","sourceRoot":"","sources":["../../../src/gateway/ws/admin-handler.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAI1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AAMnC,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,WAAW,CAAA;AAUrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAiBlD,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,SAAS,CAAW;IAC5B,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,QAAQ,CAA4B;;IAsC5C,gCAAgC;IAChC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,uBAAuB;IACvB,aAAa,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;IAI3C,iBAAiB;IACjB,gBAAgB,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IAyBrC,oCAAoC;IACpC,QAAQ,IAAI,IAAI;IAYhB,OAAO,CAAC,YAAY;YAWN,aAAa;IAyB3B,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,kBAAkB;IAkB1B,OAAO,CAAC,wBAAwB;IAiBhC,OAAO,CAAC,eAAe;IA+BvB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,kBAAkB;YASZ,kBAAkB;YAoBlB,uBAAuB;IA4BrC,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,mBAAmB;IAmC3B,OAAO,CAAC,qBAAqB;IAuB7B,OAAO,CAAC,wBAAwB;IAsBhC,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,cAAc;IAUtB,qBAAqB;IACrB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,gBAAgB;IA8CxB,OAAO,CAAC,UAAU;IA+BlB,OAAO,CAAC,UAAU;IASlB,iBAAiB;IACjB,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,cAAc;IAqCtB,OAAO,CAAC,gBAAgB;CAoBzB;AAMD,wBAAgB,oBAAoB,IAAI,cAAc,CAErD"}
|
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin WebSocket Handler
|
|
3
|
+
*
|
|
4
|
+
* 处理 /ws/admin 端点的所有管理协议消息:
|
|
5
|
+
* - req/res: 配置管理、服务状态、插件管理、日志查询
|
|
6
|
+
* - event: 日志流推送、状态变更通知
|
|
7
|
+
*/
|
|
8
|
+
import { EventEmitter } from 'node:events';
|
|
9
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
10
|
+
import { resolve, join } from 'node:path';
|
|
11
|
+
import { execSync } from 'node:child_process';
|
|
12
|
+
import { createSuccessResponse, createErrorResponse, createEvent } from './types.js';
|
|
13
|
+
import { HIVE_HOME } from '../../config.js';
|
|
14
|
+
import { LogBuffer } from './log-buffer.js';
|
|
15
|
+
// ============================================
|
|
16
|
+
// AdminWsHandler
|
|
17
|
+
// ============================================
|
|
18
|
+
export class AdminWsHandler extends EventEmitter {
|
|
19
|
+
clients = new Set();
|
|
20
|
+
logBuffer;
|
|
21
|
+
configCache = null;
|
|
22
|
+
configPath;
|
|
23
|
+
server = null;
|
|
24
|
+
httpServer = null;
|
|
25
|
+
startTime;
|
|
26
|
+
handlers;
|
|
27
|
+
constructor() {
|
|
28
|
+
super();
|
|
29
|
+
this.logBuffer = new LogBuffer(10_000);
|
|
30
|
+
this.configPath = join(HIVE_HOME, 'hive.config.json');
|
|
31
|
+
this.startTime = Date.now();
|
|
32
|
+
// 注册方法处理器
|
|
33
|
+
this.handlers = new Map([
|
|
34
|
+
['config.get', this.handleConfigGet.bind(this)],
|
|
35
|
+
['config.update', this.handleConfigUpdate.bind(this)],
|
|
36
|
+
['config.getProviderPresets', this.handleGetProviderPresets.bind(this)],
|
|
37
|
+
['status.get', this.handleStatusGet.bind(this)],
|
|
38
|
+
['server.restart', this.handleServerRestart.bind(this)],
|
|
39
|
+
['server.getProviders', this.handleGetProviders.bind(this)],
|
|
40
|
+
['provider.list', this.handleProviderList.bind(this)],
|
|
41
|
+
['provider.getModels', this.handleProviderGetModels.bind(this)],
|
|
42
|
+
['plugin.list', this.handlePluginList.bind(this)],
|
|
43
|
+
['plugin.install', this.handlePluginInstall.bind(this)],
|
|
44
|
+
['plugin.uninstall', this.handlePluginUninstall.bind(this)],
|
|
45
|
+
['plugin.updateConfig', this.handlePluginUpdateConfig.bind(this)],
|
|
46
|
+
['log.getHistory', this.handleLogGetHistory.bind(this)],
|
|
47
|
+
['log.subscribe', this.handleLogSubscribe.bind(this)],
|
|
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();
|
|
55
|
+
}
|
|
56
|
+
// ============================================
|
|
57
|
+
// 生命周期
|
|
58
|
+
// ============================================
|
|
59
|
+
/** 注入 Server 实例(在 start 后调用) */
|
|
60
|
+
setServer(server) {
|
|
61
|
+
this.server = server;
|
|
62
|
+
}
|
|
63
|
+
/** 注入 HttpServer 实例 */
|
|
64
|
+
setHttpServer(httpServer) {
|
|
65
|
+
this.httpServer = httpServer;
|
|
66
|
+
}
|
|
67
|
+
/** 处理新的 WS 连接 */
|
|
68
|
+
handleConnection(ws) {
|
|
69
|
+
const client = { ws, logSubscribed: false };
|
|
70
|
+
this.clients.add(client);
|
|
71
|
+
ws.on('message', (raw) => {
|
|
72
|
+
const data = raw.toString();
|
|
73
|
+
const msg = this.parseMessage(data);
|
|
74
|
+
if (!msg)
|
|
75
|
+
return;
|
|
76
|
+
if (msg.type === 'req') {
|
|
77
|
+
this.handleRequest(msg).then(response => {
|
|
78
|
+
ws.send(JSON.stringify(response));
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
ws.on('close', () => {
|
|
83
|
+
this.clients.delete(client);
|
|
84
|
+
});
|
|
85
|
+
ws.on('error', () => {
|
|
86
|
+
this.clients.delete(client);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/** 关闭所有连接(graceful shutdown 时调用) */
|
|
90
|
+
closeAll() {
|
|
91
|
+
this.broadcastEvent('server.shutting_down', { reason: 'shutdown' });
|
|
92
|
+
for (const client of this.clients) {
|
|
93
|
+
client.ws.close();
|
|
94
|
+
}
|
|
95
|
+
this.clients.clear();
|
|
96
|
+
}
|
|
97
|
+
// ============================================
|
|
98
|
+
// 消息处理
|
|
99
|
+
// ============================================
|
|
100
|
+
parseMessage(data) {
|
|
101
|
+
try {
|
|
102
|
+
const msg = JSON.parse(data);
|
|
103
|
+
if (!msg || typeof msg !== 'object')
|
|
104
|
+
return null;
|
|
105
|
+
if (msg.type !== 'req' || !msg.id || !msg.method)
|
|
106
|
+
return null;
|
|
107
|
+
return msg;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async handleRequest(req) {
|
|
114
|
+
const handler = this.handlers.get(req.method);
|
|
115
|
+
if (!handler) {
|
|
116
|
+
return createErrorResponse(req.id, 'NOT_FOUND', `Unknown method: ${req.method}`);
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
return await handler(req.params, req.id);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return createErrorResponse(req.id, 'INTERNAL', error instanceof Error ? error.message : 'Unknown error');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// ============================================
|
|
126
|
+
// Config Handlers
|
|
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
|
|
163
|
+
// ============================================
|
|
164
|
+
handleStatusGet(_params, id) {
|
|
165
|
+
const providerReady = this.isProviderReady();
|
|
166
|
+
const currentProvider = this.server?.agent.currentProvider;
|
|
167
|
+
const status = {
|
|
168
|
+
server: {
|
|
169
|
+
state: 'running',
|
|
170
|
+
port: this.getPort(),
|
|
171
|
+
uptime: Math.floor((Date.now() - this.startTime) / 1000),
|
|
172
|
+
version: this.getVersion(),
|
|
173
|
+
},
|
|
174
|
+
agent: {
|
|
175
|
+
initialized: !!this.server,
|
|
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
|
+
},
|
|
189
|
+
};
|
|
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
|
+
}
|
|
360
|
+
// ============================================
|
|
361
|
+
// 事件广播
|
|
362
|
+
// ============================================
|
|
363
|
+
broadcastEvent(event, data) {
|
|
364
|
+
const msg = createEvent(event, data);
|
|
365
|
+
const payload = JSON.stringify(msg);
|
|
366
|
+
for (const client of this.clients) {
|
|
367
|
+
if (client.ws.readyState === client.ws.OPEN) {
|
|
368
|
+
client.ws.send(payload);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/** 推送日志到所有已订阅的客户端 */
|
|
373
|
+
broadcastLog(entry) {
|
|
374
|
+
const msg = createEvent('log', entry);
|
|
375
|
+
const payload = JSON.stringify(msg);
|
|
376
|
+
for (const client of this.clients) {
|
|
377
|
+
if (client.logSubscribed && client.ws.readyState === client.ws.OPEN) {
|
|
378
|
+
client.ws.send(payload);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// ============================================
|
|
383
|
+
// Console 拦截
|
|
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
|
+
// 配置读写
|
|
422
|
+
// ============================================
|
|
423
|
+
loadConfig() {
|
|
424
|
+
if (this.configCache)
|
|
425
|
+
return this.configCache;
|
|
426
|
+
const defaults = {
|
|
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');
|
|
561
|
+
}
|
|
562
|
+
const pluginName = pkg.name ?? 'local-plugin';
|
|
563
|
+
const targetDir = resolve(pluginsDir, pluginName);
|
|
564
|
+
execSync(`cp -r "${resolvedSource}" "${targetDir}"`, { stdio: 'pipe' });
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
// ============================================
|
|
568
|
+
// 工厂函数
|
|
569
|
+
// ============================================
|
|
570
|
+
export function createAdminWsHandler() {
|
|
571
|
+
return new AdminWsHandler();
|
|
572
|
+
}
|
|
573
|
+
//# sourceMappingURL=admin-handler.js.map
|