@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.
Files changed (101) hide show
  1. package/dist/bootstrap.d.ts +3 -4
  2. package/dist/bootstrap.d.ts.map +1 -1
  3. package/dist/bootstrap.js +17 -24
  4. package/dist/bootstrap.js.map +1 -1
  5. package/dist/cli/commands/skill/index.d.ts +10 -0
  6. package/dist/cli/commands/skill/index.d.ts.map +1 -0
  7. package/dist/cli/commands/skill/index.js +174 -0
  8. package/dist/cli/commands/skill/index.js.map +1 -0
  9. package/dist/cli/commands/skill/installer.d.ts +75 -0
  10. package/dist/cli/commands/skill/installer.d.ts.map +1 -0
  11. package/dist/cli/commands/skill/installer.js +347 -0
  12. package/dist/cli/commands/skill/installer.js.map +1 -0
  13. package/dist/cli/index.d.ts.map +1 -1
  14. package/dist/cli/index.js +3 -0
  15. package/dist/cli/index.js.map +1 -1
  16. package/dist/gateway/http.d.ts +2 -1
  17. package/dist/gateway/http.d.ts.map +1 -1
  18. package/dist/gateway/http.js +17 -3
  19. package/dist/gateway/http.js.map +1 -1
  20. package/dist/gateway/ws/admin-handler.d.ts +24 -47
  21. package/dist/gateway/ws/admin-handler.d.ts.map +1 -1
  22. package/dist/gateway/ws/admin-handler.js +76 -465
  23. package/dist/gateway/ws/admin-handler.js.map +1 -1
  24. package/dist/gateway/ws/chat-handler.d.ts +42 -0
  25. package/dist/gateway/ws/chat-handler.d.ts.map +1 -0
  26. package/dist/gateway/ws/chat-handler.js +258 -0
  27. package/dist/gateway/ws/chat-handler.js.map +1 -0
  28. package/dist/gateway/ws/config-store.d.ts +16 -0
  29. package/dist/gateway/ws/config-store.d.ts.map +1 -0
  30. package/dist/gateway/ws/config-store.js +68 -0
  31. package/dist/gateway/ws/config-store.js.map +1 -0
  32. package/dist/gateway/ws/data-types.d.ts +3 -0
  33. package/dist/gateway/ws/data-types.d.ts.map +1 -1
  34. package/dist/gateway/ws/handler-context.d.ts +41 -0
  35. package/dist/gateway/ws/handler-context.d.ts.map +1 -0
  36. package/dist/gateway/ws/handler-context.js +8 -0
  37. package/dist/gateway/ws/handler-context.js.map +1 -0
  38. package/dist/gateway/ws/handlers/base.d.ts +14 -0
  39. package/dist/gateway/ws/handlers/base.d.ts.map +1 -0
  40. package/dist/gateway/ws/handlers/base.js +13 -0
  41. package/dist/gateway/ws/handlers/base.js.map +1 -0
  42. package/dist/gateway/ws/handlers/config-handler.d.ts +14 -0
  43. package/dist/gateway/ws/handlers/config-handler.d.ts.map +1 -0
  44. package/dist/gateway/ws/handlers/config-handler.js +53 -0
  45. package/dist/gateway/ws/handlers/config-handler.js.map +1 -0
  46. package/dist/gateway/ws/handlers/index.d.ts +10 -0
  47. package/dist/gateway/ws/handlers/index.d.ts.map +1 -0
  48. package/dist/gateway/ws/handlers/index.js +10 -0
  49. package/dist/gateway/ws/handlers/index.js.map +1 -0
  50. package/dist/gateway/ws/handlers/log-handler.d.ts +20 -0
  51. package/dist/gateway/ws/handlers/log-handler.d.ts.map +1 -0
  52. package/dist/gateway/ws/handlers/log-handler.js +65 -0
  53. package/dist/gateway/ws/handlers/log-handler.js.map +1 -0
  54. package/dist/gateway/ws/handlers/plugin-handler.d.ts +29 -0
  55. package/dist/gateway/ws/handlers/plugin-handler.d.ts.map +1 -0
  56. package/dist/gateway/ws/handlers/plugin-handler.js +197 -0
  57. package/dist/gateway/ws/handlers/plugin-handler.js.map +1 -0
  58. package/dist/gateway/ws/handlers/session-handler.d.ts +14 -0
  59. package/dist/gateway/ws/handlers/session-handler.d.ts.map +1 -0
  60. package/dist/gateway/ws/handlers/session-handler.js +36 -0
  61. package/dist/gateway/ws/handlers/session-handler.js.map +1 -0
  62. package/dist/gateway/ws/handlers/status-handler.d.ts +23 -0
  63. package/dist/gateway/ws/handlers/status-handler.d.ts.map +1 -0
  64. package/dist/gateway/ws/handlers/status-handler.js +136 -0
  65. package/dist/gateway/ws/handlers/status-handler.js.map +1 -0
  66. package/dist/gateway/ws/log-buffer.d.ts +1 -0
  67. package/dist/gateway/ws/log-buffer.d.ts.map +1 -1
  68. package/dist/gateway/ws/log-buffer.js +8 -0
  69. package/dist/gateway/ws/log-buffer.js.map +1 -1
  70. package/dist/gateway/ws/types.d.ts +1 -1
  71. package/dist/gateway/ws/types.d.ts.map +1 -1
  72. package/dist/gateway/ws/types.js.map +1 -1
  73. package/dist/graceful-shutdown.d.ts +13 -0
  74. package/dist/graceful-shutdown.d.ts.map +1 -0
  75. package/dist/graceful-shutdown.js +65 -0
  76. package/dist/graceful-shutdown.js.map +1 -0
  77. package/dist/logging/file-logger.d.ts +43 -0
  78. package/dist/logging/file-logger.d.ts.map +1 -0
  79. package/dist/logging/file-logger.js +134 -0
  80. package/dist/logging/file-logger.js.map +1 -0
  81. package/dist/logging/hive-logger.d.ts +26 -0
  82. package/dist/logging/hive-logger.d.ts.map +1 -0
  83. package/dist/logging/hive-logger.js +251 -0
  84. package/dist/logging/hive-logger.js.map +1 -0
  85. package/dist/main.d.ts +2 -0
  86. package/dist/main.d.ts.map +1 -1
  87. package/dist/main.js +68 -19
  88. package/dist/main.js.map +1 -1
  89. package/dist/plugin-manager/installer.d.ts +1 -1
  90. package/dist/plugin-manager/installer.d.ts.map +1 -1
  91. package/dist/plugin-manager/installer.js +84 -17
  92. package/dist/plugin-manager/installer.js.map +1 -1
  93. package/dist/plugin-manager/searcher.js +1 -1
  94. package/dist/plugin-manager/searcher.js.map +1 -1
  95. package/dist/plugin-manager/types.d.ts +1 -0
  96. package/dist/plugin-manager/types.d.ts.map +1 -1
  97. package/dist/plugins.d.ts +18 -0
  98. package/dist/plugins.d.ts.map +1 -1
  99. package/dist/plugins.js +50 -22
  100. package/dist/plugins.js.map +1 -1
  101. 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
