hermes-web-ui 0.3.4 → 0.3.6

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 (121) hide show
  1. package/LICENSE +21 -0
  2. package/dist/client/assets/{Add-DBcT5hzg.js → Add-CKf6ViXR.js} +1 -1
  3. package/dist/client/assets/{Button-R5s3092y.js → Button-CrrCCorI.js} +1 -1
  4. package/dist/client/assets/ChannelsView-CoREbYTy.css +1 -0
  5. package/dist/client/assets/ChannelsView-D4I7hhZO.js +1 -0
  6. package/dist/client/assets/ChatView-DxyBUK57.js +127 -0
  7. package/dist/client/assets/ChatView-Vfi_jEpI.css +1 -0
  8. package/dist/client/assets/Close-C9xwy-pW.js +45 -0
  9. package/dist/client/assets/{FormItem-ZuVh5lSE.js → FormItem-BgJdrTW0.js} +1 -1
  10. package/dist/client/assets/GatewaysView-Cib2JydO.js +1 -0
  11. package/dist/client/assets/GatewaysView-DtUN_4uh.css +1 -0
  12. package/dist/client/assets/Input-ChENEW-Z.js +234 -0
  13. package/dist/client/assets/{InputNumber-B6xJGyAP.js → InputNumber-Xd6HWSdp.js} +3 -3
  14. package/dist/client/assets/JobsView-C9-go5-X.css +1 -0
  15. package/dist/client/assets/{JobsView-BNGFt0kv.js → JobsView-SnToCbDd.js} +2 -2
  16. package/dist/client/assets/{LoginView-B-UpFAOr.js → LoginView-BZdmMnsf.js} +1 -1
  17. package/dist/client/assets/LoginView-DHPxG3eC.css +1 -0
  18. package/dist/client/assets/{LogsView-BMJ5FVwp.js → LogsView-DblvOJIg.js} +1 -1
  19. package/dist/client/assets/LogsView-VfBZmaM1.css +1 -0
  20. package/dist/client/assets/{MarkdownRenderer-C8-GFOw3.js → MarkdownRenderer-DJLVk7ei.js} +1 -1
  21. package/dist/client/assets/MarkdownRenderer-Dc9VWGMm.css +1 -0
  22. package/dist/client/assets/MemoryView-BZxpGTCE.css +1 -0
  23. package/dist/client/assets/{MemoryView-Rfsn9nzW.js → MemoryView-exXvRwCc.js} +1 -1
  24. package/dist/client/assets/{Modal-DCJNfzf6.js → Modal-B2zvXTrk.js} +6 -6
  25. package/dist/client/assets/{ModelsView-wE2P2H3O.js → ModelsView-DGs47Cj4.js} +1 -1
  26. package/dist/client/assets/ModelsView-jbgZP3YF.css +1 -0
  27. package/dist/client/assets/Popconfirm-BoZc0kKk.js +16 -0
  28. package/dist/client/assets/Popover-Cu52vG3D.js +117 -0
  29. package/dist/client/assets/ProfilesView-1BtKDO_6.css +1 -0
  30. package/dist/client/assets/ProfilesView-D0FY7Jwe.js +440 -0
  31. package/dist/client/assets/Select-BHc7u-Yf.js +340 -0
  32. package/dist/client/assets/SettingRow-Cr1VcUPz.css +1 -0
  33. package/dist/client/assets/{SettingRow-Bztj1Qvd.js → SettingRow-i-UXlco7.js} +1 -1
  34. package/dist/client/assets/SettingsView-BW6ctYG5.js +352 -0
  35. package/dist/client/assets/SettingsView-Dhr2wzAB.css +1 -0
  36. package/dist/client/assets/SkillsView-5K2WjAWZ.css +1 -0
  37. package/dist/client/assets/{SkillsView-DA7GSqMh.js → SkillsView-B5QBaAzi.js} +1 -1
  38. package/dist/client/assets/{Spin-UenY6DjU.js → Spin-DsNCRPk9.js} +3 -3
  39. package/dist/client/assets/Suffix-3xK0KZGt.js +90 -0
  40. package/dist/client/assets/{Switch-D4sdzrF1.js → Switch-Bf63XXgA.js} +2 -2
  41. package/dist/client/assets/{light-UKc15Qzy.js → Tag-Dmbj68Ki.js} +2 -2
  42. package/dist/client/assets/TerminalView-BOiVMGJZ.css +1 -0
  43. package/dist/client/assets/{TerminalView-bdOH27Rz.js → TerminalView-DrJHZ0qI.js} +16 -16
  44. package/dist/client/assets/{Tooltip-shahfZE0.js → Tooltip-CRbZNhG0.js} +1 -1
  45. package/dist/client/assets/{UsageView-LP-yDWfN.js → UsageView-DQ43JasX.js} +1 -1
  46. package/dist/client/assets/UsageView-wje1C97e.css +1 -0
  47. package/dist/client/assets/Warning-kBbRMAif.js +1 -0
  48. package/dist/client/assets/{_plugin-vue_export-helper-B5RrPTMt.js → _plugin-vue_export-helper-CnosYBkx.js} +1 -1
  49. package/dist/client/assets/app-BT9yU6N6.js +1 -0
  50. package/dist/client/assets/app-CjNVVG5x.js +1 -0
  51. package/dist/client/assets/{browser-Cnde4syl.js → browser-Djp4tkp3.js} +1 -1
  52. package/dist/client/assets/chat-DlC9S9DK.js +6 -0
  53. package/dist/client/assets/composables-DCA4Yga5.js +1 -0
  54. package/dist/client/assets/fade-in.cssr-CIVyTG6A.js +12 -0
  55. package/dist/client/assets/index-BEcRccNA.css +1 -0
  56. package/dist/client/assets/index-D12ukDT7.js +284 -0
  57. package/dist/client/assets/{jobs-Bg3B0qd3.js → jobs-CcVaCGMJ.js} +1 -1
  58. package/dist/client/assets/{light-CtcMXxnt.js → light-BF6E9z0k.js} +1 -1
  59. package/dist/client/assets/{light-B9EqymT1.js → light-BJ96fCLC.js} +1 -1
  60. package/dist/client/assets/{light-7AGE9udo.js → light-BPqyaxve.js} +1 -1
  61. package/dist/client/assets/{light-C_iMTPuX.js → light-CSp9-LhE.js} +1 -1
  62. package/dist/client/assets/light-D9G2GshF.js +1 -0
  63. package/dist/client/assets/light-KCEDTUGE.js +23 -0
  64. package/dist/client/assets/{pinia-BizF94YH.js → pinia-iHE5_ZXa.js} +1 -1
  65. package/dist/client/assets/profiles-CJCR84uQ.js +1 -0
  66. package/dist/client/assets/{router-BuVIua3w.js → router-C-NNJUuf.js} +2 -2
  67. package/dist/client/assets/{sessions-CpgDAUKL.js → sessions-C4bnNvzS.js} +1 -1
  68. package/dist/client/assets/{skills-BGUX0Zk7.js → skills-B4slZfeZ.js} +1 -1
  69. package/dist/client/assets/{use-message-DgahoF2f.js → use-message-BIpqgDet.js} +1 -1
  70. package/dist/client/assets/{useTheme-CRVFV8Ud.js → useTheme-B78N9tyz.js} +1 -1
  71. package/dist/client/index.html +29 -29
  72. package/dist/server/index.js +17 -79
  73. package/dist/server/routes/hermes/filesystem.js +3 -1
  74. package/dist/server/routes/hermes/gateways.d.ts +4 -0
  75. package/dist/server/routes/hermes/gateways.js +73 -0
  76. package/dist/server/routes/hermes/index.js +2 -0
  77. package/dist/server/routes/hermes/profiles.js +28 -84
  78. package/dist/server/routes/hermes/proxy-handler.js +17 -1
  79. package/dist/server/routes/hermes/sessions.js +9 -0
  80. package/dist/server/services/hermes/gateway-manager.d.ts +131 -0
  81. package/dist/server/services/hermes/gateway-manager.js +536 -0
  82. package/dist/server/services/hermes/sessions-db.d.ts +24 -0
  83. package/dist/server/services/hermes/sessions-db.js +147 -0
  84. package/package.json +1 -1
  85. package/dist/client/assets/ChannelsView-CH6386MX.js +0 -1
  86. package/dist/client/assets/ChannelsView-DS2AEk-a.css +0 -1
  87. package/dist/client/assets/ChatView-C_mCyb0n.js +0 -127
  88. package/dist/client/assets/ChatView-CtLv9X8G.css +0 -1
  89. package/dist/client/assets/Close-CbcMKiZ9.js +0 -45
  90. package/dist/client/assets/Input-CbOEVtEq.js +0 -234
  91. package/dist/client/assets/JobsView-CVx2Yv-y.css +0 -1
  92. package/dist/client/assets/LoginView-CA9w3oDH.css +0 -1
  93. package/dist/client/assets/LogsView-BG7Bro4j.css +0 -1
  94. package/dist/client/assets/MarkdownRenderer-Ct6cRT2D.css +0 -1
  95. package/dist/client/assets/MemoryView-DEyIgPS8.css +0 -1
  96. package/dist/client/assets/ModelsView-D1p_2trx.css +0 -1
  97. package/dist/client/assets/Popconfirm-pG5fyaNw.js +0 -16
  98. package/dist/client/assets/Popover-BcnZkOJ0.js +0 -117
  99. package/dist/client/assets/ProfilesView-CAOfb8ly.js +0 -440
  100. package/dist/client/assets/ProfilesView-Chffv0-D.css +0 -1
  101. package/dist/client/assets/Scrollbar-B7udJACg.js +0 -77
  102. package/dist/client/assets/Select-B6Xt21kv.js +0 -340
  103. package/dist/client/assets/SettingRow-CwidKv2Q.css +0 -1
  104. package/dist/client/assets/SettingsView-BIEQOPzq.css +0 -1
  105. package/dist/client/assets/SettingsView-Dwx8y_nj.js +0 -352
  106. package/dist/client/assets/SkillsView-D5bivF7Z.css +0 -1
  107. package/dist/client/assets/Suffix-DopnzIdr.js +0 -25
  108. package/dist/client/assets/TerminalView-DPdn9YA7.css +0 -1
  109. package/dist/client/assets/UsageView-CnADqqzf.css +0 -1
  110. package/dist/client/assets/Warning-DT2rnqpH.js +0 -1
  111. package/dist/client/assets/app-B3-jMgcz.js +0 -1
  112. package/dist/client/assets/app-Baqdn89g.js +0 -1
  113. package/dist/client/assets/chat-CCvtq7iz.js +0 -6
  114. package/dist/client/assets/composables-yAWv_nAD.js +0 -1
  115. package/dist/client/assets/fade-in-scale-up.cssr-7cCctcdO.js +0 -1
  116. package/dist/client/assets/index-JeutuTe0.css +0 -1
  117. package/dist/client/assets/index-vZMUiSyM.js +0 -284
  118. package/dist/client/assets/light-87-mk9Yl.js +0 -1
  119. package/dist/client/assets/profiles-DXTLdlvo.js +0 -23
  120. package/dist/client/assets/use-compitable-C1bGazqI.js +0 -1
  121. /package/dist/client/assets/{_common-DgdkN_d5.js → _common-Yp55QE79.js} +0 -0
