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.
- package/LICENSE +21 -0
- package/dist/client/assets/{Add-DBcT5hzg.js → Add-CKf6ViXR.js} +1 -1
- package/dist/client/assets/{Button-R5s3092y.js → Button-CrrCCorI.js} +1 -1
- package/dist/client/assets/ChannelsView-CoREbYTy.css +1 -0
- package/dist/client/assets/ChannelsView-D4I7hhZO.js +1 -0
- package/dist/client/assets/ChatView-DxyBUK57.js +127 -0
- package/dist/client/assets/ChatView-Vfi_jEpI.css +1 -0
- package/dist/client/assets/Close-C9xwy-pW.js +45 -0
- package/dist/client/assets/{FormItem-ZuVh5lSE.js → FormItem-BgJdrTW0.js} +1 -1
- package/dist/client/assets/GatewaysView-Cib2JydO.js +1 -0
- package/dist/client/assets/GatewaysView-DtUN_4uh.css +1 -0
- package/dist/client/assets/Input-ChENEW-Z.js +234 -0
- package/dist/client/assets/{InputNumber-B6xJGyAP.js → InputNumber-Xd6HWSdp.js} +3 -3
- package/dist/client/assets/JobsView-C9-go5-X.css +1 -0
- package/dist/client/assets/{JobsView-BNGFt0kv.js → JobsView-SnToCbDd.js} +2 -2
- package/dist/client/assets/{LoginView-B-UpFAOr.js → LoginView-BZdmMnsf.js} +1 -1
- package/dist/client/assets/LoginView-DHPxG3eC.css +1 -0
- package/dist/client/assets/{LogsView-BMJ5FVwp.js → LogsView-DblvOJIg.js} +1 -1
- package/dist/client/assets/LogsView-VfBZmaM1.css +1 -0
- package/dist/client/assets/{MarkdownRenderer-C8-GFOw3.js → MarkdownRenderer-DJLVk7ei.js} +1 -1
- package/dist/client/assets/MarkdownRenderer-Dc9VWGMm.css +1 -0
- package/dist/client/assets/MemoryView-BZxpGTCE.css +1 -0
- package/dist/client/assets/{MemoryView-Rfsn9nzW.js → MemoryView-exXvRwCc.js} +1 -1
- package/dist/client/assets/{Modal-DCJNfzf6.js → Modal-B2zvXTrk.js} +6 -6
- package/dist/client/assets/{ModelsView-wE2P2H3O.js → ModelsView-DGs47Cj4.js} +1 -1
- package/dist/client/assets/ModelsView-jbgZP3YF.css +1 -0
- package/dist/client/assets/Popconfirm-BoZc0kKk.js +16 -0
- package/dist/client/assets/Popover-Cu52vG3D.js +117 -0
- package/dist/client/assets/ProfilesView-1BtKDO_6.css +1 -0
- package/dist/client/assets/ProfilesView-D0FY7Jwe.js +440 -0
- package/dist/client/assets/Select-BHc7u-Yf.js +340 -0
- package/dist/client/assets/SettingRow-Cr1VcUPz.css +1 -0
- package/dist/client/assets/{SettingRow-Bztj1Qvd.js → SettingRow-i-UXlco7.js} +1 -1
- package/dist/client/assets/SettingsView-BW6ctYG5.js +352 -0
- package/dist/client/assets/SettingsView-Dhr2wzAB.css +1 -0
- package/dist/client/assets/SkillsView-5K2WjAWZ.css +1 -0
- package/dist/client/assets/{SkillsView-DA7GSqMh.js → SkillsView-B5QBaAzi.js} +1 -1
- package/dist/client/assets/{Spin-UenY6DjU.js → Spin-DsNCRPk9.js} +3 -3
- package/dist/client/assets/Suffix-3xK0KZGt.js +90 -0
- package/dist/client/assets/{Switch-D4sdzrF1.js → Switch-Bf63XXgA.js} +2 -2
- package/dist/client/assets/{light-UKc15Qzy.js → Tag-Dmbj68Ki.js} +2 -2
- package/dist/client/assets/TerminalView-BOiVMGJZ.css +1 -0
- package/dist/client/assets/{TerminalView-bdOH27Rz.js → TerminalView-DrJHZ0qI.js} +16 -16
- package/dist/client/assets/{Tooltip-shahfZE0.js → Tooltip-CRbZNhG0.js} +1 -1
- package/dist/client/assets/{UsageView-LP-yDWfN.js → UsageView-DQ43JasX.js} +1 -1
- package/dist/client/assets/UsageView-wje1C97e.css +1 -0
- package/dist/client/assets/Warning-kBbRMAif.js +1 -0
- package/dist/client/assets/{_plugin-vue_export-helper-B5RrPTMt.js → _plugin-vue_export-helper-CnosYBkx.js} +1 -1
- package/dist/client/assets/app-BT9yU6N6.js +1 -0
- package/dist/client/assets/app-CjNVVG5x.js +1 -0
- package/dist/client/assets/{browser-Cnde4syl.js → browser-Djp4tkp3.js} +1 -1
- package/dist/client/assets/chat-DlC9S9DK.js +6 -0
- package/dist/client/assets/composables-DCA4Yga5.js +1 -0
- package/dist/client/assets/fade-in.cssr-CIVyTG6A.js +12 -0
- package/dist/client/assets/index-BEcRccNA.css +1 -0
- package/dist/client/assets/index-D12ukDT7.js +284 -0
- package/dist/client/assets/{jobs-Bg3B0qd3.js → jobs-CcVaCGMJ.js} +1 -1
- package/dist/client/assets/{light-CtcMXxnt.js → light-BF6E9z0k.js} +1 -1
- package/dist/client/assets/{light-B9EqymT1.js → light-BJ96fCLC.js} +1 -1
- package/dist/client/assets/{light-7AGE9udo.js → light-BPqyaxve.js} +1 -1
- package/dist/client/assets/{light-C_iMTPuX.js → light-CSp9-LhE.js} +1 -1
- package/dist/client/assets/light-D9G2GshF.js +1 -0
- package/dist/client/assets/light-KCEDTUGE.js +23 -0
- package/dist/client/assets/{pinia-BizF94YH.js → pinia-iHE5_ZXa.js} +1 -1
- package/dist/client/assets/profiles-CJCR84uQ.js +1 -0
- package/dist/client/assets/{router-BuVIua3w.js → router-C-NNJUuf.js} +2 -2
- package/dist/client/assets/{sessions-CpgDAUKL.js → sessions-C4bnNvzS.js} +1 -1
- package/dist/client/assets/{skills-BGUX0Zk7.js → skills-B4slZfeZ.js} +1 -1
- package/dist/client/assets/{use-message-DgahoF2f.js → use-message-BIpqgDet.js} +1 -1
- package/dist/client/assets/{useTheme-CRVFV8Ud.js → useTheme-B78N9tyz.js} +1 -1
- package/dist/client/index.html +29 -29
- package/dist/server/index.js +17 -79
- package/dist/server/routes/hermes/filesystem.js +3 -1
- package/dist/server/routes/hermes/gateways.d.ts +4 -0
- package/dist/server/routes/hermes/gateways.js +73 -0
- package/dist/server/routes/hermes/index.js +2 -0
- package/dist/server/routes/hermes/profiles.js +28 -84
- package/dist/server/routes/hermes/proxy-handler.js +17 -1
- package/dist/server/routes/hermes/sessions.js +9 -0
- package/dist/server/services/hermes/gateway-manager.d.ts +131 -0
- package/dist/server/services/hermes/gateway-manager.js +536 -0
- package/dist/server/services/hermes/sessions-db.d.ts +24 -0
- package/dist/server/services/hermes/sessions-db.js +147 -0
- package/package.json +1 -1
- package/dist/client/assets/ChannelsView-CH6386MX.js +0 -1
- package/dist/client/assets/ChannelsView-DS2AEk-a.css +0 -1
- package/dist/client/assets/ChatView-C_mCyb0n.js +0 -127
- package/dist/client/assets/ChatView-CtLv9X8G.css +0 -1
- package/dist/client/assets/Close-CbcMKiZ9.js +0 -45
- package/dist/client/assets/Input-CbOEVtEq.js +0 -234
- package/dist/client/assets/JobsView-CVx2Yv-y.css +0 -1
- package/dist/client/assets/LoginView-CA9w3oDH.css +0 -1
- package/dist/client/assets/LogsView-BG7Bro4j.css +0 -1
- package/dist/client/assets/MarkdownRenderer-Ct6cRT2D.css +0 -1
- package/dist/client/assets/MemoryView-DEyIgPS8.css +0 -1
- package/dist/client/assets/ModelsView-D1p_2trx.css +0 -1
- package/dist/client/assets/Popconfirm-pG5fyaNw.js +0 -16
- package/dist/client/assets/Popover-BcnZkOJ0.js +0 -117
- package/dist/client/assets/ProfilesView-CAOfb8ly.js +0 -440
- package/dist/client/assets/ProfilesView-Chffv0-D.css +0 -1
- package/dist/client/assets/Scrollbar-B7udJACg.js +0 -77
- package/dist/client/assets/Select-B6Xt21kv.js +0 -340
- package/dist/client/assets/SettingRow-CwidKv2Q.css +0 -1
- package/dist/client/assets/SettingsView-BIEQOPzq.css +0 -1
- package/dist/client/assets/SettingsView-Dwx8y_nj.js +0 -352
- package/dist/client/assets/SkillsView-D5bivF7Z.css +0 -1
- package/dist/client/assets/Suffix-DopnzIdr.js +0 -25
- package/dist/client/assets/TerminalView-DPdn9YA7.css +0 -1
- package/dist/client/assets/UsageView-CnADqqzf.css +0 -1
- package/dist/client/assets/Warning-DT2rnqpH.js +0 -1
- package/dist/client/assets/app-B3-jMgcz.js +0 -1
- package/dist/client/assets/app-Baqdn89g.js +0 -1
- package/dist/client/assets/chat-CCvtq7iz.js +0 -6
- package/dist/client/assets/composables-yAWv_nAD.js +0 -1
- package/dist/client/assets/fade-in-scale-up.cssr-7cCctcdO.js +0 -1
- package/dist/client/assets/index-JeutuTe0.css +0 -1
- package/dist/client/assets/index-vZMUiSyM.js +0 -284
- package/dist/client/assets/light-87-mk9Yl.js +0 -1
- package/dist/client/assets/profiles-DXTLdlvo.js +0 -23
- package/dist/client/assets/use-compitable-C1bGazqI.js +0 -1
- /package/dist/client/assets/{_common-DgdkN_d5.js → _common-Yp55QE79.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as e}from"./router-
|
|
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-
|
|
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-
|
|
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-
|
|
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};
|
package/dist/client/index.html
CHANGED
|
@@ -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-
|
|
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/
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
15
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
16
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/assets/Button-
|
|
18
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
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-
|
|
23
|
-
<link rel="modulepreload" crossorigin href="/assets/Warning-
|
|
24
|
-
<link rel="modulepreload" crossorigin href="/assets/Modal-
|
|
25
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
26
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
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/
|
|
29
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
30
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
31
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
32
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
33
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
34
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
35
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
36
|
-
<link rel="modulepreload" crossorigin href="/assets/light-
|
|
37
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
38
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
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-
|
|
40
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BEcRccNA.css">
|
|
41
41
|
</head>
|
|
42
42
|
|
|
43
43
|
<body>
|
package/dist/server/index.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
242
|
-
const {
|
|
243
|
-
const
|
|
244
|
-
const {
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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,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
|
|
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.
|
|
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
|
-
//
|
|
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
|
-
|
|
211
|
-
|
|
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 =
|
|
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
|
});
|