@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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +211 -0
  3. package/dist/bootstrap.d.ts +36 -0
  4. package/dist/bootstrap.d.ts.map +1 -0
  5. package/dist/bootstrap.js +86 -0
  6. package/dist/bootstrap.js.map +1 -0
  7. package/dist/cli/index.d.ts +8 -0
  8. package/dist/cli/index.d.ts.map +1 -0
  9. package/dist/cli/index.js +125 -0
  10. package/dist/cli/index.js.map +1 -0
  11. package/dist/config.d.ts +93 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +156 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/gateway/auth.d.ts +16 -0
  16. package/dist/gateway/auth.d.ts.map +1 -0
  17. package/dist/gateway/auth.js +50 -0
  18. package/dist/gateway/auth.js.map +1 -0
  19. package/dist/gateway/http.d.ts +9 -0
  20. package/dist/gateway/http.d.ts.map +1 -0
  21. package/dist/gateway/http.js +178 -0
  22. package/dist/gateway/http.js.map +1 -0
  23. package/dist/gateway/websocket.d.ts +18 -0
  24. package/dist/gateway/websocket.d.ts.map +1 -0
  25. package/dist/gateway/websocket.js +197 -0
  26. package/dist/gateway/websocket.js.map +1 -0
  27. package/dist/gateway/ws/admin-handler.d.ts +68 -0
  28. package/dist/gateway/ws/admin-handler.d.ts.map +1 -0
  29. package/dist/gateway/ws/admin-handler.js +573 -0
  30. package/dist/gateway/ws/admin-handler.js.map +1 -0
  31. package/dist/gateway/ws/data-types.d.ts +130 -0
  32. package/dist/gateway/ws/data-types.d.ts.map +1 -0
  33. package/dist/gateway/ws/data-types.js +7 -0
  34. package/dist/gateway/ws/data-types.js.map +1 -0
  35. package/dist/gateway/ws/log-buffer.d.ts +29 -0
  36. package/dist/gateway/ws/log-buffer.d.ts.map +1 -0
  37. package/dist/gateway/ws/log-buffer.js +58 -0
  38. package/dist/gateway/ws/log-buffer.js.map +1 -0
  39. package/dist/gateway/ws/types.d.ts +67 -0
  40. package/dist/gateway/ws/types.d.ts.map +1 -0
  41. package/dist/gateway/ws/types.js +68 -0
  42. package/dist/gateway/ws/types.js.map +1 -0
  43. package/dist/heartbeat-scheduler.d.ts +43 -0
  44. package/dist/heartbeat-scheduler.d.ts.map +1 -0
  45. package/dist/heartbeat-scheduler.js +118 -0
  46. package/dist/heartbeat-scheduler.js.map +1 -0
  47. package/dist/main.d.ts +23 -0
  48. package/dist/main.d.ts.map +1 -0
  49. package/dist/main.js +120 -0
  50. package/dist/main.js.map +1 -0
  51. package/dist/plugin-manager/cli.d.ts +6 -0
  52. package/dist/plugin-manager/cli.d.ts.map +1 -0
  53. package/dist/plugin-manager/cli.js +115 -0
  54. package/dist/plugin-manager/cli.js.map +1 -0
  55. package/dist/plugin-manager/constants.d.ts +24 -0
  56. package/dist/plugin-manager/constants.d.ts.map +1 -0
  57. package/dist/plugin-manager/constants.js +58 -0
  58. package/dist/plugin-manager/constants.js.map +1 -0
  59. package/dist/plugin-manager/index.d.ts +11 -0
  60. package/dist/plugin-manager/index.d.ts.map +1 -0
  61. package/dist/plugin-manager/index.js +10 -0
  62. package/dist/plugin-manager/index.js.map +1 -0
  63. package/dist/plugin-manager/installer.d.ts +19 -0
  64. package/dist/plugin-manager/installer.d.ts.map +1 -0
  65. package/dist/plugin-manager/installer.js +256 -0
  66. package/dist/plugin-manager/installer.js.map +1 -0
  67. package/dist/plugin-manager/manager.d.ts +39 -0
  68. package/dist/plugin-manager/manager.d.ts.map +1 -0
  69. package/dist/plugin-manager/manager.js +171 -0
  70. package/dist/plugin-manager/manager.js.map +1 -0
  71. package/dist/plugin-manager/registry.d.ts +31 -0
  72. package/dist/plugin-manager/registry.d.ts.map +1 -0
  73. package/dist/plugin-manager/registry.js +63 -0
  74. package/dist/plugin-manager/registry.js.map +1 -0
  75. package/dist/plugin-manager/searcher.d.ts +18 -0
  76. package/dist/plugin-manager/searcher.d.ts.map +1 -0
  77. package/dist/plugin-manager/searcher.js +50 -0
  78. package/dist/plugin-manager/searcher.js.map +1 -0
  79. package/dist/plugin-manager/types.d.ts +71 -0
  80. package/dist/plugin-manager/types.d.ts.map +1 -0
  81. package/dist/plugin-manager/types.js +5 -0
  82. package/dist/plugin-manager/types.js.map +1 -0
  83. package/dist/plugins.d.ts +18 -0
  84. package/dist/plugins.d.ts.map +1 -0
  85. package/dist/plugins.js +198 -0
  86. package/dist/plugins.js.map +1 -0
  87. 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