@@ -1 +1 @@
1
- import{o as e}from"./router-BuVIua3w.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-C-NNJUuf.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-BuVIua3w.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-C-NNJUuf.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-BuVIua3w.js";import{$ as t,Y as n}from"./browser-Cnde4syl.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-C-NNJUuf.js";import{$ as t,Y as n}from"./browser-Djp4tkp3.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-BuVIua3w.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-C-NNJUuf.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,38 +6,38 @@
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-vZMUiSyM.js"></script>
10
- <link rel="modulepreload" crossorigin href="/assets/router-BuVIua3w.js">
11
- <link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-B5RrPTMt.js">
12
- <link rel="modulepreload" crossorigin href="/assets/browser-Cnde4syl.js">
13
- <link rel="modulepreload" crossorigin href="/assets/Scrollbar-B7udJACg.js">
14
- <link rel="modulepreload" crossorigin href="/assets/use-compitable-C1bGazqI.js">
15
- <link rel="modulepreload" crossorigin href="/assets/Popover-BcnZkOJ0.js">
16
- <link rel="modulepreload" crossorigin href="/assets/Close-CbcMKiZ9.js">
17
- <link rel="modulepreload" crossorigin href="/assets/Button-R5s3092y.js">
18
- <link rel="modulepreload" crossorigin href="/assets/Suffix-DopnzIdr.js">
19
- <link rel="modulepreload" crossorigin href="/assets/fade-in-scale-up.cssr-7cCctcdO.js">
20
- <link rel="modulepreload" crossorigin href="/assets/light-UKc15Qzy.js">
9
+ <script type="module" crossorigin src="/assets/index-D12ukDT7.js"></script>
10
+ <link rel="modulepreload" crossorigin href="/assets/router-C-NNJUuf.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-CnosYBkx.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/browser-Djp4tkp3.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/fade-in.cssr-CIVyTG6A.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/Suffix-3xK0KZGt.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/Close-C9xwy-pW.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/Popover-Cu52vG3D.js">
17
+ <link rel="modulepreload" crossorigin href="/assets/Button-CrrCCorI.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/Tag-Dmbj68Ki.js">
21
19
  <link rel="modulepreload" crossorigin href="/assets/create-5zWq3BEB.js">
