@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
package/dist/config.js ADDED
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Hive Server Configuration
3
+ *
4
+ * Loads configuration from hive.config.json with environment variable interpolation.
5
+ * Falls back to .env for backwards compatibility.
6
+ */
7
+ import { config as dotenvConfig } from 'dotenv';
8
+ import { existsSync, readFileSync, mkdirSync } from 'fs';
9
+ import { resolve, join } from 'path';
10
+ import AjvModule from 'ajv';
11
+ const Ajv = AjvModule.default;
12
+ /**
13
+ * Hive 工作空间根目录
14
+ *
15
+ * 优先级: HIVE_HOME 环境变量 > ~/.hive
16
+ */
17
+ function getHiveHome() {
18
+ if (process.env.HIVE_HOME) {
19
+ return process.env.HIVE_HOME;
20
+ }
21
+ return resolve(process.env.HOME || '~', '.hive');
22
+ }
23
+ export const HIVE_HOME = getHiveHome();
24
+ // 确保工作空间目录存在
25
+ if (!existsSync(HIVE_HOME)) {
26
+ mkdirSync(HIVE_HOME, { recursive: true });
27
+ }
28
+ // Load .env file if exists (for env var interpolation)
29
+ if (existsSync(join(HIVE_HOME, '.env'))) {
30
+ dotenvConfig({ path: join(HIVE_HOME, '.env') });
31
+ }
32
+ /**
33
+ * Interpolate ${ENV_VAR} placeholders in a string
34
+ * E.g., "${GLM_API_KEY}" → "actual-api-key"
35
+ */
36
+ function interpolateEnvVars(value) {
37
+ return value.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
38
+ return process.env[envVar] || '';
39
+ });
40
+ }
41
+ /**
42
+ * Recursively interpolate ${ENV_VAR} in configuration object
43
+ */
44
+ function interpolateConfig(obj) {
45
+ if (typeof obj === 'string') {
46
+ return interpolateEnvVars(obj);
47
+ }
48
+ if (Array.isArray(obj)) {
49
+ return obj.map(interpolateConfig);
50
+ }
51
+ if (obj && typeof obj === 'object') {
52
+ const result = {};
53
+ for (const [key, value] of Object.entries(obj)) {
54
+ result[key] = interpolateConfig(value);
55
+ }
56
+ return result;
57
+ }
58
+ return obj;
59
+ }
60
+ /**
61
+ * Load and parse hive.config.json
62
+ */
63
+ function loadJsonConfig() {
64
+ const configPath = join(HIVE_HOME, 'hive.config.json');
65
+ if (!existsSync(configPath)) {
66
+ return {};
67
+ }
68
+ try {
69
+ const content = readFileSync(configPath, 'utf-8');
70
+ const raw = JSON.parse(content);
71
+ const interpolated = interpolateConfig(raw);
72
+ // Validate against JSON Schema
73
+ const schemaPath = join(HIVE_HOME, 'hive.config.schema.json');
74
+ if (existsSync(schemaPath)) {
75
+ const schemaContent = readFileSync(schemaPath, 'utf-8');
76
+ const schema = JSON.parse(schemaContent);
77
+ const ajv = new Ajv({ useDefaults: true });
78
+ const validate = ajv.compile(schema);
79
+ if (!validate(interpolated)) {
80
+ console.error('[config] Validation errors:');
81
+ for (const error of validate.errors || []) {
82
+ console.error(` - ${error.instancePath}: ${error.message}`);
83
+ }
84
+ throw new Error('Configuration validation failed');
85
+ }
86
+ }
87
+ return interpolated;
88
+ }
89
+ catch (error) {
90
+ console.error('[config] Failed to load hive.config.json:', error);
91
+ throw error;
92
+ }
93
+ }
94
+ /**
95
+ * Load configuration from hive.config.json with fallbacks
96
+ */
97
+ export function loadConfig() {
98
+ const jsonConfig = loadJsonConfig();
99
+ // Server config with defaults
100
+ const server = jsonConfig.server || {};
101
+ // Auth config
102
+ const auth = jsonConfig.auth || {};
103
+ // Provider config
104
+ const provider = jsonConfig.provider || { id: 'glm' };
105
+ // Heartbeat config
106
+ const heartbeat = jsonConfig.heartbeat || {};
107
+ // Plugin list (keys of plugins object)
108
+ const plugins = jsonConfig.plugins ? Object.keys(jsonConfig.plugins) : [];
109
+ return {
110
+ port: server.port ?? parseInt(process.env.PORT || '4450', 10),
111
+ host: server.host ?? process.env.HOST ?? '127.0.0.1',
112
+ logLevel: server.logLevel ?? process.env.LOG_LEVEL ?? 'info',
113
+ auth: {
114
+ enabled: auth.enabled ?? process.env.AUTH_ENABLED === 'true',
115
+ apiKey: auth.apiKey || process.env.AUTH_API_KEY || '',
116
+ },
117
+ plugins,
118
+ provider: {
119
+ id: provider.id || process.env.PROVIDER_ID || 'glm',
120
+ apiKey: provider.apiKey || process.env.API_KEY || process.env.GLM_API_KEY || '',
121
+ model: provider.model || process.env.MODEL,
122
+ baseUrl: provider.baseUrl || process.env.BASE_URL,
123
+ },
124
+ heartbeat: {
125
+ enabled: heartbeat.enabled ?? process.env.HEARTBEAT_ENABLED === 'true',
126
+ intervalMs: heartbeat.intervalMs ?? parseInt(process.env.HEARTBEAT_INTERVAL_MS || '300000', 10),
127
+ model: heartbeat.model || process.env.HEARTBEAT_MODEL,
128
+ prompt: heartbeat.prompt || process.env.HEARTBEAT_PROMPT,
129
+ },
130
+ pluginConfigs: jsonConfig.plugins || {},
131
+ };
132
+ }
133
+ /**
134
+ * Get configuration for a specific plugin
135
+ */
136
+ export function getPluginConfig(config, pluginName) {
137
+ return config.pluginConfigs[pluginName] || {};
138
+ }
139
+ /** Cached config instance */
140
+ let _config = null;
141
+ /**
142
+ * Get configuration (lazy-loaded singleton)
143
+ */
144
+ export function getConfig() {
145
+ if (!_config) {
146
+ _config = loadConfig();
147
+ }
148
+ return _config;
149
+ }
150
+ /**
151
+ * Reset cached config (for testing)
152
+ */
153
+ export function resetConfig() {
154
+ _config = null;
155
+ }
156
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * API Key Authentication Middleware (Hono)
3
+ *
4
+ * Validates requests via Authorization: Bearer <key> header or ?apiKey=<key> query param.
5
+ */
6
+ import type { MiddlewareHandler } from 'hono';
7
+ import type { ServerConfig } from '../config.js';
8
+ /**
9
+ * Create authentication middleware for Hono
10
+ */
11
+ export declare function createAuthMiddleware(config: ServerConfig): MiddlewareHandler;
12
+ /**
13
+ * Validate API key for WebSocket upgrade (query param only)
14
+ */
15
+ export declare function validateWsApiKey(config: ServerConfig, apiKey: string | null | undefined): boolean;
16
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/gateway/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,iBAAiB,CAwB5E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAQjG"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * API Key Authentication Middleware (Hono)
3
+ *
4
+ * Validates requests via Authorization: Bearer <key> header or ?apiKey=<key> query param.
5
+ */
6
+ /**
7
+ * Create authentication middleware for Hono
8
+ */
9
+ export function createAuthMiddleware(config) {
10
+ const { enabled, apiKey } = config.auth;
11
+ return async (c, next) => {
12
+ if (!enabled || !apiKey) {
13
+ await next();
14
+ return;
15
+ }
16
+ const authHeader = c.req.header('Authorization');
17
+ const headerKey = extractBearerToken(authHeader);
18
+ const queryKey = c.req.query('apiKey');
19
+ const providedKey = headerKey || queryKey;
20
+ if (!providedKey) {
21
+ return c.json({ error: 'Missing API key' }, 401);
22
+ }
23
+ if (providedKey !== apiKey) {
24
+ return c.json({ error: 'Invalid API key' }, 401);
25
+ }
26
+ await next();
27
+ };
28
+ }
29
+ /**
30
+ * Validate API key for WebSocket upgrade (query param only)
31
+ */
32
+ export function validateWsApiKey(config, apiKey) {
33
+ if (!config.auth.enabled || !config.auth.apiKey) {
34
+ return true;
35
+ }
36
+ if (!apiKey) {
37
+ return false;
38
+ }
39
+ return apiKey === config.auth.apiKey;
40
+ }
41
+ function extractBearerToken(authHeader) {
42
+ if (!authHeader)
43
+ return undefined;
44
+ const parts = authHeader.split(' ');
45
+ if (parts.length === 2 && parts[0] === 'Bearer') {
46
+ return parts[1];
47
+ }
48
+ return undefined;
49
+ }
50
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/gateway/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAoB;IACvD,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAA;IAEvC,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;YACxB,MAAM,IAAI,EAAE,CAAA;YACZ,OAAM;QACR,CAAC;QAED,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAA;QAChD,MAAM,SAAS,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAA;QAChD,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;QACtC,MAAM,WAAW,GAAG,SAAS,IAAI,QAAQ,CAAA;QAEzC,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,GAAG,CAAC,CAAA;QAClD,CAAC;QAED,IAAI,WAAW,KAAK,MAAM,EAAE,CAAC;YAC3B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,GAAG,CAAC,CAAA;QAClD,CAAC;QAED,MAAM,IAAI,EAAE,CAAA;IACd,CAAC,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAoB,EAAE,MAAiC;IACtF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAChD,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,MAAM,CAAA;AACtC,CAAC;AAED,SAAS,kBAAkB,CAAC,UAA8B;IACxD,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAA;IACjC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACnC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * HTTP Gateway Factory
3
+ *
4
+ * Creates Hono app for HTTP API.
5
+ */
6
+ import { Hono } from 'hono';
7
+ import type { HiveContext } from '../bootstrap.js';
8
+ export declare function createHttpGateway(ctx: HiveContext): Hono;
9
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/gateway/http.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAG3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAqBlD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAgMxD"}
@@ -0,0 +1,178 @@
1
+ /**
2
+ * HTTP Gateway Factory
3
+ *
4
+ * Creates Hono app for HTTP API.
5
+ */
6
+ import { Hono } from 'hono';
7
+ import { cors } from 'hono/cors';
8
+ import { logger } from 'hono/logger';
9
+ import { createAuthMiddleware } from './auth.js';
10
+ /** Maximum message length (100KB) */
11
+ const MAX_MESSAGE_LENGTH = 100_000;
12
+ /** Maximum in-memory sessions (LRU eviction) */
13
+ const MAX_SESSIONS = 10_000;
14
+ // Simple in-memory session store with LRU eviction
15
+ const sessions = new Map();
16
+ function setSession(key, value) {
17
+ if (sessions.size >= MAX_SESSIONS) {
18
+ const firstKey = sessions.keys().next().value;
19
+ if (firstKey !== undefined)
20
+ sessions.delete(firstKey);
21
+ }
22
+ sessions.set(key, value);
23
+ }
24
+ export function createHttpGateway(ctx) {
25
+ const app = new Hono();
26
+ // Middleware
27
+ app.use('*', logger());
28
+ const allowedOrigins = process.env.CORS_ORIGINS
29
+ ? process.env.CORS_ORIGINS.split(',').map(s => s.trim())
30
+ : ['http://localhost:3000'];
31
+ app.use('*', cors({
32
+ origin: allowedOrigins,
33
+ allowMethods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
34
+ allowHeaders: ['Content-Type', 'Authorization'],
35
+ }));
36
+ // API authentication
37
+ app.use('/api/*', createAuthMiddleware(ctx.config));
38
+ // Health check (no auth required)
39
+ app.get('/health', (c) => {
40
+ return c.json({ status: 'ok', timestamp: new Date().toISOString() });
41
+ });
42
+ // Chat endpoint
43
+ app.post('/api/chat', async (c) => {
44
+ try {
45
+ const body = await c.req.json();
46
+ const { message, sessionId } = body;
47
+ if (!message) {
48
+ return c.json({ error: 'Missing message' }, 400);
49
+ }
50
+ if (typeof message !== 'string' || message.length > MAX_MESSAGE_LENGTH) {
51
+ return c.json({ error: `Message too long (max ${MAX_MESSAGE_LENGTH} chars)` }, 413);
52
+ }
53
+ const sid = sessionId || 'default';
54
+ // Get or create session
55
+ let session = sessions.get(sid);
56
+ if (!session) {
57
+ session = { id: sid, messages: [] };
58
+ setSession(sid, session);
59
+ }
60
+ // Add user message
61
+ session.messages.push({ role: 'user', content: message });
62
+ // Emit message received event
63
+ ctx.bus?.emit('message:received', {
64
+ content: message,
65
+ sessionId: sid,
66
+ timestamp: Date.now(),
67
+ });
68
+ // Send to agent (dispatch for smart routing)
69
+ const result = await ctx.agent.dispatch(message);
70
+ const response = result.text;
71
+ // Add assistant message
72
+ session.messages.push({ role: 'assistant', content: response });
73
+ // Emit message sent event
74
+ ctx.bus?.emit('message:sent', {
75
+ content: response,
76
+ sessionId: sid,
77
+ timestamp: Date.now(),
78
+ });
79
+ return c.json({
80
+ response,
81
+ sessionId: sid,
82
+ });
83
+ }
84
+ catch (error) {
85
+ const isDev = process.env.NODE_ENV !== 'production';
86
+ return c.json({
87
+ error: 'Internal server error',
88
+ ...(isDev ? { message: error instanceof Error ? error.message : 'Unknown error' } : {}),
89
+ }, 500);
90
+ }
91
+ });
92
+ // List plugins
93
+ app.get('/api/plugins', (c) => {
94
+ return c.json({
95
+ plugins: ctx.plugins.map((p) => ({
96
+ id: p.metadata.id,
97
+ name: p.metadata.name,
98
+ version: p.metadata.version,
99
+ })),
100
+ });
101
+ });
102
+ // List sessions
103
+ app.get('/api/sessions', (c) => {
104
+ const sessionList = Array.from(sessions.values()).map((s) => ({
105
+ id: s.id,
106
+ messageCount: s.messages.length,
107
+ }));
108
+ return c.json({ sessions: sessionList });
109
+ });
110
+ // Get session by ID
111
+ app.get('/api/sessions/:id', (c) => {
112
+ const sessionId = c.req.param('id');
113
+ const session = sessions.get(sessionId);
114
+ if (!session) {
115
+ return c.json({ error: 'Session not found' }, 404);
116
+ }
117
+ return c.json({ session });
118
+ });
119
+ // Delete session
120
+ app.delete('/api/sessions/:id', (c) => {
121
+ const sessionId = c.req.param('id');
122
+ const deleted = sessions.delete(sessionId);
123
+ return c.json({ success: deleted });
124
+ });
125
+ // Webhook endpoint for plugins (e.g., Feishu)
126
+ app.post('/webhook/:plugin/:appId', async (c) => {
127
+ try {
128
+ const pluginName = c.req.param('plugin');
129
+ const appId = c.req.param('appId');
130
+ // Get headers
131
+ const signature = c.req.header('X-Lark-Signature') || c.req.header('X-Feishu-Signature') || '';
132
+ const timestamp = c.req.header('X-Lark-Request-Timestamp') || c.req.header('X-Feishu-Timestamp') || '';
133
+ const nonce = c.req.header('X-Lark-Request-Nonce') || c.req.header('X-Feishu-Nonce') || '';
134
+ // Get body
135
+ const body = await c.req.json();
136
+ // Find the plugin and get its channel
137
+ const plugin = ctx.plugins.find((p) => p.metadata.id === pluginName);
138
+ if (!plugin) {
139
+ return c.json({ error: 'Plugin not found' }, 404);
140
+ }
141
+ // Get channel from plugin
142
+ const channels = plugin.getChannels();
143
+ const channel = channels.find((ch) => {
144
+ // Check channel id format: "feishu:appId"
145
+ if (ch.id === `${pluginName}:${appId}`)
146
+ return true;
147
+ // Check if channel has appId property (for Feishu channels)
148
+ if ('appId' in ch && ch.appId === appId)
149
+ return true;
150
+ return false;
151
+ });
152
+ const webhookChannel = channel;
153
+ if (!channel || typeof webhookChannel.handleWebhook !== 'function') {
154
+ return c.json({ error: 'Channel not found or does not support webhooks' }, 404);
155
+ }
156
+ // Handle webhook
157
+ const result = await webhookChannel.handleWebhook(body, signature, timestamp, nonce);
158
+ return c.json(result);
159
+ }
160
+ catch (error) {
161
+ const isDev = process.env.NODE_ENV !== 'production';
162
+ return c.json({
163
+ error: 'Webhook processing failed',
164
+ ...(isDev ? { message: error instanceof Error ? error.message : 'Unknown error' } : {}),
165
+ }, 500);
166
+ }
167
+ });
168
+ // Error handling
169
+ app.notFound((c) => {
170
+ return c.json({ error: 'Not found' }, 404);
171
+ });
172
+ app.onError((err, c) => {
173
+ const isDev = process.env.NODE_ENV !== 'production';
174
+ return c.json({ error: 'Internal server error', ...(isDev ? { message: err.message } : {}) }, 500);
175
+ });
176
+ return app;
177
+ }
178
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/gateway/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * WebSocket Gateway
3
+ *
4
+ * Real-time bidirectional communication for chat and events.
5
+ */
6
+ import type { Server as HttpServer } from 'http';
7
+ import type { HiveContext } from '../bootstrap.js';
8
+ interface OutgoingMessage {
9
+ type: string;
10
+ [key: string]: unknown;
11
+ }
12
+ export interface WebSocketGateway {
13
+ broadcast: (message: OutgoingMessage) => void;
14
+ close: () => void;
15
+ }
16
+ export declare function createWebSocketGateway(server: HttpServer, ctx: HiveContext): WebSocketGateway;
17
+ export {};
18
+ //# sourceMappingURL=websocket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../src/gateway/websocket.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAA;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAiBlD,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAA;IAC7C,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,WAAW,GACf,gBAAgB,CA6NlB"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * WebSocket Gateway
3
+ *
4
+ * Real-time bidirectional communication for chat and events.
5
+ */
6
+ import { validateWsApiKey } from './auth.js';
7
+ /** Maximum message length (100KB) — shared with HTTP gateway */
8
+ const MAX_MESSAGE_LENGTH = 100_000;
9
+ export function createWebSocketGateway(server, ctx) {
10
+ const clients = new Map();
11
+ let clientIdCounter = 0;
12
+ let busSubscriptionId = null;
13
+ // Handle WebSocket upgrade
14
+ server.on('upgrade', (request, socket, head) => {
15
+ const url = request.url;
16
+ if (!url)
17
+ return;
18
+ try {
19
+ const parsedUrl = new URL(url, `http://${request.headers.host || 'localhost'}`);
20
+ const pathname = parsedUrl.pathname;
21
+ if (pathname !== '/ws')
22
+ return;
23
+ // Validate API key from query param
24
+ const apiKey = parsedUrl.searchParams.get('apiKey');
25
+ if (!validateWsApiKey(ctx.config, apiKey)) {
26
+ socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
27
+ socket.end();
28
+ return;
29
+ }
30
+ }
31
+ catch {
32
+ // Invalid URL, ignore
33
+ }
34
+ });
35
+ /**
36
+ * Handle incoming WebSocket message
37
+ */
38
+ async function handleMessage(client, data, ctx) {
39
+ try {
40
+ const message = JSON.parse(data);
41
+ switch (message.type) {
42
+ case 'chat': {
43
+ await handleChat(client, message, ctx);
44
+ break;
45
+ }
46
+ case 'session:create': {
47
+ handleSessionCreate(client);
48
+ break;
49
+ }
50
+ case 'session:join': {
51
+ handleSessionJoin(client, message);
52
+ break;
53
+ }
54
+ default:
55
+ send(client, {
56
+ type: 'error',
57
+ message: `Unknown message type: ${message.type}`,
58
+ });
59
+ }
60
+ }
61
+ catch (error) {
62
+ send(client, {
63
+ type: 'error',
64
+ message: 'Invalid message format',
65
+ });
66
+ }
67
+ }
68
+ /**
69
+ * Handle chat message
70
+ */
71
+ async function handleChat(client, message, ctx) {
72
+ const text = message.message;
73
+ const sessionId = message.sessionId;
74
+ if (!text) {
75
+ send(client, {
76
+ type: 'error',
77
+ message: 'Missing message content',
78
+ });
79
+ return;
80
+ }
81
+ if (typeof text !== 'string' || text.length > MAX_MESSAGE_LENGTH) {
82
+ send(client, {
83
+ type: 'error',
84
+ message: `Message too long (max ${MAX_MESSAGE_LENGTH} chars)`,
85
+ });
86
+ return;
87
+ }
88
+ try {
89
+ // Emit message received event
90
+ ctx.bus?.emit('message:received', {
91
+ content: text,
92
+ sessionId: sessionId || client.sessionId,
93
+ clientId: client.id,
94
+ timestamp: Date.now(),
95
+ });
96
+ // Send to agent (dispatch for smart routing)
97
+ const result = await ctx.agent.dispatch(text);
98
+ const response = result.text;
99
+ // Emit message sent event
100
+ ctx.bus?.emit('message:sent', {
101
+ content: response,
102
+ sessionId: sessionId || client.sessionId,
103
+ clientId: client.id,
104
+ timestamp: Date.now(),
105
+ });
106
+ send(client, {
107
+ type: 'response',
108
+ message: response,
109
+ sessionId: sessionId || client.sessionId,
110
+ });
111
+ }
112
+ catch (error) {
113
+ const isDev = process.env.NODE_ENV !== 'production';
114
+ send(client, {
115
+ type: 'error',
116
+ message: isDev && error instanceof Error ? error.message : 'Internal error',
117
+ });
118
+ }
119
+ }
120
+ /**
121
+ * Handle session create
122
+ */
123
+ function handleSessionCreate(client) {
124
+ const sessionId = `session-${Date.now()}-${Math.random().toString(36).slice(2)}`;
125
+ client.sessionId = sessionId;
126
+ send(client, {
127
+ type: 'session:created',
128
+ sessionId,
129
+ });
130
+ }
131
+ /**
132
+ * Handle session join
133
+ */
134
+ function handleSessionJoin(client, message) {
135
+ const sessionId = message.sessionId;
136
+ if (!sessionId) {
137
+ send(client, {
138
+ type: 'error',
139
+ message: 'Missing sessionId',
140
+ });
141
+ return;
142
+ }
143
+ client.sessionId = sessionId;
144
+ send(client, {
145
+ type: 'session:joined',
146
+ sessionId,
147
+ });
148
+ }
149
+ /**
150
+ * Send message to client
151
+ */
152
+ function send(client, message) {
153
+ if (client.ws.readyState === 1) { // WebSocket.OPEN
154
+ client.ws.send(JSON.stringify(message));
155
+ }
156
+ }
157
+ /**
158
+ * Broadcast message to all clients
159
+ */
160
+ function broadcast(message) {
161
+ const data = JSON.stringify(message);
162
+ for (const client of clients.values()) {
163
+ if (client.ws.readyState === 1) { // WebSocket.OPEN
164
+ client.ws.send(data);
165
+ }
166
+ }
167
+ }
168
+ /**
169
+ * Close all connections
170
+ */
171
+ function close() {
172
+ // Unsubscribe from MessageBus
173
+ if (busSubscriptionId && ctx.bus) {
174
+ ctx.bus.unsubscribe(busSubscriptionId);
175
+ }
176
+ // Close all client connections
177
+ for (const client of clients.values()) {
178
+ client.ws.close();
179
+ }
180
+ clients.clear();
181
+ }
182
+ // Subscribe to MessageBus events and broadcast
183
+ if (ctx.bus) {
184
+ busSubscriptionId = ctx.bus.subscribe('plugin:event', (event) => {
185
+ broadcast({
186
+ type: 'event',
187
+ event: 'plugin:event',
188
+ data: event,
189
+ });
190
+ });
191
+ }
192
+ return {
193
+ broadcast,
194
+ close,
195
+ };
196
+ }
197
+ //# sourceMappingURL=websocket.js.map