hermes-web-ui 0.3.7 → 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 (85) hide show
  1. package/README.md +28 -12
  2. package/dist/client/assets/{Add-Cc7cgBoB.js → Add-BChxDDdy.js} +1 -1
  3. package/dist/client/assets/{Button-EoeZgIFH.js → Button-uvjCWO-i.js} +1 -1
  4. package/dist/client/assets/{ChannelsView-Bfbq3w7n.js → ChannelsView-D3a0g0rb.js} +1 -1
  5. package/dist/client/assets/{ChatView-Vfi_jEpI.css → ChatView-DI3XN8vz.css} +1 -1
  6. package/dist/client/assets/ChatView-T8LH7dwU.js +127 -0
  7. package/dist/client/assets/{Close-CKHcXisf.js → Close-DbXijZpL.js} +1 -1
  8. package/dist/client/assets/{FormItem-CvZvjrtr.js → FormItem-BRiLD3TC.js} +1 -1
  9. package/dist/client/assets/{GatewaysView-Dp4-TFPE.js → GatewaysView-rfzh1nqy.js} +1 -1
  10. package/dist/client/assets/{Input-Bk7XdoG-.js → Input-CjlUbV0M.js} +1 -1
  11. package/dist/client/assets/{InputNumber-Dn0EHi-K.js → InputNumber-CPEjoOAv.js} +1 -1
  12. package/dist/client/assets/{JobsView-D4JN73Zz.js → JobsView-D6AFr9Xe.js} +2 -2
  13. package/dist/client/assets/{LoginView--hy5CI5O.js → LoginView-DJB_TSHN.js} +1 -1
  14. package/dist/client/assets/{LogsView-Dz2ZeYad.js → LogsView-Ul8pAp42.js} +1 -1
  15. package/dist/client/assets/{MarkdownRenderer-BbedVxvo.js → MarkdownRenderer-CY7d2L7Z.js} +1 -1
  16. package/dist/client/assets/{MemoryView-D2EHM35l.js → MemoryView-BR5Dl_fa.js} +1 -1
  17. package/dist/client/assets/{Modal-C5-Iw50K.js → Modal-DDUFP8vh.js} +1 -1
  18. package/dist/client/assets/{ModelsView-CtrRf4vK.js → ModelsView-DAi9Jdmk.js} +1 -1
  19. package/dist/client/assets/{Popconfirm-C-M2anVL.js → Popconfirm-Cnb_uQVo.js} +1 -1
  20. package/dist/client/assets/{Popover-mIRPCy7U.js → Popover-DiVI0l6E.js} +1 -1
  21. package/dist/client/assets/{ProfilesView-Dy9PivgB.js → ProfilesView-BB0Zrfhi.js} +1 -1
  22. package/dist/client/assets/{Select-uZBC8HC2.js → Select-BqUA1wxE.js} +1 -1
  23. package/dist/client/assets/{SettingRow-D9R65bDj.js → SettingRow-CK0bHtaz.js} +1 -1
  24. package/dist/client/assets/{SettingsView-DWEEXqSY.js → SettingsView-BKKk44FG.js} +1 -1
  25. package/dist/client/assets/{SkillsView-CdZSRy9_.js → SkillsView-whSlyc23.js} +1 -1
  26. package/dist/client/assets/{Spin-ChbFBUOD.js → Spin-DwHJdgNz.js} +1 -1
  27. package/dist/client/assets/{Suffix-DgzfIwzx.js → Suffix-D6x-7akR.js} +1 -1
  28. package/dist/client/assets/{Switch--HhY1uSh.js → Switch-BvHRSSqt.js} +1 -1
  29. package/dist/client/assets/{Tag-B2zrHMmZ.js → Tag-BMMlXaEi.js} +1 -1
  30. package/dist/client/assets/{TerminalView-BwfnH803.js → TerminalView-el6o2Q0a.js} +1 -1
  31. package/dist/client/assets/{Tooltip-9tdvSKGi.js → Tooltip-BM4wl764.js} +1 -1
  32. package/dist/client/assets/{UsageView-zL3a7F86.js → UsageView-DQ_YKoEV.js} +1 -1
  33. package/dist/client/assets/{Warning-CXXqHzLa.js → Warning-CEC7rgvY.js} +1 -1
  34. package/dist/client/assets/{_plugin-vue_export-helper-Cnn0Z73x.js → _plugin-vue_export-helper-DgUZPfuZ.js} +1 -1
  35. package/dist/client/assets/app-DPUhLGXq.js +1 -0
  36. package/dist/client/assets/{app-BMobzABI.js → app-DUt8TNq3.js} +1 -1
  37. package/dist/client/assets/{browser-CQRjhbaB.js → browser-vxCOMmsq.js} +1 -1
  38. package/dist/client/assets/chat-i_Ge_Lfr.js +6 -0
  39. package/dist/client/assets/composables-jrQPIjcq.js +1 -0
  40. package/dist/client/assets/{fade-in.cssr-lwO9nLky.js → fade-in.cssr-DVg2CkO3.js} +1 -1
  41. package/dist/client/assets/{index-Tg6M43Om.js → index-COwJ2oY0.js} +1 -1
  42. package/dist/client/assets/{jobs-Z2HS0j2d.js → jobs-Czr1RcSG.js} +1 -1
  43. package/dist/client/assets/{light-DgIst23O.js → light-3rSjNeC-.js} +1 -1
  44. package/dist/client/assets/{light-oE8MEiWL.js → light-CKDlpgGU.js} +1 -1
  45. package/dist/client/assets/{light-Dx6qj2pM.js → light-CiIDFs7y.js} +1 -1
  46. package/dist/client/assets/{light-DZ0Ns16h.js → light-CoJqT8Vu.js} +1 -1
  47. package/dist/client/assets/{light-CjCy-Dkn.js → light-DPRJ1OEN.js} +1 -1
  48. package/dist/client/assets/{light-DzpNsLai.js → light-vTpJevRf.js} +1 -1
  49. package/dist/client/assets/{models-DLQiHB7r.js → models-BzEeJuoO.js} +1 -1
  50. package/dist/client/assets/{pinia-Dp_b1vdW.js → pinia-BoNLlsLy.js} +1 -1
  51. package/dist/client/assets/{profiles-CNTHYFZE.js → profiles-B-DFTmc2.js} +1 -1
  52. package/dist/client/assets/{router-Dj-Nmg7q.js → router-HHMeDEaP.js} +2 -2
  53. package/dist/client/assets/{sessions-C0kvgvBm.js → sessions-BmxS_BoH.js} +1 -1
  54. package/dist/client/assets/{skills-G7EoEvdS.js → skills-Be8Mzr1r.js} +1 -1
  55. package/dist/client/assets/{use-message-BgToAqhv.js → use-message-DBTY4945.js} +1 -1
  56. package/dist/client/assets/{useTheme-BUShiwRu.js → useTheme-D58Cg7k2.js} +1 -1
  57. package/dist/client/index.html +27 -27
  58. package/dist/server/index.js +19 -194
  59. package/dist/server/routes/health.d.ts +4 -0
  60. package/dist/server/routes/health.js +109 -0
  61. package/dist/server/routes/hermes/group-chat.d.ts +4 -0
  62. package/dist/server/routes/hermes/group-chat.js +146 -0
  63. package/dist/server/routes/update.d.ts +2 -0
  64. package/dist/server/routes/update.js +69 -0
  65. package/dist/server/routes/upload.js +41 -11
  66. package/dist/server/services/auth.js +1 -1
  67. package/dist/server/services/gateway-bootstrap.d.ts +2 -0
  68. package/dist/server/services/gateway-bootstrap.js +51 -0
  69. package/dist/server/services/group-chat/coordinator.d.ts +14 -0
  70. package/dist/server/services/group-chat/coordinator.js +230 -0
  71. package/dist/server/services/group-chat/index.d.ts +5 -0
  72. package/dist/server/services/group-chat/index.js +115 -0
  73. package/dist/server/services/group-chat/rooms-db.d.ts +56 -0
  74. package/dist/server/services/group-chat/rooms-db.js +199 -0
  75. package/dist/server/services/hermes/group-chat/agent-clients.d.ts +133 -0
  76. package/dist/server/services/hermes/group-chat/agent-clients.js +364 -0
  77. package/dist/server/services/hermes/group-chat/index.d.ts +72 -0
  78. package/dist/server/services/hermes/group-chat/index.js +307 -0
  79. package/dist/server/services/shutdown.d.ts +1 -0
  80. package/dist/server/services/shutdown.js +37 -0
  81. package/package.json +1 -1
  82. package/dist/client/assets/ChatView-CDdyTo72.js +0 -127
  83. package/dist/client/assets/app-Bqu9Uz-1.js +0 -1
  84. package/dist/client/assets/chat-BIdq6ZXF.js +0 -6
  85. package/dist/client/assets/composables-ClIU-Ad1.js +0 -1