22
- <link rel="modulepreload" crossorigin href="/assets/Select-B6Xt21kv.js">
23
- <link rel="modulepreload" crossorigin href="/assets/Warning-DT2rnqpH.js">
24
- <link rel="modulepreload" crossorigin href="/assets/Modal-DCJNfzf6.js">
25
- <link rel="modulepreload" crossorigin href="/assets/pinia-BizF94YH.js">
26
- <link rel="modulepreload" crossorigin href="/assets/profiles-DXTLdlvo.js">
20
+ <link rel="modulepreload" crossorigin href="/assets/Select-BHc7u-Yf.js">
21
+ <link rel="modulepreload" crossorigin href="/assets/Warning-kBbRMAif.js">
22
+ <link rel="modulepreload" crossorigin href="/assets/Modal-B2zvXTrk.js">
23
+ <link rel="modulepreload" crossorigin href="/assets/Input-ChENEW-Z.js">
24
+ <link rel="modulepreload" crossorigin href="/assets/light-KCEDTUGE.js">
27
25
  <link rel="modulepreload" crossorigin href="/assets/omit-1BRB6K75.js">
28
- <link rel="modulepreload" crossorigin href="/assets/sessions-CpgDAUKL.js">
29
- <link rel="modulepreload" crossorigin href="/assets/app-B3-jMgcz.js">
30
- <link rel="modulepreload" crossorigin href="/assets/chat-CCvtq7iz.js">
31
- <link rel="modulepreload" crossorigin href="/assets/light-B9EqymT1.js">
32
- <link rel="modulepreload" crossorigin href="/assets/light-87-mk9Yl.js">
33
- <link rel="modulepreload" crossorigin href="/assets/use-message-DgahoF2f.js">
34
- <link rel="modulepreload" crossorigin href="/assets/light-7AGE9udo.js">
35
- <link rel="modulepreload" crossorigin href="/assets/_common-DgdkN_d5.js">
36
- <link rel="modulepreload" crossorigin href="/assets/light-C_iMTPuX.js">
37
- <link rel="modulepreload" crossorigin href="/assets/light-CtcMXxnt.js">
38
- <link rel="modulepreload" crossorigin href="/assets/useTheme-CRVFV8Ud.js">
26
+ <link rel="modulepreload" crossorigin href="/assets/pinia-iHE5_ZXa.js">
27
+ <link rel="modulepreload" crossorigin href="/assets/profiles-CJCR84uQ.js">
28
+ <link rel="modulepreload" crossorigin href="/assets/sessions-C4bnNvzS.js">
29
+ <link rel="modulepreload" crossorigin href="/assets/app-BT9yU6N6.js">
30
+ <link rel="modulepreload" crossorigin href="/assets/chat-DlC9S9DK.js">
31
+ <link rel="modulepreload" crossorigin href="/assets/light-BJ96fCLC.js">
32
+ <link rel="modulepreload" crossorigin href="/assets/use-message-BIpqgDet.js">
33
+ <link rel="modulepreload" crossorigin href="/assets/light-BPqyaxve.js">
34
+ <link rel="modulepreload" crossorigin href="/assets/light-D9G2GshF.js">
35
+ <link rel="modulepreload" crossorigin href="/assets/_common-Yp55QE79.js">
36
+ <link rel="modulepreload" crossorigin href="/assets/light-CSp9-LhE.js">
37
+ <link rel="modulepreload" crossorigin href="/assets/light-BF6E9z0k.js">
38
+ <link rel="modulepreload" crossorigin href="/assets/useTheme-B78N9tyz.js">
39
39
  <link rel="modulepreload" crossorigin href="/assets/logo-Cd-t_oGE.js">