- * - req/res: 配置管理、服务状态、插件管理、日志查询
6
- * - event: 日志流推送、状态变更通知
4
+ * 处理 /ws/admin 端点的管理协议消息。
5
+ * 业务逻辑委托给 Domain Handler,本类仅负责:
6
+ * - WS 连接管理
7
+ * - 消息路由(dispatch)
8
+ * - HandlerContext 构建
9
+ * - 生命周期管理
7
10
  */
8
11
  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';
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
- configCache = null;
22
- configPath;
21
+ hiveLogger = null;
22
+ configStore;
23
23
  server = null;
24
- httpServer = null;
25
24
  startTime;
26
25
  handlers;
27
- constructor() {
26
+ statusHandler;
27
+ pluginHandler;
28
+ constructor(hiveLogger, logBuffer) {
28
29
  super();
29
- this.logBuffer = new LogBuffer(10_000);
30
- this.configPath = join(HIVE_HOME, 'hive.config.json');
30
+ this.logBuffer = logBuffer;
31
+ this.hiveLogger = hiveLogger;
32
+ this.configStore = new ConfigStore();
31
33
  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();
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 = 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
- /** 处理新的 WS 连接 */
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
- /** 关闭所有连接(graceful shutdown 时调用) */
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
- // 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
130
+ // HandlerContext
163
131
  // ============================================
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
- },
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
- // 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
- // 配置读写
168
+ // 辅助
422
169
  // ============================================
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');
170
+ findClientByWs(ws) {
171
+ for (const client of this.clients) {
172
+ if (client.ws === ws)
173
+ return client;
561
174
  }
562
- const pluginName = pkg.name ?? 'local-plugin';
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