@@ -1 +1 @@
1
- import{o as e}from"./router-Dj-Nmg7q.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
+ import{o as e}from"./router-HHMeDEaP.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-Dj-Nmg7q.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
+ import{o as e}from"./router-HHMeDEaP.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-Dj-Nmg7q.js";import{$ as t,Y as n}from"./browser-CQRjhbaB.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
+ import{F as e}from"./router-HHMeDEaP.js";import{$ as t,Y as n}from"./browser-vxCOMmsq.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-Dj-Nmg7q.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};
1
+ import{X as e,ct as t}from"./router-HHMeDEaP.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};
@@ -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-Tg6M43Om.js"></script>
10
- <link rel="modulepreload" crossorigin href="/assets/router-Dj-Nmg7q.js">
11
- <link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-Cnn0Z73x.js">
12
- <link rel="modulepreload" crossorigin href="/assets/browser-CQRjhbaB.js">
13
- <link rel="modulepreload" crossorigin href="/assets/fade-in.cssr-lwO9nLky.js">
14
- <link rel="modulepreload" crossorigin href="/assets/Suffix-DgzfIwzx.js">
15
- <link rel="modulepreload" crossorigin href="/assets/Close-CKHcXisf.js">
16
- <link rel="modulepreload" crossorigin href="/assets/Popover-mIRPCy7U.js">
17
- <link rel="modulepreload" crossorigin href="/assets/Button-EoeZgIFH.js">
18
- <link rel="modulepreload" crossorigin href="/assets/Tag-B2zrHMmZ.js">
9
+ <script type="module" crossorigin src="/assets/index-COwJ2oY0.js"></script>
10
+ <link rel="modulepreload" crossorigin href="/assets/router-HHMeDEaP.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-DgUZPfuZ.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/browser-vxCOMmsq.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/fade-in.cssr-DVg2CkO3.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/Suffix-D6x-7akR.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/Close-DbXijZpL.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/Popover-DiVI0l6E.js">
17
+ <link rel="modulepreload" crossorigin href="/assets/Button-uvjCWO-i.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/Tag-BMMlXaEi.js">
19
19
  <link rel="modulepreload" crossorigin href="/assets/create-5zWq3BEB.js">