40
- <link rel="stylesheet" crossorigin href="/assets/index-JeutuTe0.css">
40
+ <link rel="stylesheet" crossorigin href="/assets/index-BEcRccNA.css">
41
41
  </head>
42
42
 
43
43
  <body>
@@ -92,6 +92,7 @@ let server = null;
92
92
  let isShuttingDown = false;
93
93
  // 👉 如果你有子进程,一定要存
94
94
  let gatewayPid = null;
95
+ let gatewayManager = null;
95
96
  async function bootstrap() {
96
97
  await (0, promises_1.mkdir)(config_1.config.uploadDir, { recursive: true });
97
98
  await (0, promises_1.mkdir)(config_1.config.dataDir, { recursive: true });
@@ -101,8 +102,7 @@ async function bootstrap() {
101
102
  app.use(await (0, auth_1.authMiddleware)(authToken));
102
103
  console.log(`🔐 Auth enabled — token: ${authToken}`);
103
104
  }
104
- await ensureApiServerConfig();
105
- await ensureGatewayRunning();
105
+ await initGatewayManager();
106
106
  app.use((0, cors_1.default)({ origin: config_1.config.corsOrigins }));
107
107
  app.use((0, bodyparser_1.default)());
108
108
  app.use(webhook_1.webhookRoutes.routes());
@@ -140,7 +140,8 @@ async function bootstrap() {
140
140
  const hermesVersion = raw.split('\n')[0].replace('Hermes Agent ', '') || '';
141
141
  let gatewayOk = false;
142
142
  try {
143
- const res = await fetch(`${config_1.config.upstream.replace(/\/$/, '')}/health`, {
143
+ const upstream = gatewayManager?.getUpstream() || config_1.config.upstream;
144
+ const res = await fetch(`${upstream.replace(/\/$/, '')}/health`, {
144
145
  signal: AbortSignal.timeout(5000),
145
146
  });
146
147
  gatewayOk = res.ok;
@@ -206,14 +207,7 @@ function bindShutdown() {
206
207
  });
207
208
  });
208
209
  }
209
- // 2. 关闭子进程(如果有)
210
- if (gatewayPid) {
211
- try {
212
- process.kill(gatewayPid);
213
- console.log(`✓ gateway process killed: ${gatewayPid}`);
214
- }
215
- catch { }
216
- }
210
+ // gateway 是系统服务,不随 dev server 退出而停止
217
211
  }
