hermes-web-ui 0.3.7 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -2
- package/dist/client/assets/{Add-Cc7cgBoB.js → Add-s316k4Av.js} +1 -1
- package/dist/client/assets/{Button-EoeZgIFH.js → Button-BkA_RI8a.js} +1 -1
- package/dist/client/assets/{ChannelsView-Bfbq3w7n.js → ChannelsView-DPmPn8DW.js} +1 -1
- package/dist/client/assets/ChatView-DEtziOPB.js +127 -0
- package/dist/client/assets/{ChatView-Vfi_jEpI.css → ChatView-DI3XN8vz.css} +1 -1
- package/dist/client/assets/{Close-CKHcXisf.js → Close-CrLD0IXG.js} +1 -1
- package/dist/client/assets/{FormItem-CvZvjrtr.js → FormItem-CQLdFrl9.js} +1 -1
- package/dist/client/assets/{GatewaysView-Dp4-TFPE.js → GatewaysView-CC1Y0tZZ.js} +1 -1
- package/dist/client/assets/{Input-Bk7XdoG-.js → Input-nXKlujwJ.js} +1 -1
- package/dist/client/assets/{InputNumber-Dn0EHi-K.js → InputNumber-DLZwwIyX.js} +1 -1
- package/dist/client/assets/{JobsView-D4JN73Zz.js → JobsView-BC0bBrJO.js} +2 -2
- package/dist/client/assets/{LoginView--hy5CI5O.js → LoginView-PqpFR9bV.js} +1 -1
- package/dist/client/assets/{LogsView-Dz2ZeYad.js → LogsView-DtR88N0b.js} +1 -1
- package/dist/client/assets/{MarkdownRenderer-BbedVxvo.js → MarkdownRenderer-BSLfTurm.js} +1 -1
- package/dist/client/assets/{MemoryView-D2EHM35l.js → MemoryView-CJRWnePL.js} +1 -1
- package/dist/client/assets/{Modal-C5-Iw50K.js → Modal-Bp9RK8LZ.js} +1 -1
- package/dist/client/assets/{ModelsView-CtrRf4vK.js → ModelsView-7Obe34Cz.js} +1 -1
- package/dist/client/assets/{Popconfirm-C-M2anVL.js → Popconfirm-DTdUi7r_.js} +1 -1
- package/dist/client/assets/{Popover-mIRPCy7U.js → Popover-_M3o0B7L.js} +1 -1
- package/dist/client/assets/{ProfilesView-Dy9PivgB.js → ProfilesView-1_GmRx-S.js} +1 -1
- package/dist/client/assets/{Select-uZBC8HC2.js → Select-aHPR3urY.js} +1 -1
- package/dist/client/assets/{SettingRow-D9R65bDj.js → SettingRow-DKasLuS5.js} +1 -1
- package/dist/client/assets/{SettingsView-DWEEXqSY.js → SettingsView-DZCA7_CM.js} +1 -1
- package/dist/client/assets/{SkillsView-CdZSRy9_.js → SkillsView-Dk7O05cK.js} +1 -1
- package/dist/client/assets/{Spin-ChbFBUOD.js → Spin-Bt_9cTiO.js} +1 -1
- package/dist/client/assets/{Suffix-DgzfIwzx.js → Suffix-XaH8SDbR.js} +1 -1
- package/dist/client/assets/{Switch--HhY1uSh.js → Switch-D1_psmjT.js} +1 -1
- package/dist/client/assets/{Tag-B2zrHMmZ.js → Tag-3FaOhoJN.js} +1 -1
- package/dist/client/assets/{TerminalView-BwfnH803.js → TerminalView-DNU7oQxK.js} +1 -1
- package/dist/client/assets/{Tooltip-9tdvSKGi.js → Tooltip-YHrHWGPa.js} +1 -1
- package/dist/client/assets/{UsageView-zL3a7F86.js → UsageView-COCrOiiV.js} +1 -1
- package/dist/client/assets/{Warning-CXXqHzLa.js → Warning-B6CM9aBl.js} +1 -1
- package/dist/client/assets/{_plugin-vue_export-helper-Cnn0Z73x.js → _plugin-vue_export-helper-BGG8ORDx.js} +1 -1
- package/dist/client/assets/{app-BMobzABI.js → app-B7ktf7Fh.js} +1 -1
- package/dist/client/assets/app-BPvTl2-V.js +1 -0
- package/dist/client/assets/{browser-CQRjhbaB.js → browser-f5W8abIG.js} +1 -1
- package/dist/client/assets/chat-6q6pkzEW.js +6 -0
- package/dist/client/assets/composables-xV7dhNpf.js +1 -0
- package/dist/client/assets/{fade-in.cssr-lwO9nLky.js → fade-in.cssr-ifHK7yH1.js} +1 -1
- package/dist/client/assets/{index-Tg6M43Om.js → index-CSCYx7ux.js} +1 -1
- package/dist/client/assets/{jobs-Z2HS0j2d.js → jobs-DObWfhbO.js} +1 -1
- package/dist/client/assets/{light-oE8MEiWL.js → light-CxjyoF0s.js} +1 -1
- package/dist/client/assets/{light-Dx6qj2pM.js → light-D1yfed_s.js} +1 -1
- package/dist/client/assets/{light-DzpNsLai.js → light-DWy-mwyK.js} +1 -1
- package/dist/client/assets/{light-CjCy-Dkn.js → light-D_3MwJj1.js} +1 -1
- package/dist/client/assets/{light-DgIst23O.js → light-DgLcPjgU.js} +1 -1
- package/dist/client/assets/{light-DZ0Ns16h.js → light-TGFKT-UB.js} +1 -1
- package/dist/client/assets/{models-DLQiHB7r.js → models-DQ4CT-vv.js} +1 -1
- package/dist/client/assets/{pinia-Dp_b1vdW.js → pinia-DcAkZ8vx.js} +1 -1
- package/dist/client/assets/{profiles-CNTHYFZE.js → profiles-DzkigJwq.js} +1 -1
- package/dist/client/assets/{router-Dj-Nmg7q.js → router-D8sJ39Io.js} +2 -2
- package/dist/client/assets/{sessions-C0kvgvBm.js → sessions-Dg8n9PBo.js} +1 -1
- package/dist/client/assets/{skills-G7EoEvdS.js → skills-BehzdECn.js} +1 -1
- package/dist/client/assets/{use-message-BgToAqhv.js → use-message-DBz2JSTt.js} +1 -1
- package/dist/client/assets/{useTheme-BUShiwRu.js → useTheme-UdVT814n.js} +1 -1
- package/dist/client/index.html +27 -27
- package/dist/server/index.js +8 -3
- package/dist/server/routes/hermes/group-chat.d.ts +2 -0
- package/dist/server/routes/hermes/group-chat.js +135 -0
- package/dist/server/routes/upload.js +41 -11
- package/dist/server/services/group-chat/coordinator.d.ts +14 -0
- package/dist/server/services/group-chat/coordinator.js +230 -0
- package/dist/server/services/group-chat/index.d.ts +5 -0
- package/dist/server/services/group-chat/index.js +115 -0
- package/dist/server/services/group-chat/rooms-db.d.ts +56 -0
- package/dist/server/services/group-chat/rooms-db.js +199 -0
- package/package.json +1 -1
- package/dist/client/assets/ChatView-CDdyTo72.js +0 -127
- package/dist/client/assets/app-Bqu9Uz-1.js +0 -1
- package/dist/client/assets/chat-BIdq6ZXF.js +0 -6
- package/dist/client/assets/composables-ClIU-Ad1.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as e}from"./router-
|
|
1
|
+
import{o as e}from"./router-D8sJ39Io.js";async function t(t,n){let r=new URLSearchParams;t&&r.set(`source`,t),n&&r.set(`limit`,String(n));let i=r.toString();return(await e(`/api/hermes/sessions${i?`?${i}`:``}`)).sessions}async function n(t){try{return(await e(`/api/hermes/sessions/${t}`)).session}catch{return null}}async function r(t){try{return await e(`/api/hermes/sessions/${t}`,{method:`DELETE`}),!0}catch{return!1}}async function i(t,n){try{return await e(`/api/hermes/sessions/${t}/rename`,{method:`POST`,body:JSON.stringify({title:n})}),!0}catch{return!1}}export{i,n,t as r,r as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as e}from"./router-
|
|
1
|
+
import{o as e}from"./router-D8sJ39Io.js";async function t(){return(await e(`/api/hermes/skills`)).categories}async function n(t){return(await e(`/api/hermes/skills/${t}`)).content}async function r(t,n){return(await e(`/api/hermes/skills/${t}/${n}/files`)).files}async function i(){return e(`/api/hermes/memory`)}async function a(t,n){await e(`/api/hermes/memory`,{method:`POST`,body:JSON.stringify({section:t,content:n})})}async function o(t,n){await e(`/api/hermes/skills/toggle`,{method:`PUT`,body:JSON.stringify({name:t,enabled:n})})}export{a,t as i,n,o,r,i as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{F as e}from"./router-
|
|
1
|
+
import{F as e}from"./router-D8sJ39Io.js";import{$ as t,Y as n}from"./browser-f5W8abIG.js";var r=t(`n-message-api`),i=t(`n-message-provider`);function a(){let t=e(r,null);return t===null&&n(`use-message`,"No outer <n-message-provider /> founded. See prerequisite in https://www.naiveui.com/en-US/os-theme/components/message for more details. If you want to use `useMessage` outside setup, please check https://www.naiveui.com/zh-CN/os-theme/components/message#Q-&-A."),t}export{r as n,i as r,a as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{X as e,ct as t}from"./router-
|
|
1
|
+
import{X as e,ct as t}from"./router-D8sJ39Io.js";var n=`hermes_theme`,r=t(localStorage.getItem(n)||`system`),i=t(!1);function a(e){i.value=e,document.documentElement.classList.toggle(`dark`,e)}function o(e){return e===`system`?window.matchMedia(`(prefers-color-scheme: dark)`).matches:e===`dark`}a(o(r.value)),window.matchMedia(`(prefers-color-scheme: dark)`).addEventListener(`change`,()=>{r.value===`system`&&a(o(`system`))}),e(r,e=>{localStorage.setItem(n,e),a(o(e))});function s(){function e(e){r.value=e}function t(){r.value=i.value?`light`:`dark`}return{mode:r,isDark:i,setMode:e,toggleTheme:t}}export{s as t};
|
package/dist/client/index.html
CHANGED
|
@@ -6,36 +6,36 @@
|
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>Hermes</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/router-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="/assets/browser-
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/fade-in.cssr-
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/Suffix-
|
|
15
|
-
<link rel="modulepreload" crossorigin href="/assets/Close-
|
|
16
|
-
<link rel="modulepreload" crossorigin href="/assets/Popover-
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/assets/Button-
|
|
18
|
-
<link rel="modulepreload" crossorigin href="/assets/Tag-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-CSCYx7ux.js"></script>
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/router-D8sJ39Io.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-BGG8ORDx.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/browser-f5W8abIG.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/fade-in.cssr-ifHK7yH1.js">
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/Suffix-XaH8SDbR.js">
|
|
15
|
+
<link rel="modulepreload" crossorigin href="/assets/Close-CrLD0IXG.js">
|
|
16
|
+
<link rel="modulepreload" crossorigin href="/assets/Popover-_M3o0B7L.js">
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/assets/Button-BkA_RI8a.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/assets/Tag-3FaOhoJN.js">
|
|
19
19
|
<link rel="modulepreload" crossorigin href="/assets/create-5zWq3BEB.js">
|
|
20
|
-
<link rel="modulepreload" crossorigin href="/assets/Select-
|
|
21
|
-
<link rel="modulepreload" crossorigin href="/assets/Warning-
|
|
22
|
-
<link rel="modulepreload" crossorigin href="/assets/Modal-
|
|
23
|
-
<link rel="modulepreload" crossorigin href="/assets/Input-
|
|
24
|
-
<link rel="modulepreload" crossorigin href="/assets/light-
|
|
20
|
+
<link rel="modulepreload" crossorigin href="/assets/Select-aHPR3urY.js">
|
|
21
|
+
<link rel="modulepreload" crossorigin href="/assets/Warning-B6CM9aBl.js">
|
|
22
|
+
<link rel="modulepreload" crossorigin href="/assets/Modal-Bp9RK8LZ.js">
|
|
23
|
+
<link rel="modulepreload" crossorigin href="/assets/Input-nXKlujwJ.js">
|
|
24
|
+
<link rel="modulepreload" crossorigin href="/assets/light-D1yfed_s.js">
|
|
25
25
|
<link rel="modulepreload" crossorigin href="/assets/omit-1BRB6K75.js">
|
|
26
|
-
<link rel="modulepreload" crossorigin href="/assets/pinia-
|
|
27
|
-
<link rel="modulepreload" crossorigin href="/assets/profiles-
|
|
28
|
-
<link rel="modulepreload" crossorigin href="/assets/sessions-
|
|
29
|
-
<link rel="modulepreload" crossorigin href="/assets/app-
|
|
30
|
-
<link rel="modulepreload" crossorigin href="/assets/chat-
|
|
31
|
-
<link rel="modulepreload" crossorigin href="/assets/light-
|
|
32
|
-
<link rel="modulepreload" crossorigin href="/assets/use-message-
|
|
33
|
-
<link rel="modulepreload" crossorigin href="/assets/light-
|
|
34
|
-
<link rel="modulepreload" crossorigin href="/assets/light-
|
|
26
|
+
<link rel="modulepreload" crossorigin href="/assets/pinia-DcAkZ8vx.js">
|
|
27
|
+
<link rel="modulepreload" crossorigin href="/assets/profiles-DzkigJwq.js">
|
|
28
|
+
<link rel="modulepreload" crossorigin href="/assets/sessions-Dg8n9PBo.js">
|
|
29
|
+
<link rel="modulepreload" crossorigin href="/assets/app-B7ktf7Fh.js">
|
|
30
|
+
<link rel="modulepreload" crossorigin href="/assets/chat-6q6pkzEW.js">
|
|
31
|
+
<link rel="modulepreload" crossorigin href="/assets/light-TGFKT-UB.js">
|
|
32
|
+
<link rel="modulepreload" crossorigin href="/assets/use-message-DBz2JSTt.js">
|
|
33
|
+
<link rel="modulepreload" crossorigin href="/assets/light-DWy-mwyK.js">
|
|
34
|
+
<link rel="modulepreload" crossorigin href="/assets/light-CxjyoF0s.js">
|
|
35
35
|
<link rel="modulepreload" crossorigin href="/assets/_common-Yp55QE79.js">
|
|
36
|
-
<link rel="modulepreload" crossorigin href="/assets/light-
|
|
37
|
-
<link rel="modulepreload" crossorigin href="/assets/light-
|
|
38
|
-
<link rel="modulepreload" crossorigin href="/assets/useTheme-
|
|
36
|
+
<link rel="modulepreload" crossorigin href="/assets/light-D_3MwJj1.js">
|
|
37
|
+
<link rel="modulepreload" crossorigin href="/assets/light-DgLcPjgU.js">
|
|
38
|
+
<link rel="modulepreload" crossorigin href="/assets/useTheme-UdVT814n.js">
|
|
39
39
|
<link rel="modulepreload" crossorigin href="/assets/logo-Cd-t_oGE.js">
|
|
40
40
|
<link rel="stylesheet" crossorigin href="/assets/index-BEcRccNA.css">
|
|
41
41
|
</head>
|
package/dist/server/index.js
CHANGED
|
@@ -72,6 +72,7 @@ async function checkLatestVersion() {
|
|
|
72
72
|
try {
|
|
73
73
|
const res = await fetch('https://registry.npmjs.org/hermes-web-ui/latest', {
|
|
74
74
|
signal: AbortSignal.timeout(5000),
|
|
75
|
+
headers: { 'Cache-Control': 'no-cache' },
|
|
75
76
|
});
|
|
76
77
|
if (res.ok) {
|
|
77
78
|
const data = await res.json();
|
|
@@ -111,9 +112,11 @@ async function bootstrap() {
|
|
|
111
112
|
app.use(async (ctx, next) => {
|
|
112
113
|
if (ctx.path === '/api/hermes/update' && ctx.method === 'POST') {
|
|
113
114
|
const isWin = process.platform === 'win32';
|
|
115
|
+
// Run npm install directly — calling `hermes-web-ui update` would kill this
|
|
116
|
+
// process (stopDaemon) before the response can be sent to the client.
|
|
114
117
|
const cmd = isWin
|
|
115
|
-
? 'cmd /c hermes-web-ui
|
|
116
|
-
: 'hermes-web-ui
|
|
118
|
+
? 'cmd /c npm install -g hermes-web-ui@latest'
|
|
119
|
+
: 'npm install -g hermes-web-ui@latest';
|
|
117
120
|
try {
|
|
118
121
|
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
119
122
|
const output = execSync(cmd, {
|
|
@@ -122,6 +125,8 @@ async function bootstrap() {
|
|
|
122
125
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
123
126
|
});
|
|
124
127
|
ctx.body = { success: true, message: output.trim() };
|
|
128
|
+
// Restart the server after response is sent
|
|
129
|
+
setTimeout(() => process.exit(0), 1000);
|
|
125
130
|
}
|
|
126
131
|
catch (err) {
|
|
127
132
|
ctx.status = 500;
|
|
@@ -186,7 +191,7 @@ async function bootstrap() {
|
|
|
186
191
|
bindShutdown();
|
|
187
192
|
// Check for updates every 4 hours
|
|
188
193
|
checkLatestVersion();
|
|
189
|
-
setInterval(checkLatestVersion,
|
|
194
|
+
setInterval(checkLatestVersion, 60 * 60 * 1000);
|
|
190
195
|
}
|
|
191
196
|
// ============================
|
|
192
197
|
// ✅ 统一关闭逻辑(核心)
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.groupChatRoutes = void 0;
|
|
40
|
+
const router_1 = __importDefault(require("@koa/router"));
|
|
41
|
+
const roomsDb = __importStar(require("../../services/group-chat/rooms-db"));
|
|
42
|
+
exports.groupChatRoutes = new router_1.default();
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Rooms
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
exports.groupChatRoutes.get('/api/hermes/group-chat/rooms', async (ctx) => {
|
|
47
|
+
const rooms = roomsDb.listRooms();
|
|
48
|
+
ctx.body = { rooms };
|
|
49
|
+
});
|
|
50
|
+
exports.groupChatRoutes.post('/api/hermes/group-chat/rooms', async (ctx) => {
|
|
51
|
+
const { name } = ctx.request.body;
|
|
52
|
+
if (!name?.trim()) {
|
|
53
|
+
ctx.status = 400;
|
|
54
|
+
ctx.body = { error: 'name is required' };
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const room = roomsDb.createRoom(name.trim());
|
|
58
|
+
ctx.body = { room };
|
|
59
|
+
});
|
|
60
|
+
exports.groupChatRoutes.get('/api/hermes/group-chat/rooms/:roomId', async (ctx) => {
|
|
61
|
+
const { roomId } = ctx.params;
|
|
62
|
+
const room = roomsDb.getRoom(roomId);
|
|
63
|
+
if (!room) {
|
|
64
|
+
ctx.status = 404;
|
|
65
|
+
ctx.body = { error: 'Room not found' };
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const agents = roomsDb.getRoomAgents(roomId);
|
|
69
|
+
ctx.body = { room, agents };
|
|
70
|
+
});
|
|
71
|
+
exports.groupChatRoutes.put('/api/hermes/group-chat/rooms/:roomId', async (ctx) => {
|
|
72
|
+
const { roomId } = ctx.params;
|
|
73
|
+
const { name, settings } = ctx.request.body;
|
|
74
|
+
const updated = roomsDb.updateRoom(roomId, { name, settings });
|
|
75
|
+
if (!updated) {
|
|
76
|
+
ctx.status = 404;
|
|
77
|
+
ctx.body = { error: 'Room not found' };
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
ctx.body = { success: true };
|
|
81
|
+
});
|
|
82
|
+
exports.groupChatRoutes.delete('/api/hermes/group-chat/rooms/:roomId', async (ctx) => {
|
|
83
|
+
const { roomId } = ctx.params;
|
|
84
|
+
roomsDb.deleteRoom(roomId);
|
|
85
|
+
ctx.body = { success: true };
|
|
86
|
+
});
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Agents
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
exports.groupChatRoutes.get('/api/hermes/group-chat/rooms/:roomId/agents', async (ctx) => {
|
|
91
|
+
const { roomId } = ctx.params;
|
|
92
|
+
const agents = roomsDb.getRoomAgents(roomId);
|
|
93
|
+
ctx.body = { agents };
|
|
94
|
+
});
|
|
95
|
+
exports.groupChatRoutes.post('/api/hermes/group-chat/rooms/:roomId/agents', async (ctx) => {
|
|
96
|
+
const { roomId } = ctx.params;
|
|
97
|
+
const { agent_name, profile, role_prompt } = ctx.request.body;
|
|
98
|
+
if (!agent_name?.trim() || !profile?.trim()) {
|
|
99
|
+
ctx.status = 400;
|
|
100
|
+
ctx.body = { error: 'agent_name and profile are required' };
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// Validate agent_name only contains word characters
|
|
104
|
+
if (!/^\w+$/.test(agent_name.trim())) {
|
|
105
|
+
ctx.status = 400;
|
|
106
|
+
ctx.body = { error: 'agent_name must only contain letters, numbers, and underscores' };
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const agent = roomsDb.addRoomAgent(roomId, {
|
|
110
|
+
agent_name: agent_name.trim(),
|
|
111
|
+
profile: profile.trim(),
|
|
112
|
+
role_prompt,
|
|
113
|
+
});
|
|
114
|
+
if (!agent) {
|
|
115
|
+
ctx.status = 409;
|
|
116
|
+
ctx.body = { error: 'Agent already exists in this room' };
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
ctx.body = { agent };
|
|
120
|
+
});
|
|
121
|
+
exports.groupChatRoutes.delete('/api/hermes/group-chat/rooms/:roomId/agents/:agentName', async (ctx) => {
|
|
122
|
+
const { roomId, agentName } = ctx.params;
|
|
123
|
+
roomsDb.removeRoomAgent(roomId, agentName);
|
|
124
|
+
ctx.body = { success: true };
|
|
125
|
+
});
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Messages
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
exports.groupChatRoutes.get('/api/hermes/group-chat/rooms/:roomId/messages', async (ctx) => {
|
|
130
|
+
const { roomId } = ctx.params;
|
|
131
|
+
const limit = parseInt(ctx.query.limit) || 100;
|
|
132
|
+
const offset = parseInt(ctx.query.offset) || 0;
|
|
133
|
+
const messages = roomsDb.getRoomMessages(roomId, limit, offset);
|
|
134
|
+
ctx.body = { messages };
|
|
135
|
+
});
|
|
@@ -23,28 +23,58 @@ exports.uploadRoutes.post('/upload', async (ctx) => {
|
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
await (0, promises_1.mkdir)(config_1.config.uploadDir, { recursive: true });
|
|
26
|
-
// Read raw body
|
|
26
|
+
// Read raw body as Buffer
|
|
27
27
|
const chunks = [];
|
|
28
28
|
for await (const chunk of ctx.req)
|
|
29
29
|
chunks.push(chunk);
|
|
30
|
-
const
|
|
31
|
-
const
|
|
30
|
+
const raw = Buffer.concat(chunks);
|
|
31
|
+
const boundaryBuf = Buffer.from(boundary);
|
|
32
|
+
const parts = splitMultipart(raw, boundaryBuf);
|
|
32
33
|
const results = [];
|
|
33
34
|
for (const part of parts) {
|
|
34
|
-
const headerEnd = part.indexOf('\r\n\r\n');
|
|
35
|
+
const headerEnd = part.indexOf(Buffer.from('\r\n\r\n'));
|
|
35
36
|
if (headerEnd === -1)
|
|
36
37
|
continue;
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
38
|
+
const headerBuf = part.subarray(0, headerEnd);
|
|
39
|
+
const header = headerBuf.toString('utf-8');
|
|
40
|
+
const data = part.subarray(headerEnd + 4, part.length - 2);
|
|
41
|
+
// Try RFC 5987 filename* first, fall back to filename
|
|
42
|
+
let filename = '';
|
|
43
|
+
const filenameStarMatch = header.match(/filename\*=UTF-8''(.+)/i);
|
|
44
|
+
if (filenameStarMatch) {
|
|
45
|
+
filename = decodeURIComponent(filenameStarMatch[1]);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const filenameMatch = header.match(/filename="([^"]+)"/);
|
|
49
|
+
if (!filenameMatch)
|
|
50
|
+
continue;
|
|
51
|
+
filename = filenameMatch[1];
|
|
52
|
+
}
|
|
43
53
|
const ext = filename.includes('.') ? '.' + filename.split('.').pop() : '';
|
|
44
54
|
const savedName = (0, crypto_1.randomBytes)(8).toString('hex') + ext;
|
|
45
55
|
const savedPath = `${config_1.config.uploadDir}/${savedName}`;
|
|
46
|
-
await (0, promises_1.writeFile)(savedPath,
|
|
56
|
+
await (0, promises_1.writeFile)(savedPath, data);
|
|
47
57
|
results.push({ name: filename, path: savedPath });
|
|
48
58
|
}
|
|
49
59
|
ctx.body = { files: results };
|
|
50
60
|
});
|
|
61
|
+
/**
|
|
62
|
+
* Split a multipart Buffer by boundary, returning part Buffers.
|
|
63
|
+
* Avoids string decoding so multi-byte characters (e.g. Chinese filenames) are preserved.
|
|
64
|
+
*/
|
|
65
|
+
function splitMultipart(raw, boundary) {
|
|
66
|
+
const parts = [];
|
|
67
|
+
let start = 0;
|
|
68
|
+
while (true) {
|
|
69
|
+
const idx = raw.indexOf(boundary, start);
|
|
70
|
+
if (idx === -1)
|
|
71
|
+
break;
|
|
72
|
+
if (start > 0) {
|
|
73
|
+
// Skip the \r\n after boundary
|
|
74
|
+
const partStart = start + 2;
|
|
75
|
+
parts.push(raw.subarray(partStart, idx));
|
|
76
|
+
}
|
|
77
|
+
start = idx + boundary.length;
|
|
78
|
+
}
|
|
79
|
+
return parts;
|
|
80
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as roomsDb from './rooms-db';
|
|
2
|
+
export type AgentStatus = 'thinking' | 'responding' | 'done' | 'error';
|
|
3
|
+
export interface CoordinatorCallbacks {
|
|
4
|
+
onDelta: (roomId: string, agentName: string, text: string) => void;
|
|
5
|
+
onAgentStatus: (roomId: string, agentName: string, status: AgentStatus) => void;
|
|
6
|
+
onAgentComplete: (roomId: string, agentName: string, message: roomsDb.RoomMessage) => void;
|
|
7
|
+
onAgentError: (roomId: string, agentName: string, error: string) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function parseMentions(text: string): string[];
|
|
10
|
+
export declare function buildAgentContext(roomId: string, agentName: string, agents: roomsDb.RoomAgent[], maxMessages?: number): Array<{
|
|
11
|
+
role: string;
|
|
12
|
+
content: string;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function processMessage(roomId: string, content: string, callbacks: CoordinatorCallbacks): Promise<void>;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parseMentions = parseMentions;
|
|
37
|
+
exports.buildAgentContext = buildAgentContext;
|
|
38
|
+
exports.processMessage = processMessage;
|
|
39
|
+
const roomsDb = __importStar(require("./rooms-db"));
|
|
40
|
+
const gateways_1 = require("../../routes/hermes/gateways");
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// @mention parsing
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
function parseMentions(text) {
|
|
45
|
+
const pattern = /@(\w+)/g;
|
|
46
|
+
const mentions = [];
|
|
47
|
+
let match;
|
|
48
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
49
|
+
mentions.push(match[1]);
|
|
50
|
+
}
|
|
51
|
+
return [...new Set(mentions)];
|
|
52
|
+
}
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Context building
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
function buildAgentContext(roomId, agentName, agents, maxMessages = 20) {
|
|
57
|
+
const recentMessages = roomsDb.getRecentContext(roomId, agentName, maxMessages);
|
|
58
|
+
const context = [];
|
|
59
|
+
// Add role prompt
|
|
60
|
+
const agent = agents.find((a) => a.agent_name === agentName);
|
|
61
|
+
if (agent?.role_prompt) {
|
|
62
|
+
context.push({ role: 'system', content: agent.role_prompt });
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
context.push({
|
|
66
|
+
role: 'system',
|
|
67
|
+
content: `You are ${agentName}, participating in a group chat. Respond concisely. You can see messages where you were @mentioned and public messages (no @mentions).`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// Convert recent messages (DESC from DB → reverse to ASC chronological)
|
|
71
|
+
for (const msg of recentMessages.reverse()) {
|
|
72
|
+
if (msg.role === 'user') {
|
|
73
|
+
context.push({ role: 'user', content: msg.content });
|
|
74
|
+
}
|
|
75
|
+
else if (msg.role === 'assistant' && msg.agent_name) {
|
|
76
|
+
context.push({ role: 'assistant', content: `[${msg.agent_name}] ${msg.content}` });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return context;
|
|
80
|
+
}
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Main: process a user message
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
async function processMessage(roomId, content, callbacks) {
|
|
85
|
+
const mentions = parseMentions(content);
|
|
86
|
+
const agents = roomsDb.getRoomAgents(roomId);
|
|
87
|
+
if (agents.length === 0)
|
|
88
|
+
return;
|
|
89
|
+
// Determine responding agents
|
|
90
|
+
const respondingAgents = mentions.length > 0
|
|
91
|
+
? agents.filter((a) => mentions.includes(a.agent_name))
|
|
92
|
+
: agents;
|
|
93
|
+
if (respondingAgents.length === 0)
|
|
94
|
+
return;
|
|
95
|
+
// Store user message
|
|
96
|
+
const round = roomsDb.getLatestRound(roomId);
|
|
97
|
+
roomsDb.addMessage({
|
|
98
|
+
room_id: roomId,
|
|
99
|
+
role: 'user',
|
|
100
|
+
content,
|
|
101
|
+
mentions,
|
|
102
|
+
round,
|
|
103
|
+
});
|
|
104
|
+
// Fan out to agents in parallel
|
|
105
|
+
const tasks = respondingAgents.map((agent) => invokeAgent(roomId, agent, round, content, callbacks).catch(() => { }));
|
|
106
|
+
await Promise.allSettled(tasks);
|
|
107
|
+
}
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Invoke a single agent
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
async function invokeAgent(roomId, agent, round, userMessage, callbacks) {
|
|
112
|
+
const agentName = agent.agent_name;
|
|
113
|
+
let upstream = '';
|
|
114
|
+
let apiKey = null;
|
|
115
|
+
try {
|
|
116
|
+
callbacks.onAgentStatus(roomId, agentName, 'thinking');
|
|
117
|
+
// Resolve gateway URL and API key for this agent's profile
|
|
118
|
+
const manager = (0, gateways_1.getGatewayManager)();
|
|
119
|
+
if (!manager)
|
|
120
|
+
throw new Error('GatewayManager not initialized');
|
|
121
|
+
upstream = manager.getUpstream(agent.profile);
|
|
122
|
+
apiKey = manager.getApiKey(agent.profile);
|
|
123
|
+
// Build conversation context
|
|
124
|
+
const agents = roomsDb.getRoomAgents(roomId);
|
|
125
|
+
const context = buildAgentContext(roomId, agentName, agents);
|
|
126
|
+
// Start run via Hermes API Server
|
|
127
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
128
|
+
if (apiKey)
|
|
129
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
130
|
+
const body = { input: userMessage };
|
|
131
|
+
if (context.length > 0)
|
|
132
|
+
body.conversation_history = context;
|
|
133
|
+
if (agent.session_id)
|
|
134
|
+
body.session_id = agent.session_id;
|
|
135
|
+
const runRes = await fetch(`${upstream}/v1/runs`, {
|
|
136
|
+
method: 'POST',
|
|
137
|
+
headers,
|
|
138
|
+
body: JSON.stringify(body),
|
|
139
|
+
signal: AbortSignal.timeout(120_000),
|
|
140
|
+
});
|
|
141
|
+
if (!runRes.ok) {
|
|
142
|
+
const errText = await runRes.text();
|
|
143
|
+
throw new Error(`Hermes API error ${runRes.status}: ${errText.slice(0, 200)}`);
|
|
144
|
+
}
|
|
145
|
+
const runData = await runRes.json();
|
|
146
|
+
const runId = runData.run_id || runData.id;
|
|
147
|
+
if (!runId)
|
|
148
|
+
throw new Error('No run_id returned from Hermes API');
|
|
149
|
+
// Persist session_id for subsequent messages
|
|
150
|
+
if (runData.session_id && runData.session_id !== agent.session_id) {
|
|
151
|
+
roomsDb.updateRoomAgentSession(roomId, agentName, runData.session_id);
|
|
152
|
+
}
|
|
153
|
+
callbacks.onAgentStatus(roomId, agentName, 'responding');
|
|
154
|
+
// Stream SSE events
|
|
155
|
+
const sseHeaders = {};
|
|
156
|
+
if (apiKey)
|
|
157
|
+
sseHeaders['Authorization'] = `Bearer ${apiKey}`;
|
|
158
|
+
const sseRes = await fetch(`${upstream}/v1/runs/${runId}/events`, {
|
|
159
|
+
headers: sseHeaders,
|
|
160
|
+
signal: AbortSignal.timeout(300_000),
|
|
161
|
+
});
|
|
162
|
+
if (!sseRes.ok || !sseRes.body) {
|
|
163
|
+
throw new Error(`Failed to stream events: ${sseRes.status}`);
|
|
164
|
+
}
|
|
165
|
+
let fullContent = '';
|
|
166
|
+
const reader = sseRes.body.getReader();
|
|
167
|
+
const decoder = new TextDecoder();
|
|
168
|
+
let buffer = '';
|
|
169
|
+
try {
|
|
170
|
+
while (true) {
|
|
171
|
+
const { done, value } = await reader.read();
|
|
172
|
+
if (done)
|
|
173
|
+
break;
|
|
174
|
+
buffer += decoder.decode(value, { stream: true });
|
|
175
|
+
const lines = buffer.split('\n');
|
|
176
|
+
buffer = lines.pop() || '';
|
|
177
|
+
for (const line of lines) {
|
|
178
|
+
if (!line.startsWith('data: '))
|
|
179
|
+
continue;
|
|
180
|
+
const data = line.slice(6).trim();
|
|
181
|
+
if (data === '[DONE]')
|
|
182
|
+
continue;
|
|
183
|
+
try {
|
|
184
|
+
const event = JSON.parse(data);
|
|
185
|
+
if (event.event === 'message.delta' && event.delta) {
|
|
186
|
+
fullContent += event.delta;
|
|
187
|
+
callbacks.onDelta(roomId, agentName, event.delta);
|
|
188
|
+
}
|
|
189
|
+
if (event.event === 'run.completed' || event.event === 'run.failed') {
|
|
190
|
+
// If delta contained in completed event
|
|
191
|
+
if (event.event === 'run.completed' && event.response) {
|
|
192
|
+
fullContent = event.response;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// skip malformed JSON
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
reader.releaseLock();
|
|
204
|
+
}
|
|
205
|
+
if (!fullContent)
|
|
206
|
+
fullContent = '(no response)';
|
|
207
|
+
// Store completed response
|
|
208
|
+
const msg = roomsDb.addMessage({
|
|
209
|
+
room_id: roomId,
|
|
210
|
+
role: 'assistant',
|
|
211
|
+
agent_name: agentName,
|
|
212
|
+
content: fullContent,
|
|
213
|
+
round,
|
|
214
|
+
});
|
|
215
|
+
callbacks.onAgentStatus(roomId, agentName, 'done');
|
|
216
|
+
callbacks.onAgentComplete(roomId, agentName, msg);
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
const errorMsg = err?.message || 'Unknown error';
|
|
220
|
+
callbacks.onAgentStatus(roomId, agentName, 'error');
|
|
221
|
+
callbacks.onAgentError(roomId, agentName, errorMsg);
|
|
222
|
+
roomsDb.addMessage({
|
|
223
|
+
room_id: roomId,
|
|
224
|
+
role: 'system',
|
|
225
|
+
agent_name: agentName,
|
|
226
|
+
content: `Error: ${errorMsg}`,
|
|
227
|
+
round,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Server as SocketIOServer } from 'socket.io';
|
|
2
|
+
import type { Server as HttpServer } from 'http';
|
|
3
|
+
export declare function setupGroupChatSocketIO(httpServer: HttpServer): SocketIOServer;
|
|
4
|
+
export declare function getGroupChatIO(): SocketIOServer | null;
|
|
5
|
+
export declare function disconnectGroupChatIO(): void;
|