hermes-web-ui 0.3.8 → 0.4.0

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 (75) hide show
  1. package/README.md +7 -10
  2. package/dist/client/assets/{Add-s316k4Av.js → Add-BChxDDdy.js} +1 -1
  3. package/dist/client/assets/{Button-BkA_RI8a.js → Button-uvjCWO-i.js} +1 -1
  4. package/dist/client/assets/{ChannelsView-DPmPn8DW.js → ChannelsView-D3a0g0rb.js} +1 -1
  5. package/dist/client/assets/{ChatView-DEtziOPB.js → ChatView-T8LH7dwU.js} +1 -1
  6. package/dist/client/assets/{Close-CrLD0IXG.js → Close-DbXijZpL.js} +1 -1
  7. package/dist/client/assets/{FormItem-CQLdFrl9.js → FormItem-BRiLD3TC.js} +1 -1
  8. package/dist/client/assets/{GatewaysView-CC1Y0tZZ.js → GatewaysView-rfzh1nqy.js} +1 -1
  9. package/dist/client/assets/{Input-nXKlujwJ.js → Input-CjlUbV0M.js} +1 -1
  10. package/dist/client/assets/{InputNumber-DLZwwIyX.js → InputNumber-CPEjoOAv.js} +1 -1
  11. package/dist/client/assets/{JobsView-BC0bBrJO.js → JobsView-D6AFr9Xe.js} +2 -2
  12. package/dist/client/assets/{LoginView-PqpFR9bV.js → LoginView-DJB_TSHN.js} +1 -1
  13. package/dist/client/assets/{LogsView-DtR88N0b.js → LogsView-Ul8pAp42.js} +1 -1
  14. package/dist/client/assets/{MarkdownRenderer-BSLfTurm.js → MarkdownRenderer-CY7d2L7Z.js} +1 -1
  15. package/dist/client/assets/{MemoryView-CJRWnePL.js → MemoryView-BR5Dl_fa.js} +1 -1
  16. package/dist/client/assets/{Modal-Bp9RK8LZ.js → Modal-DDUFP8vh.js} +1 -1
  17. package/dist/client/assets/{ModelsView-7Obe34Cz.js → ModelsView-DAi9Jdmk.js} +1 -1
  18. package/dist/client/assets/{Popconfirm-DTdUi7r_.js → Popconfirm-Cnb_uQVo.js} +1 -1
  19. package/dist/client/assets/{Popover-_M3o0B7L.js → Popover-DiVI0l6E.js} +1 -1
  20. package/dist/client/assets/{ProfilesView-1_GmRx-S.js → ProfilesView-BB0Zrfhi.js} +1 -1
  21. package/dist/client/assets/{Select-aHPR3urY.js → Select-BqUA1wxE.js} +1 -1
  22. package/dist/client/assets/{SettingRow-DKasLuS5.js → SettingRow-CK0bHtaz.js} +1 -1
  23. package/dist/client/assets/{SettingsView-DZCA7_CM.js → SettingsView-BKKk44FG.js} +1 -1
  24. package/dist/client/assets/{SkillsView-Dk7O05cK.js → SkillsView-whSlyc23.js} +1 -1
  25. package/dist/client/assets/{Spin-Bt_9cTiO.js → Spin-DwHJdgNz.js} +1 -1
  26. package/dist/client/assets/{Suffix-XaH8SDbR.js → Suffix-D6x-7akR.js} +1 -1
  27. package/dist/client/assets/{Switch-D1_psmjT.js → Switch-BvHRSSqt.js} +1 -1
  28. package/dist/client/assets/{Tag-3FaOhoJN.js → Tag-BMMlXaEi.js} +1 -1
  29. package/dist/client/assets/{TerminalView-DNU7oQxK.js → TerminalView-el6o2Q0a.js} +1 -1
  30. package/dist/client/assets/{Tooltip-YHrHWGPa.js → Tooltip-BM4wl764.js} +1 -1
  31. package/dist/client/assets/{UsageView-COCrOiiV.js → UsageView-DQ_YKoEV.js} +1 -1
  32. package/dist/client/assets/{Warning-B6CM9aBl.js → Warning-CEC7rgvY.js} +1 -1
  33. package/dist/client/assets/{_plugin-vue_export-helper-BGG8ORDx.js → _plugin-vue_export-helper-DgUZPfuZ.js} +1 -1
  34. package/dist/client/assets/app-DPUhLGXq.js +1 -0
  35. package/dist/client/assets/{app-B7ktf7Fh.js → app-DUt8TNq3.js} +1 -1
  36. package/dist/client/assets/{browser-f5W8abIG.js → browser-vxCOMmsq.js} +1 -1
  37. package/dist/client/assets/{chat-6q6pkzEW.js → chat-i_Ge_Lfr.js} +2 -2
  38. package/dist/client/assets/composables-jrQPIjcq.js +1 -0
  39. package/dist/client/assets/{fade-in.cssr-ifHK7yH1.js → fade-in.cssr-DVg2CkO3.js} +1 -1
  40. package/dist/client/assets/{index-CSCYx7ux.js → index-COwJ2oY0.js} +1 -1
  41. package/dist/client/assets/{jobs-DObWfhbO.js → jobs-Czr1RcSG.js} +1 -1
  42. package/dist/client/assets/{light-DgLcPjgU.js → light-3rSjNeC-.js} +1 -1
  43. package/dist/client/assets/{light-CxjyoF0s.js → light-CKDlpgGU.js} +1 -1
  44. package/dist/client/assets/{light-D1yfed_s.js → light-CiIDFs7y.js} +1 -1
  45. package/dist/client/assets/{light-TGFKT-UB.js → light-CoJqT8Vu.js} +1 -1
  46. package/dist/client/assets/{light-D_3MwJj1.js → light-DPRJ1OEN.js} +1 -1
  47. package/dist/client/assets/{light-DWy-mwyK.js → light-vTpJevRf.js} +1 -1
  48. package/dist/client/assets/{models-DQ4CT-vv.js → models-BzEeJuoO.js} +1 -1
  49. package/dist/client/assets/{pinia-DcAkZ8vx.js → pinia-BoNLlsLy.js} +1 -1
  50. package/dist/client/assets/{profiles-DzkigJwq.js → profiles-B-DFTmc2.js} +1 -1
  51. package/dist/client/assets/{router-D8sJ39Io.js → router-HHMeDEaP.js} +2 -2
  52. package/dist/client/assets/{sessions-Dg8n9PBo.js → sessions-BmxS_BoH.js} +1 -1
  53. package/dist/client/assets/{skills-BehzdECn.js → skills-Be8Mzr1r.js} +1 -1
  54. package/dist/client/assets/{use-message-DBz2JSTt.js → use-message-DBTY4945.js} +1 -1
  55. package/dist/client/assets/{useTheme-UdVT814n.js → useTheme-D58Cg7k2.js} +1 -1
  56. package/dist/client/index.html +27 -27
  57. package/dist/server/index.js +19 -199
  58. package/dist/server/routes/health.d.ts +4 -0
  59. package/dist/server/routes/health.js +109 -0
  60. package/dist/server/routes/hermes/group-chat.d.ts +2 -0
  61. package/dist/server/routes/hermes/group-chat.js +112 -101
  62. package/dist/server/routes/update.d.ts +2 -0
  63. package/dist/server/routes/update.js +69 -0
  64. package/dist/server/services/auth.js +1 -1
  65. package/dist/server/services/gateway-bootstrap.d.ts +2 -0
  66. package/dist/server/services/gateway-bootstrap.js +51 -0
  67. package/dist/server/services/hermes/group-chat/agent-clients.d.ts +133 -0
  68. package/dist/server/services/hermes/group-chat/agent-clients.js +364 -0
  69. package/dist/server/services/hermes/group-chat/index.d.ts +72 -0
  70. package/dist/server/services/hermes/group-chat/index.js +307 -0
  71. package/dist/server/services/shutdown.d.ts +1 -0
  72. package/dist/server/services/shutdown.js +37 -0
  73. package/package.json +1 -1
  74. package/dist/client/assets/app-BPvTl2-V.js +0 -1
  75. package/dist/client/assets/composables-xV7dhNpf.js +0 -1