20
- <link rel="modulepreload" crossorigin href="/assets/Select-uZBC8HC2.js">
21
- <link rel="modulepreload" crossorigin href="/assets/Warning-CXXqHzLa.js">
22
- <link rel="modulepreload" crossorigin href="/assets/Modal-C5-Iw50K.js">
23
- <link rel="modulepreload" crossorigin href="/assets/Input-Bk7XdoG-.js">
24
- <link rel="modulepreload" crossorigin href="/assets/light-Dx6qj2pM.js">
20
+ <link rel="modulepreload" crossorigin href="/assets/Select-BqUA1wxE.js">
21
+ <link rel="modulepreload" crossorigin href="/assets/Warning-CEC7rgvY.js">
22
+ <link rel="modulepreload" crossorigin href="/assets/Modal-DDUFP8vh.js">
23
+ <link rel="modulepreload" crossorigin href="/assets/Input-CjlUbV0M.js">
24
+ <link rel="modulepreload" crossorigin href="/assets/light-CiIDFs7y.js">
25
25
  <link rel="modulepreload" crossorigin href="/assets/omit-1BRB6K75.js">
26
- <link rel="modulepreload" crossorigin href="/assets/pinia-Dp_b1vdW.js">
27
- <link rel="modulepreload" crossorigin href="/assets/profiles-CNTHYFZE.js">
28
- <link rel="modulepreload" crossorigin href="/assets/sessions-C0kvgvBm.js">
29
- <link rel="modulepreload" crossorigin href="/assets/app-BMobzABI.js">
30
- <link rel="modulepreload" crossorigin href="/assets/chat-BIdq6ZXF.js">
31
- <link rel="modulepreload" crossorigin href="/assets/light-DZ0Ns16h.js">
32
- <link rel="modulepreload" crossorigin href="/assets/use-message-BgToAqhv.js">
33
- <link rel="modulepreload" crossorigin href="/assets/light-DzpNsLai.js">
34
- <link rel="modulepreload" crossorigin href="/assets/light-oE8MEiWL.js">
26
+ <link rel="modulepreload" crossorigin href="/assets/pinia-BoNLlsLy.js">
27
+ <link rel="modulepreload" crossorigin href="/assets/profiles-B-DFTmc2.js">
28
+ <link rel="modulepreload" crossorigin href="/assets/sessions-BmxS_BoH.js">
29
+ <link rel="modulepreload" crossorigin href="/assets/app-DUt8TNq3.js">
30
+ <link rel="modulepreload" crossorigin href="/assets/chat-i_Ge_Lfr.js">
31
+ <link rel="modulepreload" crossorigin href="/assets/light-CoJqT8Vu.js">
32
+ <link rel="modulepreload" crossorigin href="/assets/use-message-DBTY4945.js">
33
+ <link rel="modulepreload" crossorigin href="/assets/light-vTpJevRf.js">
34
+ <link rel="modulepreload" crossorigin href="/assets/light-CKDlpgGU.js">
35
35
  <link rel="modulepreload" crossorigin href="/assets/_common-Yp55QE79.js">