218
212
  catch (err) {
219
213
  console.error('shutdown error:', err);
@@ -236,74 +230,18 @@ function bindShutdown() {
236
230
  });
237
231
  }
238
232
  // ============================
239
- // 你的原逻辑(基本不动)
233
+ // Gateway Manager
240
234
  // ============================
241
- async function ensureApiServerConfig() {
242
- const { readFileSync, writeFileSync, existsSync, copyFileSync } = await Promise.resolve().then(() => __importStar(require('fs')));
243
- const yaml = (await Promise.resolve().then(() => __importStar(require('js-yaml')))).default;
244
- const { getActiveConfigPath } = await Promise.resolve().then(() => __importStar(require('./services/hermes/hermes-profile')));
245
- const configPath = getActiveConfigPath();
246
- const defaults = {
247
- enabled: true,
248
- host: '127.0.0.1',
249
- port: 8642,
250
- key: '',
251
- cors_origins: '*',
252
- };
253
- try {
254
- if (!existsSync(configPath)) {
255
- console.log('✗ config.yaml not found');
256
- return;
257
- }
258
- const content = readFileSync(configPath, 'utf-8');
259
- const cfg = yaml.load(content) || {};
260
- if (!cfg.platforms)
261
- cfg.platforms = {};
262
- if (!cfg.platforms.api_server)
263
- cfg.platforms.api_server = {};
264
- cfg.platforms.api_server = defaults;
265
- copyFileSync(configPath, configPath + '.bak');
266
- writeFileSync(configPath, yaml.dump(cfg), 'utf-8');
267
- await restartGateway();
268
- }
269
- catch (err) {
270
- console.error('config error:', err.message);
271
- }
272
- }
273
- async function ensureGatewayRunning() {
274
- const upstream = config_1.config.upstream.replace(/\/$/, '');
275
- const waitForGatewayReady = async (timeoutMs = 15000) => {
276
- const deadline = Date.now() + timeoutMs;
277
- while (Date.now() < deadline) {
278
- try {
279
- const res = await fetch(`${upstream}/health`, { signal: AbortSignal.timeout(2000) });
280
- if (res.ok)
281
- return true;
282
- }
283
- catch { }
284
- await new Promise(r => setTimeout(r, 300));
285
- }
286
- return false;
287
- };
288
- try {
289
- const res = await fetch(`${upstream}/health`, { signal: AbortSignal.timeout(5000) });
290
- if (res.ok)
291
- return;
292
- }
293
- catch { }
294
- console.log('⚠ Gateway not running, starting...');
295
- try {
296
- // 👉 关键:保存 PID
297
- gatewayPid = await startGatewayBackground();
298
- if (await waitForGatewayReady()) {
299
- console.log(`✓ Gateway started (PID: ${gatewayPid})`);
300
- }
301
- else {
302
- console.error('gateway start failed: timed out waiting for health');
303
- }
304
- }
305
- catch (err) {
306
- console.error('gateway start failed:', err.message);
307
- }
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();
308
246
  }
309
247
  bootstrap();