@@ -0,0 +1,307 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GroupChatServer = void 0;
4
+ const node_sqlite_1 = require("node:sqlite");
5
+ const socket_io_1 = require("socket.io");
6
+ const path_1 = require("path");
7
+ const fs_1 = require("fs");
8
+ const auth_1 = require("../../auth");
9
+ const config_1 = require("../../../config");
10
+ const agent_clients_1 = require("./agent-clients");
11
+ // ─── SQLite Storage ───────────────────────────────────────────
12
+ class ChatStorage {
13
+ db;
14
+ constructor(dbPath) {
15
+ this.db = new node_sqlite_1.DatabaseSync(dbPath);
16
+ this.db.exec(`
17
+ CREATE TABLE IF NOT EXISTS rooms (
18
+ id TEXT PRIMARY KEY,
19
+ name TEXT NOT NULL,
20
+ inviteCode TEXT UNIQUE
21
+ )
22
+ `);
23
+ this.db.exec(`
24
+ CREATE TABLE IF NOT EXISTS messages (
25
+ id TEXT PRIMARY KEY,
26
+ roomId TEXT NOT NULL,
27
+ senderId TEXT NOT NULL,
28
+ senderName TEXT NOT NULL,
29
+ content TEXT NOT NULL,
30
+ timestamp INTEGER NOT NULL,
31
+ FOREIGN KEY (roomId) REFERENCES rooms(id)
32
+ )
33
+ `);
34
+ this.db.exec('CREATE INDEX IF NOT EXISTS idx_messages_room ON messages(roomId, timestamp)');
35
+ this.db.exec(`
36
+ CREATE TABLE IF NOT EXISTS room_agents (
37
+ id TEXT PRIMARY KEY,
38
+ roomId TEXT NOT NULL,
39
+ agentId TEXT NOT NULL,
40
+ profile TEXT NOT NULL,
41
+ name TEXT NOT NULL,
42
+ description TEXT NOT NULL DEFAULT '',
43
+ invited INTEGER NOT NULL DEFAULT 0,
44
+ FOREIGN KEY (roomId) REFERENCES rooms(id)
45
+ )
46
+ `);
47
+ this.db.exec('CREATE INDEX IF NOT EXISTS idx_room_agents_room ON room_agents(roomId)');
48
+ }
49
+ // ─── Rooms ────────────────────────────────────────────────
50
+ getRoom(roomId) {
51
+ return this.db.prepare('SELECT id, name, inviteCode FROM rooms WHERE id = ?').get(roomId);
52
+ }
53
+ getRoomByInviteCode(code) {
54
+ return this.db.prepare('SELECT id, name, inviteCode FROM rooms WHERE inviteCode = ?').get(code);
55
+ }
56
+ getAllRooms() {
57
+ return this.db.prepare('SELECT id, name, inviteCode FROM rooms ORDER BY id').all();
58
+ }
59
+ saveRoom(id, name, inviteCode) {
60
+ this.db.prepare('INSERT OR IGNORE INTO rooms (id, name, inviteCode) VALUES (?, ?, ?)').run(id, name, inviteCode || null);
61
+ }
62
+ updateRoomInviteCode(roomId, inviteCode) {
63
+ this.db.prepare('UPDATE rooms SET inviteCode = ? WHERE id = ?').run(inviteCode, roomId);
64
+ }
65
+ // ─── Messages ─────────────────────────────────────────────
66
+ getMessages(roomId, limit = 500) {
67
+ const rows = this.db.prepare('SELECT id, roomId, senderId, senderName, content, timestamp FROM messages WHERE roomId = ? ORDER BY timestamp DESC LIMIT ?').all(roomId, limit);
68
+ return rows.reverse(); // return in chronological order
69
+ }
70
+ addMessage(msg) {
71
+ this.db.prepare('INSERT INTO messages (id, roomId, senderId, senderName, content, timestamp) VALUES (?, ?, ?, ?, ?, ?)').run(msg.id, msg.roomId, msg.senderId, msg.senderName, msg.content, msg.timestamp);
72
+ }
73
+ pruneMessages(roomId, keep = 500) {
74
+ const count = this.db.prepare('SELECT COUNT(*) as c FROM messages WHERE roomId = ?').get(roomId).c;
75
+ if (count > keep) {
76
+ const cutoff = this.db.prepare('SELECT timestamp FROM messages WHERE roomId = ? ORDER BY timestamp DESC LIMIT 1 OFFSET ?').get(roomId, keep - 1);
77
+ if (cutoff) {
78
+ this.db.prepare('DELETE FROM messages WHERE roomId = ? AND timestamp < ?').run(roomId, cutoff.timestamp);
79
+ }
80
+ }
81
+ }
82
+ // ─── Room Agents ──────────────────────────────────────────
83
+ getRoomAgents(roomId) {
84
+ return this.db.prepare('SELECT id, roomId, agentId, profile, name, description, invited FROM room_agents WHERE roomId = ?').all(roomId);
85
+ }
86
+ addRoomAgent(roomId, agentId, profile, name, description, invited) {
87
+ const id = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
88
+ this.db.prepare('INSERT INTO room_agents (id, roomId, agentId, profile, name, description, invited) VALUES (?, ?, ?, ?, ?, ?, ?)').run(id, roomId, agentId, profile, name, description, invited);
89
+ return { id, roomId, agentId, profile, name, description, invited };
90
+ }
91
+ removeRoomAgent(agentId) {
92
+ this.db.prepare('DELETE FROM room_agents WHERE id = ?').run(agentId);
93
+ }
94
+ close() {
95
+ this.db.close();
96
+ }
97
+ }
98
+ // ─── ChatRoom (in-memory, for online members) ─────────────────
99
+ class ChatRoom {
100
+ id;
101
+ name;
102
+ members = new Map();
103
+ constructor(id, name) {
104
+ this.id = id;
105
+ this.name = name || id;
106
+ }
107
+ addMember(id, name) {
108
+ const member = { id, name, joinedAt: Date.now() };
109
+ this.members.set(id, member);
110
+ return member;
111
+ }
112
+ removeMember(id) {
113
+ const member = this.members.get(id);
114
+ this.members.delete(id);
115
+ return member;
116
+ }
117
+ getMembersList() {
118
+ return Array.from(this.members.values());
119
+ }
120
+ hasMember(id) {
121
+ return this.members.has(id);
122
+ }
123
+ }
124
+ // ─── GroupChat Server ────────────────────────────────────────
125
+ class GroupChatServer {
126
+ io;
127
+ storage;
128
+ rooms = new Map();
129
+ userNames = new Map();
130
+ agentClients = new agent_clients_1.AgentClients();
131
+ gatewayManager = null;
132
+ setGatewayManager(manager) {
133
+ this.gatewayManager = manager;
134
+ this.agentClients.setGatewayManager(manager);
135
+ }
136
+ constructor(httpServer) {
137
+ const dbPath = (0, path_1.join)(config_1.config.dataDir, 'webui.db');
138
+ (0, fs_1.mkdirSync)(config_1.config.dataDir, { recursive: true });
139
+ this.storage = new ChatStorage(dbPath);
140
+ this.io = new socket_io_1.Server(httpServer, {
141
+ path: '/api/hermes/group-chat',
142
+ cors: { origin: '*', methods: ['GET', 'POST'] },
143
+ });
144
+ this.io.use(this.authMiddleware.bind(this));
145
+ this.io.on('connection', this.onConnection.bind(this));
146
+ // Restore persisted rooms into memory
147
+ this.storage.getAllRooms().forEach((row) => {
148
+ this.rooms.set(row.id, new ChatRoom(row.id, row.name));
149
+ });
150
+ console.log('[GroupChat] Socket.IO ready at /group-chat');
151
+ // Restore agent connections from SQLite
152
+ this.restoreAgents();
153
+ }
154
+ getIO() {
155
+ return this.io;
156
+ }
157
+ getStorage() {
158
+ return this.storage;
159
+ }
160
+ getRoomIds() {
161
+ return Array.from(this.rooms.keys());
162
+ }
163
+ // ─── Restore Agents ─────────────────────────────────────────
164
+ async restoreAgents() {
165
+ const rooms = this.storage.getAllRooms();
166
+ let total = 0;
167
+ for (const room of rooms) {
168
+ const agents = this.storage.getRoomAgents(room.id);
169
+ for (const agent of agents) {
170
+ try {
171
+ const client = await this.agentClients.createAgent({
172
+ profile: agent.profile,
173
+ name: agent.name,
174
+ description: agent.description,
175
+ invited: agent.invited,
176
+ });
177
+ await this.agentClients.addAgentToRoom(room.id, client);
178
+ total++;
179
+ }
180
+ catch (err) {
181
+ console.error(`[GroupChat] Failed to restore agent ${agent.name} in room ${room.id}: ${err.message}`);
182
+ }
183
+ }
184
+ }
185
+ if (total > 0) {
186
+ console.log(`[GroupChat] Restored ${total} agent(s) across ${rooms.length} room(s)`);
187
+ }
188
+ }
189
+ // ─── Auth ───────────────────────────────────────────────────
190
+ async authMiddleware(socket, next) {
191
+ const authToken = await (0, auth_1.getToken)();
192
+ if (authToken) {
193
+ const token = socket.handshake.auth.token || socket.handshake.query.token || '';
194
+ if (token !== authToken) {
195
+ return next(new Error('Unauthorized'));
196
+ }
197
+ }
198
+ next();
199
+ }
200
+ // ─── Connection ─────────────────────────────────────────────
201
+ onConnection(socket) {
202
+ const userId = socket.id;
203
+ const userName = socket.handshake.auth.name || `User-${userId.slice(0, 6)}`;
204
+ this.userNames.set(userId, userName);
205
+ console.log(`[GroupChat] Connected: ${userName} (${userId})`);
206
+ socket.on('join', (data, ack) => this.handleJoin(socket, data, ack));
207
+ socket.on('message', (data, ack) => this.handleMessage(socket, data, ack));
208
+ socket.on('typing', (data) => this.handleTyping(socket, data));
209
+ socket.on('stop_typing', (data) => this.handleStopTyping(socket, data));
210
+ socket.on('disconnect', () => this.handleDisconnect(socket));
211
+ }
212
+ // ─── Handlers ───────────────────────────────────────────────
213
+ handleJoin(socket, data, ack) {
214
+ const userId = socket.id;
215
+ if (data.name)
216
+ this.userNames.set(userId, data.name);
217
+ const userName = this.userNames.get(userId);
218
+ const roomId = data.roomId || 'general';
219
+ let room = this.rooms.get(roomId);
220
+ if (!room) {
221
+ room = new ChatRoom(roomId);
222
+ this.rooms.set(roomId, room);
223
+ this.storage.saveRoom(roomId, roomId);
224
+ }
225
+ room.addMember(userId, userName);
226
+ socket.join(roomId);
227
+ socket.to(roomId).emit('member_joined', {
228
+ roomId,
229
+ memberId: userId,
230
+ memberName: userName,
231
+ members: room.getMembersList(),
232
+ });
233
+ // Load history from SQLite
234
+ const messages = this.storage.getMessages(roomId);
235
+ ack?.({
236
+ roomId,
237
+ roomName: room.name,
238
+ members: room.getMembersList(),
239
+ messages,
240
+ rooms: this.getRoomIds(),
241
+ });
242
+ console.log(`[GroupChat] ${userName} joined room: ${roomId}`);
243
+ }
244
+ handleMessage(socket, data, ack) {
245
+ const userId = socket.id;
246
+ const roomId = data.roomId || 'general';
247
+ const room = this.rooms.get(roomId);
248
+ if (!room || !room.hasMember(userId)) {
249
+ ack?.({ error: 'Not in room' });
250
+ return;
251
+ }
252
+ const msg = {
253
+ id: this.generateId(),
254
+ roomId,
255
+ senderId: userId,
256
+ senderName: this.userNames.get(userId),
257
+ content: data.content,
258
+ timestamp: Date.now(),
259
+ };
260
+ this.storage.addMessage(msg);
261
+ this.storage.pruneMessages(roomId);
262
+ this.io.to(roomId).emit('message', msg);
263
+ ack?.({ id: msg.id });
264
+ }
265
+ handleTyping(socket, data) {
266
+ const roomId = data.roomId || 'general';
267
+ socket.to(roomId).emit('typing', {
268
+ roomId,
269
+ userId: socket.id,
270
+ userName: this.userNames.get(socket.id),
271
+ });
272
+ }
273
+ handleStopTyping(socket, data) {
274
+ const roomId = data.roomId || 'general';
275
+ socket.to(roomId).emit('stop_typing', {
276
+ roomId,
277
+ userId: socket.id,
278
+ userName: this.userNames.get(socket.id),
279
+ });
280
+ }
281
+ handleDisconnect(socket) {
282
+ const userId = socket.id;
283
+ const userName = this.userNames.get(userId);
284
+ console.log(`[GroupChat] Disconnected: ${userName} (${userId})`);
285
+ this.leaveAllRooms(socket, userId, userName || userId);
286
+ this.userNames.delete(userId);
287
+ }
288
+ // ─── Helpers ────────────────────────────────────────────────
289
+ leaveAllRooms(socket, userId, userName) {
290
+ this.rooms.forEach((room, rid) => {
291
+ if (room.hasMember(userId)) {
292
+ room.removeMember(userId);
293
+ socket.leave(rid);
294
+ this.io.to(rid).emit('member_left', {
295
+ roomId: rid,
296
+ memberId: userId,
297
+ memberName: userName,
298
+ members: room.getMembersList(),
299
+ });
300
+ }
301
+ });
302
+ }
303
+ generateId() {
304
+ return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
305
+ }
306
+ }
307
+ exports.GroupChatServer = GroupChatServer;
@@ -0,0 +1 @@
1
+ export declare function bindShutdown(server: any): void;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bindShutdown = bindShutdown;
4
+ function bindShutdown(server) {
5
+ let isShuttingDown = false;
6
+ const shutdown = async (signal) => {
7
+ if (isShuttingDown)
8
+ return;
9
+ isShuttingDown = true;
10
+ console.log(`\n[${signal}] shutting down...`);
11
+ try {
12
+ if (server) {
13
+ await new Promise((resolve) => {
14
+ server.close(() => {
15
+ console.log('✓ http server closed');
16
+ resolve();
17
+ });
18
+ });
19
+ }
20
+ }
21
+ catch (err) {
22
+ console.error('shutdown error:', err);
23
+ }
24
+ process.exit(0);
25
+ };
26
+ process.once('SIGUSR2', shutdown);
27
+ process.on('SIGINT', shutdown);
28
+ process.on('SIGTERM', shutdown);
29
+ process.on('uncaughtException', (err) => {
30
+ console.error('uncaughtException:', err);
31
+ shutdown('uncaughtException');
32
+ });
33
+ process.on('unhandledRejection', (err) => {
34
+ console.error('unhandledRejection:', err);
35
+ shutdown('unhandledRejection');
36
+ });
37
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hermes-web-ui",
3
- "version": "0.3.8",
3
+ "version": "0.4.0",
4
4
  "description": "Web dashboard for Hermes Agent — multi-platform AI chat, session management, scheduled jobs, usage analytics & channel configuration (Telegram, Discord, Slack, WhatsApp)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1 +0,0 @@
1
- import{t as e}from"./app-B7ktf7Fh.js";export{e as useAppStore};
@@ -1 +0,0 @@
1
- import{F as e}from"./router-D8sJ39Io.js";import{Y as t}from"./browser-f5W8abIG.js";import{s as n}from"./Modal-Bp9RK8LZ.js";function r(){let r=e(n,null);return r===null&&t(`use-dialog`,`No outer <n-dialog-provider /> founded.`),r}export{r as t};