36
- <link rel="modulepreload" crossorigin href="/assets/light-CjCy-Dkn.js">
37
- <link rel="modulepreload" crossorigin href="/assets/light-DgIst23O.js">
38
- <link rel="modulepreload" crossorigin href="/assets/useTheme-BUShiwRu.js">
36
+ <link rel="modulepreload" crossorigin href="/assets/light-DPRJ1OEN.js">
37
+ <link rel="modulepreload" crossorigin href="/assets/light-3rSjNeC-.js">
38
+ <link rel="modulepreload" crossorigin href="/assets/useTheme-D58Cg7k2.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>
@@ -1,37 +1,4 @@
1
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
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
@@ -42,125 +9,41 @@ const cors_1 = __importDefault(require("@koa/cors"));
42
9
  const bodyparser_1 = __importDefault(require("@koa/bodyparser"));
43
10
  const koa_static_1 = __importDefault(require("koa-static"));
44
11
  const koa_send_1 = __importDefault(require("koa-send"));
12
+ const os_1 = __importDefault(require("os"));
45
13
  const path_1 = require("path");
46
14
  const promises_1 = require("fs/promises");
47
- const fs_1 = require("fs");
48
15
  const config_1 = require("./config");
49
16
  const hermes_1 = require("./routes/hermes");
50
17
  const upload_1 = require("./routes/upload");
51
18
  const webhook_1 = require("./routes/webhook");
52
- const hermesCli = __importStar(require("./services/hermes/hermes-cli"));
19
+ const update_1 = require("./routes/update");
20
+ const health_1 = require("./routes/health");
53
21
  const auth_1 = require("./services/auth");
54
- function getLocalVersion() {
55
- // production: dist/server → ../../package.json
56
- // dev: packages/server/src → ../../../package.json
57
- const candidates = [
58
- (0, path_1.resolve)(__dirname, '../../package.json'),
59
- (0, path_1.resolve)(__dirname, '../../../package.json'),
60
- ];
61
- for (const p of candidates) {
62
- try {
63
- return JSON.parse((0, fs_1.readFileSync)(p, 'utf-8')).version;
64
- }
65
- catch { }
66
- }
67
- return '0.0.0';
68
- }
69
- const LOCAL_VERSION = getLocalVersion();
70
- let cachedLatestVersion = '';
71
- async function checkLatestVersion() {
72
- try {
73
- const res = await fetch('https://registry.npmjs.org/hermes-web-ui/latest', {
74
- signal: AbortSignal.timeout(5000),
75
- });
76
- if (res.ok) {
77
- const data = await res.json();
78
- const latest = data.version || '';
79
- if (latest && latest !== cachedLatestVersion) {
80
- cachedLatestVersion = latest;
81
- if (latest !== LOCAL_VERSION) {
82
- console.log(`⬆ New version available: v${LOCAL_VERSION} → v${latest}`);
83
- }
84
- }
85
- }
86
- }
87
- catch { }
88
- }
89
- const app = new koa_1.default();
90
- const { restartGateway, startGateway, startGatewayBackground, getVersion } = hermesCli;
22
+ const gateway_bootstrap_1 = require("./services/gateway-bootstrap");
23
+ const shutdown_1 = require("./services/shutdown");
91
24
  let server = null;