@@ -436,8 +436,10 @@ exports.fsRoutes.get('/api/hermes/available-models', async (ctx) => {
436
436
  const config = await readConfigYaml();
437
437
  const modelSection = config.model;
438
438
  let currentDefault = '';
439
+ let currentDefaultProvider = '';
439
440
  if (typeof modelSection === 'object' && modelSection !== null) {
440
441
  currentDefault = String(modelSection.default || '').trim();
442
+ currentDefaultProvider = String(modelSection.provider || '').trim();
441
443
  }
442
444
  else if (typeof modelSection === 'string') {
443
445
  currentDefault = modelSection.trim();
@@ -560,7 +562,7 @@ exports.fsRoutes.get('/api/hermes/available-models', async (ctx) => {
560
562
  ctx.body = fallback;
561
563
  return;
562
564
  }
563
- ctx.body = { default: currentDefault, groups: dedupedGroups };
565
+ ctx.body = { default: currentDefault, default_provider: currentDefaultProvider, groups: dedupedGroups };
564
566
  }
565
567
  catch (err) {
566
568
  ctx.status = 500;
@@ -0,0 +1,4 @@
1
+ import Router from '@koa/router';
2
+ export declare const gatewayRoutes: Router<import("koa").DefaultState, import("koa").DefaultContext>;
3
+ export declare function setGatewayManager(mgr: any): void;
4
+ export declare function getGatewayManager(): any;
@@ -0,0 +1,73 @@
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.gatewayRoutes = void 0;
7
+ exports.setGatewayManager = setGatewayManager;
8
+ exports.getGatewayManager = getGatewayManager;
9
+ const router_1 = __importDefault(require("@koa/router"));
10
+ exports.gatewayRoutes = new router_1.default();
11
+ // Get singleton instance — set during bootstrap
12
+ let manager = null;
13
+ function setGatewayManager(mgr) {
14
+ manager = mgr;
15
+ }
16
+ function getGatewayManager() {
17
+ return manager;
18
+ }
19
+ // List all gateway statuses
20
+ exports.gatewayRoutes.get('/api/hermes/gateways', async (ctx) => {
21
+ if (!manager) {
22
+ ctx.status = 503;
23
+ ctx.body = { error: 'GatewayManager not initialized' };
24
+ return;
25
+ }
26
+ const gateways = await manager.listAll();
27
+ ctx.body = { gateways };
28
+ });
29
+ // Start a profile's gateway
30
+ exports.gatewayRoutes.post('/api/hermes/gateways/:name/start', async (ctx) => {
31
+ if (!manager) {
32
+ ctx.status = 503;
33
+ ctx.body = { error: 'GatewayManager not initialized' };
34
+ return;
35
+ }
36
+ const { name } = ctx.params;
37
+ try {
38
+ const status = await manager.start(name);
39
+ ctx.body = { success: true, gateway: status };
40
+ }
41
+ catch (err) {
42
+ ctx.status = 500;
43
+ ctx.body = { error: err.message };
44
+ }
45
+ });
46
+ // Stop a profile's gateway
47
+ exports.gatewayRoutes.post('/api/hermes/gateways/:name/stop', async (ctx) => {
48
+ if (!manager) {
49
+ ctx.status = 503;
50
+ ctx.body = { error: 'GatewayManager not initialized' };
51
+ return;
52
+ }
53
+ const { name } = ctx.params;
54
+ try {
55
+ await manager.stop(name);
56
+ ctx.body = { success: true };
57
+ }
58
+ catch (err) {
59
+ ctx.status = 500;
60
+ ctx.body = { error: err.message };
61
+ }
62
+ });
63
+ // Check a profile's gateway health
64
+ exports.gatewayRoutes.get('/api/hermes/gateways/:name/health', async (ctx) => {
65
+ if (!manager) {
66
+ ctx.status = 503;
67
+ ctx.body = { error: 'GatewayManager not initialized' };
68
+ return;
69
+ }
70
+ const { name } = ctx.params;
71
+ const status = await manager.detectStatus(name);
72
+ ctx.body = { gateway: status };
73
+ });
@@ -12,6 +12,7 @@ const filesystem_1 = require("./filesystem");
12
12
  const logs_1 = require("./logs");
13
13
  const weixin_1 = require("./weixin");
14
14
  const codex_auth_1 = require("./codex-auth");
15
+ const gateways_1 = require("./gateways");
15
16
  const proxy_1 = require("./proxy");
16
17
  Object.defineProperty(exports, "proxyMiddleware", { enumerable: true, get: function () { return proxy_1.proxyMiddleware; } });
17
18
  const terminal_1 = require("./terminal");
@@ -24,4 +25,5 @@ exports.hermesRoutes.use(filesystem_1.fsRoutes.routes());
24
25
  exports.hermesRoutes.use(logs_1.logRoutes.routes());
25
26
  exports.hermesRoutes.use(weixin_1.weixinRoutes.routes());
26
27
  exports.hermesRoutes.use(codex_auth_1.codexAuthRoutes.routes());
28
+ exports.hermesRoutes.use(gateways_1.gatewayRoutes.routes());
27
29
  exports.hermesRoutes.use(proxy_1.proxyRoutes.routes());
@@ -42,38 +42,8 @@ const fs_1 = require("fs");
42
42
  const promises_1 = require("fs/promises");
43
43
  const path_1 = require("path");
44
44
  const os_1 = require("os");
45
- const js_yaml_1 = __importDefault(require("js-yaml"));
46
45
  const hermesCli = __importStar(require("../../services/hermes/hermes-cli"));
47
- const apiServerDefaults = {
48
- enabled: true,
49
- host: '127.0.0.1',
50
- port: 8642,
51
- key: '',
52
- cors_origins: '*',
53
- };
54
- function ensureApiServerConfig(profilePath) {
55
- const configPath = (0, path_1.join)(profilePath, 'config.yaml');
56
- try {
57
- if (!(0, fs_1.existsSync)(configPath)) {
58
- // Profile has no config.yaml — run hermes setup --reset to generate full defaults,
59
- // then inject api_server config (setup itself doesn't add it)
60
- console.log(`[Profile] No config.yaml for ${profilePath}, running setup --reset`);
61
- return { needSetup: true, path: profilePath };
62
- }
63
- const content = (0, fs_1.readFileSync)(configPath, 'utf-8');
64
- const cfg = js_yaml_1.default.load(content) || {};
65
- if (!cfg.platforms)
66
- cfg.platforms = {};
67
- if (!cfg.platforms.api_server) {
68
- cfg.platforms.api_server = { ...apiServerDefaults };
69
- (0, fs_1.writeFileSync)(configPath, js_yaml_1.default.dump(cfg), 'utf-8');
70
- console.log(`[Profile] Ensured api_server config for: ${profilePath}`);
71
- }
72
- return { needSetup: false, path: profilePath };
73
- }
74
- catch { }
75
- return { needSetup: false, path: profilePath };
76
- }
46
+ const gateways_1 = require("./gateways");
77
47
  exports.profileRoutes = new router_1.default();
78
48
  // GET /api/profiles - List all profiles
79
49
  exports.profileRoutes.get('/api/hermes/profiles', async (ctx) => {
@@ -96,6 +66,16 @@ exports.profileRoutes.post('/api/hermes/profiles', async (ctx) => {
96
66
  }
97
67
  try {
98
68
  const output = await hermesCli.createProfile(name, clone);
69
+ // 创建完成后启动该 profile 的网关
70
+ const mgr = (0, gateways_1.getGatewayManager)();
71
+ if (mgr) {
72
+ try {
73
+ await mgr.start(name);
74
+ }
75
+ catch (err) {
76
+ console.error(`[Profile] Failed to start gateway for ${name}:`, err.message);
77
+ }
78
+ }
99
79
  ctx.body = { success: true, message: output.trim() };
100
80
  }
101
81
  catch (err) {
@@ -124,6 +104,14 @@ exports.profileRoutes.delete('/api/hermes/profiles/:name', async (ctx) => {
124
104
  return;
125
105
  }
126
106
  try {
107
+ // Stop gateway for this profile before deleting
108
+ const mgr = (0, gateways_1.getGatewayManager)();
109
+ if (mgr) {
110
+ try {
111
+ await mgr.stop(name);
112
+ }
113
+ catch { }
114
+ }
127
115
  const ok = await hermesCli.deleteProfile(name);
128
116
  if (ok) {
129
117
  ctx.body = { success: true };
@@ -171,55 +159,27 @@ exports.profileRoutes.put('/api/hermes/profiles/active', async (ctx) => {
171
159
  return;
172
160
  }
173
161
  try {
174
- // 1. Stop gateway
175
- try {
176
- await hermesCli.stopGateway();
177
- }
178
- catch { }
179
- // 2. Kill gateway by port if still running
180
- try {
181
- const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
182
- const isWin = process.platform === 'win32';
183
- let pids = '';
184
- if (isWin) {
185
- const out = execSync('netstat -aon | findstr :8642', { encoding: 'utf-8', timeout: 5000 }).trim();
186
- const lines = out.split('\n').filter(l => l.includes('LISTENING'));
187
- pids = Array.from(new Set(lines.map(l => l.trim().split(/\s+/).pop()).filter(Boolean))).join(' ');
188
- }
189
- else {
190
- pids = execSync('lsof -ti:8642', { encoding: 'utf-8', timeout: 5000 }).trim();
191
- }
192
- if (pids) {
193
- if (isWin) {
194
- execSync(`taskkill /F /PID ${pids.split(' ').join(' /PID ')}`, { timeout: 5000 });
195
- }
196
- else {
197
- execSync(`kill -9 ${pids}`, { timeout: 5000 });
198
- }
199
- await new Promise(r => setTimeout(r, 2000));
200
- }
201
- }
202
- catch { }
203
- // 3. Switch profile
162
+ // 1. Switch profile only (no gateway stop/restart)
204
163
  const output = await hermesCli.useProfile(name);
205
164
  await new Promise(r => setTimeout(r, 1000));
206
- // 4. Ensure api_server config for new profile
165
+ // 2. Update GatewayManager active profile
166
+ const mgr = (0, gateways_1.getGatewayManager)();
167
+ if (mgr) {
168
+ mgr.setActiveProfile(name);
169
+ }
170
+ // 3. Ensure api_server config for new profile
207
171
  try {
208
172
  const detail = await hermesCli.getProfile(name);
209
173
  console.log(`[Profile] detail.path = ${detail.path}`);
210
- const result = ensureApiServerConfig(detail.path);
211
- if (result?.needSetup) {
212
- // No config.yaml — run setup --reset to create full default config,
213
- // then ensure api_server is present
174
+ if (!(0, fs_1.existsSync)((0, path_1.join)(detail.path, 'config.yaml'))) {
175
+ // No config.yaml — run setup --reset to create full default config
214
176
  try {
215
177
  await hermesCli.setupReset();
216
178
  }
217
179
  catch { }
218
- ensureApiServerConfig(detail.path);
219
180
  }
220
181
  // Create .env if target has none
221
182
  const profileEnv = (0, path_1.join)(detail.path, '.env');
222
- console.log(`[Profile] .env exists: ${(0, fs_1.existsSync)(profileEnv)}, path: ${profileEnv}`);
223
183
  if (!(0, fs_1.existsSync)(profileEnv)) {
224
184
  (0, fs_1.writeFileSync)(profileEnv, '# Hermes Agent Environment Configuration\n', 'utf-8');
225
185
  console.log(`[Profile] Created .env for: ${detail.path}`);
@@ -228,22 +188,6 @@ exports.profileRoutes.put('/api/hermes/profiles/active', async (ctx) => {
228
188
  catch (err) {
229
189
  console.error(`[Profile] Ensure config failed:`, err.message);
230
190
  }
231
- // 5. Start gateway
232
- try {
233
- await hermesCli.startGateway();
234
- console.log('[Profile] Gateway started');
235
- }
236
- catch {
237
- // Fallback: background mode (for WSL etc.)
238
- try {
239
- const pid = await hermesCli.startGatewayBackground();
240
- await new Promise(r => setTimeout(r, 3000));
241
- console.log(`[Profile] Gateway started in background mode (PID: ${pid})`);
242
- }
243
- catch (err) {
244
- console.error('[Profile] Gateway start failed:', err.message);
245
- }
246
- }
247
191
  ctx.body = { success: true, message: output.trim() };
248
192
  }
249
193
  catch (err) {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.proxy = proxy;
4
4
  const config_1 = require("../../config");
5
+ const gateways_1 = require("./gateways");
5
6
  function isTransientGatewayError(err) {
6
7
  const msg = String(err?.message || '');
7
8
  const causeCode = String(err?.cause?.code || '');
@@ -26,8 +27,23 @@ async function waitForGatewayReady(upstream, timeoutMs = 5000) {
26
27
  }
27
28
  return false;
28
29
  }
30
+ /** Resolve upstream URL for a request based on profile header/query */
31
+ function resolveUpstream(ctx) {
32
+ const mgr = (0, gateways_1.getGatewayManager)();
33
+ if (mgr) {
34
+ // Check X-Hermes-Profile header or ?profile= query param
35
+ const profile = ctx.get('x-hermes-profile') || ctx.query.profile;
36
+ if (profile) {
37
+ return mgr.getUpstream(profile);
38
+ }
39
+ // Default to active profile's upstream
40
+ return mgr.getUpstream();
41
+ }
42
+ // Fallback: static upstream from config
43
+ return config_1.config.upstream.replace(/\/$/, '');
44
+ }
29
45
  async function proxy(ctx) {
30
- const upstream = config_1.config.upstream.replace(/\/$/, '');
46
+ const upstream = resolveUpstream(ctx);
31
47
  // Rewrite path for upstream gateway:
32
48
  // /api/hermes/v1/* -> /v1/* (upstream uses /v1/ prefix)
33
49
  // /api/hermes/* -> /api/* (upstream uses /api/ prefix)
@@ -39,11 +39,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.sessionRoutes = void 0;
40
40
  const router_1 = __importDefault(require("@koa/router"));
41
41
  const hermesCli = __importStar(require("../../services/hermes/hermes-cli"));
42
+ const sessions_db_1 = require("../../services/hermes/sessions-db");
42
43
  exports.sessionRoutes = new router_1.default();
43
44
  // List sessions from Hermes
44
45
  exports.sessionRoutes.get('/api/hermes/sessions', async (ctx) => {
45
46
  const source = ctx.query.source || undefined;
46
47
  const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : undefined;
48
+ try {
49
+ const sessions = await (0, sessions_db_1.listSessionSummaries)(source, limit && limit > 0 ? limit : 2000);
50
+ ctx.body = { sessions };
51
+ return;
52
+ }
53
+ catch (err) {
54
+ console.warn('[Hermes Session DB] summary query failed, falling back to CLI:', err);
55
+ }
47
56
  const sessions = await hermesCli.listSessions(source, limit);
48
57
  ctx.body = { sessions };
49
58
  });