92
- let isShuttingDown = false;
93
- // 👉 如果你有子进程,一定要存
94
- let gatewayPid = null;
95
- let gatewayManager = null;
96
25
  async function bootstrap() {
97
26
  await (0, promises_1.mkdir)(config_1.config.uploadDir, { recursive: true });
98
27
  await (0, promises_1.mkdir)(config_1.config.dataDir, { recursive: true });
99
- // Auth (after mkdir so data dir exists)
100
28
  const authToken = await (0, auth_1.getToken)();
29
+ const app = new koa_1.default();
101
30
  if (authToken) {
102
31
  app.use(await (0, auth_1.authMiddleware)(authToken));
103
32
  console.log(`🔐 Auth enabled — token: ${authToken}`);
104
33
  }
105
- await initGatewayManager();
34
+ await (0, gateway_bootstrap_1.initGatewayManager)();
106
35
  app.use((0, cors_1.default)({ origin: config_1.config.corsOrigins }));
107
36
  app.use((0, bodyparser_1.default)());
37
+ // Shared routes (no agent prefix)
108
38
  app.use(webhook_1.webhookRoutes.routes());
109
39
  app.use(upload_1.uploadRoutes.routes());
110
- // update (must be before hermesRoutes which includes proxy routes)
111
- app.use(async (ctx, next) => {
112
- if (ctx.path === '/api/hermes/update' && ctx.method === 'POST') {
113
- const isWin = process.platform === 'win32';
114
- const cmd = isWin
115
- ? 'cmd /c hermes-web-ui update'
116
- : 'hermes-web-ui update';
117
- try {
118
- const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
119
- const output = execSync(cmd, {
120
- encoding: 'utf-8',
121
- timeout: 120000,
122
- stdio: ['pipe', 'pipe', 'pipe'],
123
- });
124
- ctx.body = { success: true, message: output.trim() };
125
- }
126
- catch (err) {
127
- ctx.status = 500;
128
- ctx.body = { success: false, message: err.stderr || err.message };
129
- }
130
- return;
131
- }
132
- await next();
133
- });
40
+ app.use(update_1.updateRoutes.routes());
41
+ // Hermes routes (must be after update — proxy catch-all matches everything)
134
42
  app.use(hermes_1.hermesRoutes.routes());
135
43
  app.use(hermes_1.proxyMiddleware);
136
- // health
137
- app.use(async (ctx, next) => {
138
- if (ctx.path === '/health') {
139
- const raw = await getVersion();
140
- const hermesVersion = raw.split('\n')[0].replace('Hermes Agent ', '') || '';
141
- let gatewayOk = false;
142
- try {
143
- const upstream = gatewayManager?.getUpstream() || config_1.config.upstream;
144
- const res = await fetch(`${upstream.replace(/\/$/, '')}/health`, {
145
- signal: AbortSignal.timeout(5000),
146
- });
147
- gatewayOk = res.ok;
148
- }
149
- catch { }
150
- ctx.body = {
151
- status: gatewayOk ? 'ok' : 'error',
152
- platform: 'hermes-agent',
153
- version: hermesVersion,
154
- gateway: gatewayOk ? 'running' : 'stopped',
155
- webui_version: LOCAL_VERSION,
156
- webui_latest: cachedLatestVersion,
157
- webui_update_available: cachedLatestVersion && cachedLatestVersion !== LOCAL_VERSION,
158
- };
159
- return;
160
- }
161
- await next();
162
- });
163
- // SPA
44
+ // Health check
45
+ app.use(health_1.healthRoutes.routes());
46
+ // SPA fallback
164
47
  const distDir = (0, path_1.resolve)(__dirname, '..', 'client');
165
48
  app.use((0, koa_static_1.default)(distDir));
166
49
  app.use(async (ctx) => {
@@ -171,77 +54,19 @@ async function bootstrap() {
171
54
  await (0, koa_send_1.default)(ctx, 'index.html', { root: distDir });
172
55
  }
173
56
  });
174
- // 🚀 启动服务
57
+ // Start server
175
58
  server = app.listen(config_1.config.port, '0.0.0.0');
176
- // Terminal WebSocket (must be after server is created)
177
59
  (0, hermes_1.setupTerminalWebSocket)(server);
178
60
  server.on('listening', () => {
179
- console.log(`➜ Server: http://localhost:${config_1.config.port}`);
61
+ const interfaces = os_1.default.networkInterfaces();
62
+ const localIp = Object.values(interfaces).flat().find(i => i?.family === 'IPv4' && !i?.internal)?.address || 'localhost';
63
+ console.log(`➜ Server: http://localhost:${config_1.config.port} (LAN: http://${localIp}:${config_1.config.port})`);
180
64
  console.log(`➜ Upstream: ${config_1.config.upstream}`);
181
65
  });
182
66
  server.on('error', (err) => {
183
67
  console.error('Server error:', err.message);
184
68
  });
185
- // 👇 绑定退出信号
186
- bindShutdown();
187
- // Check for updates every 4 hours
188
- checkLatestVersion();
189
- setInterval(checkLatestVersion, 4 * 60 * 60 * 1000);
190
- }
191
- // ============================
192
- // ✅ 统一关闭逻辑(核心)
193
- // ============================
194
- function bindShutdown() {
195
- const shutdown = async (signal) => {
196
- if (isShuttingDown)
197
- return;
198
- isShuttingDown = true;
199
- console.log(`\n[${signal}] shutting down...`);
200
- try {
201
- // ✅ 1. 关闭 HTTP server
202
- if (server) {
203
- await new Promise((resolve) => {
204
- server.close(() => {
205
- console.log('✓ http server closed');
206
- resolve();
207
- });
208
- });
209
- }
210
- // gateway 是系统服务,不随 dev server 退出而停止
211
- }
212
- catch (err) {
213
- console.error('shutdown error:', err);
214
- }
215
- process.exit(0);
216
- };
217
- // 👉 nodemon 专用(必须 once)
218
- process.once('SIGUSR2', shutdown);
219
- // 👉 正常退出
220
- process.on('SIGINT', shutdown);
221
- process.on('SIGTERM', shutdown);
222
- // 👉 防止异常退出没处理
223
- process.on('uncaughtException', (err) => {
224
- console.error('uncaughtException:', err);
225
- shutdown('uncaughtException');
226
- });
227
- process.on('unhandledRejection', (err) => {
228
- console.error('unhandledRejection:', err);
229
- shutdown('unhandledRejection');
230
- });
231
- }
232
- // ============================
233
- // Gateway Manager
234
- // ============================
235
- async function initGatewayManager() {
236
- const { GatewayManager } = await Promise.resolve().then(() => __importStar(require('./services/hermes/gateway-manager')));
237
- const { getActiveProfileName } = await Promise.resolve().then(() => __importStar(require('./services/hermes/hermes-profile')));
238
- const { setGatewayManager } = await Promise.resolve().then(() => __importStar(require('./routes/hermes/gateways')));
239
- const activeProfile = getActiveProfileName();
240
- gatewayManager = new GatewayManager(activeProfile);
241
- setGatewayManager(gatewayManager);
242
- // Detect all running gateways
243
- await gatewayManager.detectAllOnStartup();
244
- // Start all gateways that aren't running
245
- await gatewayManager.startAll();
69
+ (0, shutdown_1.bindShutdown)(server);
70
+ (0, health_1.startVersionCheck)();
246
71
  }
247
72
  bootstrap();
@@ -0,0 +1,4 @@
1
+ import Router from '@koa/router';
2
+ export declare function checkLatestVersion(): Promise<void>;
3
+ export declare function startVersionCheck(): void;
4
+ export declare const healthRoutes: Router<import("koa").DefaultState, import("koa").DefaultContext>;
@@ -0,0 +1,109 @@
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.healthRoutes = void 0;
40
+ exports.checkLatestVersion = checkLatestVersion;
41
+ exports.startVersionCheck = startVersionCheck;
42
+ const router_1 = __importDefault(require("@koa/router"));
43
+ const path_1 = require("path");
44
+ const fs_1 = require("fs");
45
+ const gateways_1 = require("./hermes/gateways");
46
+ const hermesCli = __importStar(require("../services/hermes/hermes-cli"));
47
+ const config_1 = require("../config");
48
+ function getLocalVersion() {
49
+ const candidates = [
50
+ (0, path_1.resolve)(__dirname, '../../../package.json'),
51
+ (0, path_1.resolve)(__dirname, '../../../../package.json'),
52
+ ];
53
+ for (const p of candidates) {
54
+ try {
55
+ return JSON.parse((0, fs_1.readFileSync)(p, 'utf-8')).version;
56
+ }
57
+ catch { }
58
+ }
59
+ return '0.0.0';
60
+ }
61
+ const LOCAL_VERSION = getLocalVersion();
62
+ let cachedLatestVersion = '';
63
+ async function checkLatestVersion() {
64
+ try {
65
+ const res = await fetch('https://registry.npmjs.org/hermes-web-ui/latest', {
66
+ signal: AbortSignal.timeout(5000),
67
+ headers: { 'Cache-Control': 'no-cache' },
68
+ });
69
+ if (res.ok) {
70
+ const data = await res.json();
71
+ const latest = data.version || '';
72
+ if (latest && latest !== cachedLatestVersion) {
73
+ cachedLatestVersion = latest;
74
+ if (latest !== LOCAL_VERSION) {
75
+ console.log(`⬆ New version available: v${LOCAL_VERSION} → v${latest}`);
76
+ }
77
+ }
78
+ }
79
+ }
80
+ catch { }
81
+ }
82
+ function startVersionCheck() {
83
+ checkLatestVersion();
84
+ setInterval(checkLatestVersion, 60 * 60 * 1000);
85
+ }
86
+ exports.healthRoutes = new router_1.default();
87
+ exports.healthRoutes.get('/health', async (ctx) => {
88
+ const raw = await hermesCli.getVersion();
89
+ const hermesVersion = raw.split('\n')[0].replace('Hermes Agent ', '') || '';
90
+ let gatewayOk = false;
91
+ try {
92
+ const mgr = (0, gateways_1.getGatewayManager)();
93
+ const upstream = mgr?.getUpstream() || config_1.config.upstream;
94
+ const res = await fetch(`${upstream.replace(/\/$/, '')}/health`, {
95
+ signal: AbortSignal.timeout(5000),
96
+ });
97
+ gatewayOk = res.ok;
98
+ }
99
+ catch { }
100
+ ctx.body = {
101
+ status: gatewayOk ? 'ok' : 'error',
102
+ platform: 'hermes-agent',
103
+ version: hermesVersion,
104
+ gateway: gatewayOk ? 'running' : 'stopped',
105
+ webui_version: LOCAL_VERSION,
106
+ webui_latest: cachedLatestVersion,
107
+ webui_update_available: cachedLatestVersion && cachedLatestVersion !== LOCAL_VERSION,
108
+ };
109
+ });
@@ -0,0 +1,4 @@
1
+ import Router from '@koa/router';
2
+ import type { GroupChatServer } from '../../services/hermes/group-chat';
3
+ export declare const groupChatRoutes: Router<import("koa").DefaultState, import("koa").DefaultContext>;
4
+ export declare function setGroupChatServer(server: GroupChatServer): void;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.groupChatRoutes = void 0;
7
+ exports.setGroupChatServer = setGroupChatServer;
8
+ const router_1 = __importDefault(require("@koa/router"));
9
+ exports.groupChatRoutes = new router_1.default();
10
+ let chatServer = null;
11
+ function setGroupChatServer(server) {
12
+ chatServer = server;
13
+ }
14
+ function generateId() {
15
+ return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
16
+ }
17
+ // Create room
18
+ exports.groupChatRoutes.post('/api/hermes/group-chat/rooms', async (ctx) => {
19
+ if (!chatServer) {
20
+ ctx.status = 503;
21
+ ctx.body = { error: 'Group chat not initialized' };
22
+ return;
23
+ }
24
+ const { name, inviteCode, agents } = ctx.request.body;
25
+ if (!name || !inviteCode) {
26
+ ctx.status = 400;
27
+ ctx.body = { error: 'name and inviteCode are required' };
28
+ return;
29
+ }
30
+ const roomId = generateId();
31
+ const storage = chatServer.getStorage();
32
+ storage.saveRoom(roomId, name, inviteCode);
33
+ // Save agents to DB and auto-connect via Socket.IO
34
+ const addedAgents = [];
35
+ for (const a of agents || []) {
36
+ const agentId = generateId();
37
+ const agent = storage.addRoomAgent(roomId, agentId, a.profile, a.name || a.profile, a.description || '', a.invited ? 1 : 0);
38
+ addedAgents.push(agent);
39
+ try {
40
+ const client = await chatServer.agentClients.createAgent({
41
+ profile: agent.profile,
42
+ name: agent.name,
43
+ description: agent.description,
44
+ invited: agent.invited,
45
+ });
46
+ await chatServer.agentClients.addAgentToRoom(roomId, client);
47
+ }
48
+ catch (err) {
49
+ console.error(`[GroupChat] Failed to connect agent ${a.profile} to room ${roomId}: ${err.message}`);
50
+ }
51
+ }
52
+ const room = storage.getRoom(roomId);
53
+ ctx.body = { room, agents: addedAgents };
54
+ });
55
+ // List rooms
56
+ exports.groupChatRoutes.get('/api/hermes/group-chat/rooms', async (ctx) => {
57
+ if (!chatServer) {
58
+ ctx.status = 503;
59
+ ctx.body = { error: 'Group chat not initialized' };
60
+ return;
61
+ }
62
+ const rooms = chatServer.getStorage().getAllRooms();
63
+ ctx.body = { rooms };
64
+ });
65
+ // Get room by invite code
66
+ exports.groupChatRoutes.get('/api/hermes/group-chat/rooms/join/:code', async (ctx) => {
67
+ if (!chatServer) {
68
+ ctx.status = 503;
69
+ ctx.body = { error: 'Group chat not initialized' };
70
+ return;
71
+ }
72
+ const room = chatServer.getStorage().getRoomByInviteCode(ctx.params.code);
73
+ if (!room) {
74
+ ctx.status = 404;
75
+ ctx.body = { error: 'Room not found' };
76
+ return;
77
+ }
78
+ ctx.body = { room };
79
+ });
80
+ // Update room invite code
81
+ exports.groupChatRoutes.put('/api/hermes/group-chat/rooms/:roomId/invite-code', async (ctx) => {
82
+ if (!chatServer) {
83
+ ctx.status = 503;
84
+ ctx.body = { error: 'Group chat not initialized' };
85
+ return;
86
+ }
87
+ const { inviteCode } = ctx.request.body;
88
+ if (!inviteCode) {
89
+ ctx.status = 400;
90
+ ctx.body = { error: 'inviteCode is required' };
91
+ return;
92
+ }
93
+ chatServer.getStorage().updateRoomInviteCode(ctx.params.roomId, inviteCode);
94
+ ctx.body = { success: true };
95
+ });
96
+ // Add agent to room
97
+ exports.groupChatRoutes.post('/api/hermes/group-chat/rooms/:roomId/agents', async (ctx) => {
98
+ if (!chatServer) {
99
+ ctx.status = 503;
100
+ ctx.body = { error: 'Group chat not initialized' };
101
+ return;
102
+ }
103
+ const { profile, name, description, invited } = ctx.request.body;
104
+ if (!profile) {
105
+ ctx.status = 400;
106
+ ctx.body = { error: 'profile is required' };
107
+ return;
108
+ }
109
+ const agentId = generateId();
110
+ const agent = chatServer.getStorage().addRoomAgent(ctx.params.roomId, agentId, profile, name || profile, description || '', invited ? 1 : 0);
111
+ // Auto-connect agent via Socket.IO
112
+ try {
113
+ const client = await chatServer.agentClients.createAgent({
114
+ profile: agent.profile,
115
+ name: agent.name,
116
+ description: agent.description,
117
+ invited: agent.invited,
118
+ });
119
+ await chatServer.agentClients.addAgentToRoom(ctx.params.roomId, client);
120
+ }
121
+ catch (err) {
122
+ console.error(`[GroupChat] Failed to connect agent ${profile} to room ${ctx.params.roomId}: ${err.message}`);
123
+ }
124
+ ctx.body = { agent };
125
+ });
126
+ // List agents in room
127
+ exports.groupChatRoutes.get('/api/hermes/group-chat/rooms/:roomId/agents', async (ctx) => {
128
+ if (!chatServer) {
129
+ ctx.status = 503;
130
+ ctx.body = { error: 'Group chat not initialized' };
131
+ return;
132
+ }
133
+ const agents = chatServer.getStorage().getRoomAgents(ctx.params.roomId);
134
+ ctx.body = { agents };
135
+ });
136
+ // Remove agent from room
137
+ exports.groupChatRoutes.delete('/api/hermes/group-chat/rooms/:roomId/agents/:agentId', async (ctx) => {
138
+ if (!chatServer) {
139
+ ctx.status = 503;
140
+ ctx.body = { error: 'Group chat not initialized' };
141
+ return;
142
+ }
143
+ chatServer.getStorage().removeRoomAgent(ctx.params.agentId);
144
+ chatServer.agentClients.removeAgentFromRoom(ctx.params.roomId, ctx.params.agentId);
145
+ ctx.body = { success: true };
146
+ });
@@ -0,0 +1,2 @@
1
+ import Router from '@koa/router';
2
+ export declare const updateRoutes: Router<import("koa").DefaultState, import("koa").DefaultContext>;