hermes-web-ui 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/bin/hermes-web-ui.mjs +50 -13
- package/dist/client/assets/Add-XpaL69a7.js +1 -0
- package/dist/client/assets/{Button-zECKCotA.js → Button-DZWlvt3_.js} +14 -14
- package/dist/client/assets/{ChannelsView-xCdAEZam.css → ChannelsView-D168bwSq.css} +1 -1
- package/dist/client/assets/ChannelsView-DsFZWVNp.js +1 -0
- package/dist/client/assets/ChatView-DoqDlZLC.js +127 -0
- package/dist/client/assets/{Close-DjLWDodG.js → Close-CynFIaWE.js} +10 -10
- package/dist/client/assets/FormItem-9JbgiHgw.js +110 -0
- package/dist/client/assets/Input-BUMpvosL.js +234 -0
- package/dist/client/assets/InputNumber-BOptkZXt.js +13 -0
- package/dist/client/assets/{JobsView-DsfGyjnT.js → JobsView-C-nRY00Q.js} +2 -2
- package/dist/client/assets/{LoginView-BiWaCYS0.js → LoginView-CLrTbJPK.js} +1 -1
- package/dist/client/assets/{LogsView-DuORyFFY.js → LogsView-BjeUz4w_.js} +1 -1
- package/dist/client/assets/{MarkdownRenderer-BA3oPVqT.js → MarkdownRenderer-pJXYVaJf.js} +1 -1
- package/dist/client/assets/MemoryView-B_b0Bw4s.js +7 -0
- package/dist/client/assets/MemoryView-hIfyUkwz.css +1 -0
- package/dist/client/assets/{Modal-WQKdvHJY.js → Modal-Gb5CDHOn.js} +56 -56
- package/dist/client/assets/ModelsView-BHO6JeeK.js +1 -0
- package/dist/client/assets/{Popconfirm-02A7vL_z.js → Popconfirm-f3hviugz.js} +4 -4
- package/dist/client/assets/Popover-BtQeULHo.js +117 -0
- package/dist/client/assets/ProfilesView-C4GoKpgi.js +440 -0
- package/dist/client/assets/ProfilesView-jqeO8Zv3.css +1 -0
- package/dist/client/assets/{Scrollbar-Bg2FeTtI.js → Scrollbar-Bui9E0cT.js} +17 -17
- package/dist/client/assets/Select-By6oALhZ.js +340 -0
- package/dist/client/assets/{SettingRow-CLLFxWP3.js → SettingRow-CWqG1TP7.js} +1 -1
- package/dist/client/assets/SettingsView-CzgT0dAx.js +352 -0
- package/dist/client/assets/{SkillsView-D6CegW4w.js → SkillsView-2xeOBpxm.js} +1 -1
- package/dist/client/assets/{Spin-lybeIDeX.js → Spin-DD5RgTEV.js} +4 -4
- package/dist/client/assets/{Suffix-Dv1UElLH.js → Suffix-CkTuXehC.js} +5 -5
- package/dist/client/assets/{Switch-mgeu-lpD.js → Switch-BqpUglHp.js} +15 -15
- package/dist/client/assets/{Tag-DWc4lbAm.js → Tag-BdqJCtwA.js} +15 -15
- package/dist/client/assets/{TerminalView-DmnXOHFu.js → TerminalView-DOvaJfhr.js} +1 -1
- package/dist/client/assets/{Tooltip-DrDTmrj3.js → Tooltip-2f394xdV.js} +1 -1
- package/dist/client/assets/{UsageView-C3O6-vbW.js → UsageView-Cd7Gui1x.js} +1 -1
- package/dist/client/assets/{Warning-t1i_4T5i.js → Warning-21tMOc2L.js} +1 -1
- package/dist/client/assets/{_plugin-vue_export-helper-BkT4A29V.js → _plugin-vue_export-helper-C39Y8Snr.js} +1 -1
- package/dist/client/assets/app-Dc1GzlQi.js +1 -0
- package/dist/client/assets/app-DjAPGKg7.js +1 -0
- package/dist/client/assets/{browser-DzAOCZtm.js → browser-DMdqvQBg.js} +9 -9
- package/dist/client/assets/{chat-CX1Hza8X.js → chat-1kL3Ps59.js} +2 -2
- package/dist/client/assets/composables-BxlGNoT-.js +1 -0
- package/dist/client/assets/create-5zWq3BEB.js +1 -0
- package/dist/client/assets/fade-in-scale-up.cssr-HXBs3oL0.js +1 -0
- package/dist/client/assets/index-BT19Bivl.css +1 -0
- package/dist/client/assets/index-CFIEnaxD.js +284 -0
- package/dist/client/assets/{jobs-B9NHIxuL.js → jobs-YFhNrsBu.js} +1 -1
- package/dist/client/assets/{pinia-bPUFQqFK.js → pinia-BPgWlB7x.js} +1 -1
- package/dist/client/assets/profiles-H8Bd9mDO.js +23 -0
- package/dist/client/assets/{router-COwpqexM.js → router-cAIqC_mO.js} +2 -2
- package/dist/client/assets/{sessions-B1SPfe-R.js → sessions-fFwnFvo3.js} +1 -1
- package/dist/client/assets/{skills-CRtljpt6.js → skills-DZWMwFIf.js} +1 -1
- package/dist/client/assets/use-compitable-esQP7BHf.js +1 -0
- package/dist/client/assets/use-message-CnDHqZf8.js +1 -0
- package/dist/client/index.html +24 -22
- package/dist/server/data/.token +1 -0
- package/dist/server/index.js +70 -4
- package/dist/server/routes/hermes/config.js +11 -11
- package/dist/server/routes/hermes/filesystem.js +154 -29
- package/dist/server/routes/hermes/profiles.js +136 -14
- package/dist/server/routes/hermes/terminal.js +12 -34
- package/dist/server/routes/hermes/weixin.js +6 -6
- package/dist/server/services/hermes-cli.d.ts +4 -0
- package/dist/server/services/hermes-cli.js +17 -0
- package/dist/server/services/hermes-profile.d.ts +22 -0
- package/dist/server/services/hermes-profile.js +60 -0
- package/dist/server/shared/providers.js +8 -14
- package/package.json +1 -1
- package/dist/client/assets/ChannelsView-Bvh3tnio.js +0 -1
- package/dist/client/assets/ChatView-CJZahJ1V.js +0 -127
- package/dist/client/assets/FormItem-CZ9auYA2.js +0 -110
- package/dist/client/assets/Input-DxJk6pT-.js +0 -234
- package/dist/client/assets/InputNumber-BTqp81h0.js +0 -13
- package/dist/client/assets/MemoryView-CU3JHweh.css +0 -1
- package/dist/client/assets/MemoryView-VgQFNMsP.js +0 -5
- package/dist/client/assets/ModelsView-QDRloKd5.js +0 -1
- package/dist/client/assets/Popover-2C1zuptC.js +0 -117
- package/dist/client/assets/Select-C_hKaXgI.js +0 -340
- package/dist/client/assets/SettingsView-CeScqhwO.js +0 -352
- package/dist/client/assets/app-BUjbfMSg.js +0 -1
- package/dist/client/assets/app-p59ZckFN.js +0 -1
- package/dist/client/assets/context-BM8ZQCOz.js +0 -1
- package/dist/client/assets/fade-in-scale-up.cssr-DQl-Z54c.js +0 -1
- package/dist/client/assets/index-95z-iQoj.js +0 -306
- package/dist/client/assets/index-C33O1KZm.css +0 -1
- package/dist/client/assets/use-compitable-BnlNjEug.js +0 -1
- package/dist/client/assets/use-message-g8F4Z57w.js +0 -1
- /package/dist/client/assets/{omit-C4dR5R2G.js → omit-1BRB6K75.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as e}from"./router-
|
|
1
|
+
import{o as e}from"./router-cAIqC_mO.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-cAIqC_mO.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};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{C as e}from"./router-cAIqC_mO.js";function t(t,n){return e(()=>{for(let e of n)if(t[e]!==void 0)return t[e];return t[n[n.length-1]]})}export{t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{F as e}from"./router-cAIqC_mO.js";import{$ as t,Y as n}from"./browser-DMdqvQBg.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};
|
package/dist/client/index.html
CHANGED
|
@@ -6,29 +6,31 @@
|
|
|
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/Scrollbar-
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/use-compitable-
|
|
15
|
-
<link rel="modulepreload" crossorigin href="/assets/Popover-
|
|
16
|
-
<link rel="modulepreload" crossorigin href="/assets/Close-
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/assets/Button-
|
|
18
|
-
<link rel="modulepreload" crossorigin href="/assets/Suffix-
|
|
19
|
-
<link rel="modulepreload" crossorigin href="/assets/fade-in-scale-up.cssr-
|
|
20
|
-
<link rel="modulepreload" crossorigin href="/assets/Tag-
|
|
21
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
22
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
23
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
24
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
25
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
26
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
27
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
28
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
29
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-CFIEnaxD.js"></script>
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/router-cAIqC_mO.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-C39Y8Snr.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/browser-DMdqvQBg.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/Scrollbar-Bui9E0cT.js">
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/use-compitable-esQP7BHf.js">
|
|
15
|
+
<link rel="modulepreload" crossorigin href="/assets/Popover-BtQeULHo.js">
|
|
16
|
+
<link rel="modulepreload" crossorigin href="/assets/Close-CynFIaWE.js">
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/assets/Button-DZWlvt3_.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/assets/Suffix-CkTuXehC.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="/assets/fade-in-scale-up.cssr-HXBs3oL0.js">
|
|
20
|
+
<link rel="modulepreload" crossorigin href="/assets/Tag-BdqJCtwA.js">
|
|
21
|
+
<link rel="modulepreload" crossorigin href="/assets/create-5zWq3BEB.js">
|
|
22
|
+
<link rel="modulepreload" crossorigin href="/assets/Select-By6oALhZ.js">
|
|
23
|
+
<link rel="modulepreload" crossorigin href="/assets/Warning-21tMOc2L.js">
|
|
24
|
+
<link rel="modulepreload" crossorigin href="/assets/Modal-Gb5CDHOn.js">
|
|
25
|
+
<link rel="modulepreload" crossorigin href="/assets/pinia-BPgWlB7x.js">
|
|
26
|
+
<link rel="modulepreload" crossorigin href="/assets/profiles-H8Bd9mDO.js">
|
|
27
|
+
<link rel="modulepreload" crossorigin href="/assets/omit-1BRB6K75.js">
|
|
28
|
+
<link rel="modulepreload" crossorigin href="/assets/use-message-CnDHqZf8.js">
|
|
29
|
+
<link rel="modulepreload" crossorigin href="/assets/sessions-fFwnFvo3.js">
|
|
30
|
+
<link rel="modulepreload" crossorigin href="/assets/app-Dc1GzlQi.js">
|
|
31
|
+
<link rel="modulepreload" crossorigin href="/assets/chat-1kL3Ps59.js">
|
|
30
32
|
<link rel="modulepreload" crossorigin href="/assets/logo-Cd-t_oGE.js">
|
|
31
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
33
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BT19Bivl.css">
|
|
32
34
|
</head>
|
|
33
35
|
|
|
34
36
|
<body>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
f56904d3b78a9bb085ba075b2827b1c29bf06d41bdddba5b758fd4e68845f5e7
|
package/dist/server/index.js
CHANGED
|
@@ -44,12 +44,48 @@ const koa_static_1 = __importDefault(require("koa-static"));
|
|
|
44
44
|
const koa_send_1 = __importDefault(require("koa-send"));
|
|
45
45
|
const path_1 = require("path");
|
|
46
46
|
const promises_1 = require("fs/promises");
|
|
47
|
+
const fs_1 = require("fs");
|
|
47
48
|
const config_1 = require("./config");
|
|
48
49
|
const hermes_1 = require("./routes/hermes");
|
|
49
50
|
const upload_1 = require("./routes/upload");
|
|
50
51
|
const webhook_1 = require("./routes/webhook");
|
|
51
52
|
const hermesCli = __importStar(require("./services/hermes-cli"));
|
|
52
53
|
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
|
+
}
|
|
53
89
|
const app = new koa_1.default();
|
|
54
90
|
const { restartGateway, startGateway, startGatewayBackground, getVersion } = hermesCli;
|
|
55
91
|
let server = null;
|
|
@@ -71,13 +107,37 @@ async function bootstrap() {
|
|
|
71
107
|
app.use((0, bodyparser_1.default)());
|
|
72
108
|
app.use(webhook_1.webhookRoutes.routes());
|
|
73
109
|
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
|
+
});
|
|
74
134
|
app.use(hermes_1.hermesRoutes.routes());
|
|
75
135
|
app.use(hermes_1.proxyMiddleware);
|
|
76
136
|
// health
|
|
77
137
|
app.use(async (ctx, next) => {
|
|
78
138
|
if (ctx.path === '/health') {
|
|
79
139
|
const raw = await getVersion();
|
|
80
|
-
const
|
|
140
|
+
const hermesVersion = raw.split('\n')[0].replace('Hermes Agent ', '') || '';
|
|
81
141
|
let gatewayOk = false;
|
|
82
142
|
try {
|
|
83
143
|
const res = await fetch(`${config_1.config.upstream.replace(/\/$/, '')}/health`, {
|
|
@@ -89,8 +149,11 @@ async function bootstrap() {
|
|
|
89
149
|
ctx.body = {
|
|
90
150
|
status: gatewayOk ? 'ok' : 'error',
|
|
91
151
|
platform: 'hermes-agent',
|
|
92
|
-
version,
|
|
152
|
+
version: hermesVersion,
|
|
93
153
|
gateway: gatewayOk ? 'running' : 'stopped',
|
|
154
|
+
webui_version: LOCAL_VERSION,
|
|
155
|
+
webui_latest: cachedLatestVersion,
|
|
156
|
+
webui_update_available: cachedLatestVersion && cachedLatestVersion !== LOCAL_VERSION,
|
|
94
157
|
};
|
|
95
158
|
return;
|
|
96
159
|
}
|
|
@@ -120,6 +183,9 @@ async function bootstrap() {
|
|
|
120
183
|
});
|
|
121
184
|
// 👇 绑定退出信号
|
|
122
185
|
bindShutdown();
|
|
186
|
+
// Check for updates every 4 hours
|
|
187
|
+
checkLatestVersion();
|
|
188
|
+
setInterval(checkLatestVersion, 4 * 60 * 60 * 1000);
|
|
123
189
|
}
|
|
124
190
|
// ============================
|
|
125
191
|
// ✅ 统一关闭逻辑(核心)
|
|
@@ -173,10 +239,10 @@ function bindShutdown() {
|
|
|
173
239
|
// 你的原逻辑(基本不动)
|
|
174
240
|
// ============================
|
|
175
241
|
async function ensureApiServerConfig() {
|
|
176
|
-
const { homedir } = await Promise.resolve().then(() => __importStar(require('os')));
|
|
177
242
|
const { readFileSync, writeFileSync, existsSync, copyFileSync } = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
178
243
|
const yaml = (await Promise.resolve().then(() => __importStar(require('js-yaml')))).default;
|
|
179
|
-
const
|
|
244
|
+
const { getActiveConfigPath } = await Promise.resolve().then(() => __importStar(require('./services/hermes-profile')));
|
|
245
|
+
const configPath = getActiveConfigPath();
|
|
180
246
|
const defaults = {
|
|
181
247
|
enabled: true,
|
|
182
248
|
host: '127.0.0.1',
|
|
@@ -7,17 +7,16 @@ exports.configRoutes = void 0;
|
|
|
7
7
|
const router_1 = __importDefault(require("@koa/router"));
|
|
8
8
|
const promises_1 = require("fs/promises");
|
|
9
9
|
const promises_2 = require("fs/promises");
|
|
10
|
-
const path_1 = require("path");
|
|
11
|
-
const os_1 = require("os");
|
|
12
10
|
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
13
11
|
const hermes_cli_1 = require("../../services/hermes-cli");
|
|
12
|
+
const hermes_profile_1 = require("../../services/hermes-profile");
|
|
14
13
|
// Platform sections that require gateway restart after config change
|
|
15
14
|
const PLATFORM_SECTIONS = new Set([
|
|
16
15
|
'telegram', 'discord', 'slack', 'whatsapp', 'matrix',
|
|
17
16
|
'weixin', 'wecom', 'feishu', 'dingtalk',
|
|
18
17
|
]);
|
|
19
|
-
const configPath = (
|
|
20
|
-
const envPath = (
|
|
18
|
+
const configPath = () => (0, hermes_profile_1.getActiveConfigPath)();
|
|
19
|
+
const envPath = () => (0, hermes_profile_1.getActiveEnvPath)();
|
|
21
20
|
// Env var → (platform, configPath in PlatformConfig) mapping
|
|
22
21
|
// Matches hermes _apply_env_overrides() in gateway/config.py
|
|
23
22
|
const envPlatformMap = {
|
|
@@ -85,7 +84,7 @@ function getNested(obj, path) {
|
|
|
85
84
|
}
|
|
86
85
|
async function readEnvPlatforms() {
|
|
87
86
|
try {
|
|
88
|
-
const raw = await (0, promises_1.readFile)(envPath, 'utf-8');
|
|
87
|
+
const raw = await (0, promises_1.readFile)(envPath(), 'utf-8');
|
|
89
88
|
const env = parseEnv(raw);
|
|
90
89
|
const platforms = {};
|
|
91
90
|
for (const [envKey, [platform, cfgPath]] of Object.entries(envPlatformMap)) {
|
|
@@ -110,7 +109,7 @@ async function readEnvPlatforms() {
|
|
|
110
109
|
async function saveEnvValue(key, value) {
|
|
111
110
|
let raw;
|
|
112
111
|
try {
|
|
113
|
-
raw = await (0, promises_1.readFile)(envPath, 'utf-8');
|
|
112
|
+
raw = await (0, promises_1.readFile)(envPath(), 'utf-8');
|
|
114
113
|
}
|
|
115
114
|
catch {
|
|
116
115
|
raw = '';
|
|
@@ -151,26 +150,27 @@ async function saveEnvValue(key, value) {
|
|
|
151
150
|
}
|
|
152
151
|
// Remove trailing empty lines, keep exactly one trailing newline
|
|
153
152
|
let output = result.join('\n').replace(/\n{3,}/g, '\n\n').replace(/\n+$/, '') + '\n';
|
|
154
|
-
await (0, promises_1.writeFile)(envPath, output, 'utf-8');
|
|
153
|
+
await (0, promises_1.writeFile)(envPath(), output, 'utf-8');
|
|
155
154
|
// Set permissions to 0600 (owner only), matching hermes behavior
|
|
156
155
|
try {
|
|
157
|
-
await (0, promises_2.chmod)(envPath, 0o600);
|
|
156
|
+
await (0, promises_2.chmod)(envPath(), 0o600);
|
|
158
157
|
}
|
|
159
158
|
catch { /* ignore */ }
|
|
160
159
|
}
|
|
161
160
|
async function readConfig() {
|
|
162
|
-
const raw = await (0, promises_1.readFile)(configPath, 'utf-8');
|
|
161
|
+
const raw = await (0, promises_1.readFile)(configPath(), 'utf-8');
|
|
163
162
|
return js_yaml_1.default.load(raw) || {};
|
|
164
163
|
}
|
|
165
164
|
async function writeConfig(data) {
|
|
166
|
-
|
|
165
|
+
const cp = configPath();
|
|
166
|
+
await (0, promises_1.copyFile)(cp, cp + '.bak');
|
|
167
167
|
const yamlStr = js_yaml_1.default.dump(data, {
|
|
168
168
|
lineWidth: -1,
|
|
169
169
|
noRefs: true,
|
|
170
170
|
quotingType: '"',
|
|
171
171
|
forceQuotes: false,
|
|
172
172
|
});
|
|
173
|
-
await (0, promises_1.writeFile)(
|
|
173
|
+
await (0, promises_1.writeFile)(cp, yamlStr, 'utf-8');
|
|
174
174
|
}
|
|
175
175
|
exports.configRoutes = new router_1.default();
|
|
176
176
|
// GET /api/config — read config sections
|
|
@@ -1,4 +1,37 @@
|
|
|
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
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -7,12 +40,71 @@ exports.fsRoutes = void 0;
|
|
|
7
40
|
const router_1 = __importDefault(require("@koa/router"));
|
|
8
41
|
const promises_1 = require("fs/promises");
|
|
9
42
|
const path_1 = require("path");
|
|
10
|
-
const os_1 = require("os");
|
|
11
43
|
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
12
|
-
const
|
|
44
|
+
const hermes_profile_1 = require("../../services/hermes-profile");
|
|
45
|
+
const hermesCli = __importStar(require("../../services/hermes-cli"));
|
|
46
|
+
// --- Provider env var mapping (from hermes providers.py HERMES_OVERLAYS + config.py) ---
|
|
47
|
+
// Maps provider key → { api_key_envs: all env var aliases for API key, base_url_env: env var for base URL }
|
|
48
|
+
const PROVIDER_ENV_MAP = {
|
|
49
|
+
openrouter: { api_key_env: 'OPENROUTER_API_KEY', base_url_env: 'OPENROUTER_BASE_URL' },
|
|
50
|
+
zai: { api_key_env: 'ZAI_API_KEY', base_url_env: '' },
|
|
51
|
+
'kimi-for-coding': { api_key_env: 'KIMI_API_KEY', base_url_env: '' },
|
|
52
|
+
minimax: { api_key_env: 'MINIMAX_API_KEY', base_url_env: 'MINIMAX_BASE_URL' },
|
|
53
|
+
'minimax-cn': { api_key_env: 'MINIMAX_API_KEY', base_url_env: 'MINIMAX_CN_BASE_URL' },
|
|
54
|
+
deepseek: { api_key_env: 'DEEPSEEK_API_KEY', base_url_env: 'DEEPSEEK_BASE_URL' },
|
|
55
|
+
alibaba: { api_key_env: 'DASHSCOPE_API_KEY', base_url_env: 'DASHSCOPE_BASE_URL' },
|
|
56
|
+
anthropic: { api_key_env: 'ANTHROPIC_API_KEY', base_url_env: '' },
|
|
57
|
+
xai: { api_key_env: 'XAI_API_KEY', base_url_env: 'XAI_BASE_URL' },
|
|
58
|
+
xiaomi: { api_key_env: 'XIAOMI_API_KEY', base_url_env: 'XIAOMI_BASE_URL' },
|
|
59
|
+
gemini: { api_key_env: 'GEMINI_API_KEY', base_url_env: '' },
|
|
60
|
+
kilo: { api_key_env: 'KILO_API_KEY', base_url_env: 'KILOCODE_BASE_URL' },
|
|
61
|
+
vercel: { api_key_env: 'AI_GATEWAY_API_KEY', base_url_env: '' },
|
|
62
|
+
opencode: { api_key_env: 'OPENCODE_API_KEY', base_url_env: 'OPENCODE_ZEN_BASE_URL' },
|
|
63
|
+
'opencode-go': { api_key_env: 'OPENCODE_API_KEY', base_url_env: 'OPENCODE_GO_BASE_URL' },
|
|
64
|
+
huggingface: { api_key_env: 'HF_TOKEN', base_url_env: 'HF_BASE_URL' },
|
|
65
|
+
};
|
|
66
|
+
async function saveEnvValue(key, value) {
|
|
67
|
+
const envPath = (0, hermes_profile_1.getActiveEnvPath)();
|
|
68
|
+
let raw;
|
|
69
|
+
try {
|
|
70
|
+
raw = await (0, promises_1.readFile)(envPath, 'utf-8');
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
raw = '';
|
|
74
|
+
}
|
|
75
|
+
const remove = !value;
|
|
76
|
+
const lines = raw.split('\n');
|
|
77
|
+
let found = false;
|
|
78
|
+
const result = [];
|
|
79
|
+
for (const line of lines) {
|
|
80
|
+
const trimmed = line.trim();
|
|
81
|
+
if (trimmed.startsWith('#') && trimmed.startsWith(`# ${key}=`)) {
|
|
82
|
+
if (!remove)
|
|
83
|
+
result.push(`${key}=${value}`);
|
|
84
|
+
found = true;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const eqIdx = trimmed.indexOf('=');
|
|
88
|
+
if (eqIdx !== -1 && trimmed.slice(0, eqIdx).trim() === key) {
|
|
89
|
+
if (!remove)
|
|
90
|
+
result.push(`${key}=${value}`);
|
|
91
|
+
found = true;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
result.push(line);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (!found && !remove) {
|
|
99
|
+
result.push(`${key}=${value}`);
|
|
100
|
+
}
|
|
101
|
+
let output = result.join('\n').replace(/\n{3,}/g, '\n\n').replace(/\n+$/, '') + '\n';
|
|
102
|
+
await (0, promises_1.writeFile)(envPath, output, 'utf-8');
|
|
103
|
+
}
|
|
104
|
+
const authPath = () => (0, hermes_profile_1.getActiveAuthPath)();
|
|
13
105
|
async function loadAuthJson() {
|
|
14
106
|
try {
|
|
15
|
-
const raw = await (0, promises_1.readFile)(authPath, 'utf-8');
|
|
107
|
+
const raw = await (0, promises_1.readFile)(authPath(), 'utf-8');
|
|
16
108
|
return JSON.parse(raw);
|
|
17
109
|
}
|
|
18
110
|
catch {
|
|
@@ -20,7 +112,7 @@ async function loadAuthJson() {
|
|
|
20
112
|
}
|
|
21
113
|
}
|
|
22
114
|
async function saveAuthJson(auth) {
|
|
23
|
-
await (0, promises_1.writeFile)(authPath, JSON.stringify(auth, null, 2) + '\n', 'utf-8');
|
|
115
|
+
await (0, promises_1.writeFile)(authPath(), JSON.stringify(auth, null, 2) + '\n', 'utf-8');
|
|
24
116
|
}
|
|
25
117
|
async function fetchProviderModels(baseUrl, apiKey) {
|
|
26
118
|
try {
|
|
@@ -49,7 +141,7 @@ async function fetchProviderModels(baseUrl, apiKey) {
|
|
|
49
141
|
const providers_1 = require("../../shared/providers");
|
|
50
142
|
const PROVIDER_MODEL_CATALOG = (0, providers_1.buildProviderModelMap)();
|
|
51
143
|
exports.fsRoutes = new router_1.default();
|
|
52
|
-
const hermesDir = (
|
|
144
|
+
const hermesDir = () => (0, hermes_profile_1.getActiveProfileDir)();
|
|
53
145
|
// --- Helpers ---
|
|
54
146
|
function extractDescription(content) {
|
|
55
147
|
const lines = content.split('\n');
|
|
@@ -95,26 +187,27 @@ async function safeStat(filePath) {
|
|
|
95
187
|
}
|
|
96
188
|
}
|
|
97
189
|
// --- Config YAML helpers ---
|
|
98
|
-
const configPath = (
|
|
190
|
+
const configPath = () => (0, hermes_profile_1.getActiveConfigPath)();
|
|
99
191
|
async function readConfigYaml() {
|
|
100
|
-
const raw = await safeReadFile(configPath);
|
|
192
|
+
const raw = await safeReadFile(configPath());
|
|
101
193
|
if (!raw)
|
|
102
194
|
return {};
|
|
103
195
|
return js_yaml_1.default.load(raw) || {};
|
|
104
196
|
}
|
|
105
197
|
async function writeConfigYaml(config) {
|
|
106
|
-
|
|
198
|
+
const cp = configPath();
|
|
199
|
+
await (0, promises_1.copyFile)(cp, cp + '.bak');
|
|
107
200
|
const yamlStr = js_yaml_1.default.dump(config, {
|
|
108
201
|
lineWidth: -1,
|
|
109
202
|
noRefs: true,
|
|
110
203
|
quotingType: '"',
|
|
111
204
|
});
|
|
112
|
-
await (0, promises_1.writeFile)(
|
|
205
|
+
await (0, promises_1.writeFile)(cp, yamlStr, 'utf-8');
|
|
113
206
|
}
|
|
114
207
|
// --- Skills Routes ---
|
|
115
208
|
// List all skills grouped by category
|
|
116
209
|
exports.fsRoutes.get('/api/hermes/skills', async (ctx) => {
|
|
117
|
-
const skillsDir = (0, path_1.join)(hermesDir, 'skills');
|
|
210
|
+
const skillsDir = (0, path_1.join)(hermesDir(), 'skills');
|
|
118
211
|
try {
|
|
119
212
|
// Read disabled skills list from config.yaml
|
|
120
213
|
const config = await readConfigYaml();
|
|
@@ -213,7 +306,7 @@ async function listFilesRecursive(dir, prefix) {
|
|
|
213
306
|
}
|
|
214
307
|
exports.fsRoutes.get('/api/hermes/skills/:category/:skill/files', async (ctx) => {
|
|
215
308
|
const { category, skill } = ctx.params;
|
|
216
|
-
const skillDir = (0, path_1.join)(hermesDir, 'skills', category, skill);
|
|
309
|
+
const skillDir = (0, path_1.join)(hermesDir(), 'skills', category, skill);
|
|
217
310
|
try {
|
|
218
311
|
const allFiles = await listFilesRecursive(skillDir, '');
|
|
219
312
|
const files = allFiles.filter(f => f.path !== 'SKILL.md');
|
|
@@ -227,8 +320,9 @@ exports.fsRoutes.get('/api/hermes/skills/:category/:skill/files', async (ctx) =>
|
|
|
227
320
|
// Read a specific file under skills/ (must be registered after the /files route)
|
|
228
321
|
exports.fsRoutes.get('/api/hermes/skills/{*path}', async (ctx) => {
|
|
229
322
|
const filePath = ctx.params.path;
|
|
230
|
-
const
|
|
231
|
-
|
|
323
|
+
const hd = hermesDir();
|
|
324
|
+
const fullPath = (0, path_1.resolve)((0, path_1.join)(hd, 'skills', filePath));
|
|
325
|
+
if (!fullPath.startsWith((0, path_1.join)(hd, 'skills'))) {
|
|
232
326
|
ctx.status = 403;
|
|
233
327
|
ctx.body = { error: 'Access denied' };
|
|
234
328
|
return;
|
|
@@ -243,19 +337,25 @@ exports.fsRoutes.get('/api/hermes/skills/{*path}', async (ctx) => {
|
|
|
243
337
|
});
|
|
244
338
|
// --- Memory Routes ---
|
|
245
339
|
exports.fsRoutes.get('/api/hermes/memory', async (ctx) => {
|
|
246
|
-
const
|
|
247
|
-
const
|
|
248
|
-
const
|
|
340
|
+
const hd = hermesDir();
|
|
341
|
+
const memoryPath = (0, path_1.join)(hd, 'memories', 'MEMORY.md');
|
|
342
|
+
const userPath = (0, path_1.join)(hd, 'memories', 'USER.md');
|
|
343
|
+
const soulPath = (0, path_1.join)(hd, 'SOUL.md');
|
|
344
|
+
const [memory, user, soul, memoryStat, userStat, soulStat] = await Promise.all([
|
|
249
345
|
safeReadFile(memoryPath),
|
|
250
346
|
safeReadFile(userPath),
|
|
347
|
+
safeReadFile(soulPath),
|
|
251
348
|
safeStat(memoryPath),
|
|
252
349
|
safeStat(userPath),
|
|
350
|
+
safeStat(soulPath),
|
|
253
351
|
]);
|
|
254
352
|
ctx.body = {
|
|
255
353
|
memory: memory || '',
|
|
256
354
|
user: user || '',
|
|
355
|
+
soul: soul || '',
|
|
257
356
|
memory_mtime: memoryStat?.mtime || null,
|
|
258
357
|
user_mtime: userStat?.mtime || null,
|
|
358
|
+
soul_mtime: soulStat?.mtime || null,
|
|
259
359
|
};
|
|
260
360
|
});
|
|
261
361
|
exports.fsRoutes.post('/api/hermes/memory', async (ctx) => {
|
|
@@ -265,13 +365,19 @@ exports.fsRoutes.post('/api/hermes/memory', async (ctx) => {
|
|
|
265
365
|
ctx.body = { error: 'Missing section or content' };
|
|
266
366
|
return;
|
|
267
367
|
}
|
|
268
|
-
if (section !== 'memory' && section !== 'user') {
|
|
368
|
+
if (section !== 'memory' && section !== 'user' && section !== 'soul') {
|
|
269
369
|
ctx.status = 400;
|
|
270
|
-
ctx.body = { error: 'Section must be "memory" or "
|
|
370
|
+
ctx.body = { error: 'Section must be "memory", "user", or "soul"' };
|
|
271
371
|
return;
|
|
272
372
|
}
|
|
273
|
-
|
|
274
|
-
|
|
373
|
+
let filePath;
|
|
374
|
+
if (section === 'soul') {
|
|
375
|
+
filePath = (0, path_1.join)(hermesDir(), 'SOUL.md');
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
const fileName = section === 'memory' ? 'MEMORY.md' : 'USER.md';
|
|
379
|
+
filePath = (0, path_1.join)(hermesDir(), 'memories', fileName);
|
|
380
|
+
}
|
|
275
381
|
try {
|
|
276
382
|
await (0, promises_1.writeFile)(filePath, content, 'utf-8');
|
|
277
383
|
ctx.body = { success: true };
|
|
@@ -444,16 +550,20 @@ exports.fsRoutes.post('/api/hermes/config/providers', async (ctx) => {
|
|
|
444
550
|
return;
|
|
445
551
|
}
|
|
446
552
|
try {
|
|
447
|
-
//
|
|
448
|
-
const config = await readConfigYaml();
|
|
449
|
-
if (!Array.isArray(config.custom_providers)) {
|
|
450
|
-
config.custom_providers = [];
|
|
451
|
-
}
|
|
452
|
-
config.custom_providers.push({ name, base_url, api_key, model });
|
|
453
|
-
await writeConfigYaml(config);
|
|
454
|
-
// 2. Write to auth.json credential_pool
|
|
553
|
+
// Determine if this is a built-in provider or a custom one
|
|
455
554
|
const poolKey = providerKey
|
|
456
555
|
|| `custom:${name.trim().toLowerCase().replace(/ /g, '-')}`;
|
|
556
|
+
const isBuiltin = poolKey in PROVIDER_ENV_MAP;
|
|
557
|
+
if (!isBuiltin) {
|
|
558
|
+
// Custom provider: write to config.yaml custom_providers
|
|
559
|
+
const config = await readConfigYaml();
|
|
560
|
+
if (!Array.isArray(config.custom_providers)) {
|
|
561
|
+
config.custom_providers = [];
|
|
562
|
+
}
|
|
563
|
+
config.custom_providers.push({ name, base_url, api_key, model });
|
|
564
|
+
await writeConfigYaml(config);
|
|
565
|
+
}
|
|
566
|
+
// Write to auth.json credential_pool (all providers)
|
|
457
567
|
const auth = await loadAuthJson() || { credential_pool: {} };
|
|
458
568
|
if (!auth.credential_pool)
|
|
459
569
|
auth.credential_pool = {};
|
|
@@ -468,7 +578,15 @@ exports.fsRoutes.post('/api/hermes/config/providers', async (ctx) => {
|
|
|
468
578
|
last_status: null,
|
|
469
579
|
});
|
|
470
580
|
await saveAuthJson(auth);
|
|
471
|
-
//
|
|
581
|
+
// Write API key to .env (built-in providers only)
|
|
582
|
+
const envMapping = PROVIDER_ENV_MAP[poolKey] || PROVIDER_ENV_MAP[providerKey || ''];
|
|
583
|
+
if (envMapping) {
|
|
584
|
+
await saveEnvValue(envMapping.api_key_env, api_key);
|
|
585
|
+
if (envMapping.base_url_env) {
|
|
586
|
+
await saveEnvValue(envMapping.base_url_env, base_url);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
// Auto-switch model to the newly added provider
|
|
472
590
|
const config2 = await readConfigYaml();
|
|
473
591
|
if (typeof config2.model !== 'object' || config2.model === null) {
|
|
474
592
|
config2.model = {};
|
|
@@ -476,6 +594,13 @@ exports.fsRoutes.post('/api/hermes/config/providers', async (ctx) => {
|
|
|
476
594
|
config2.model.default = model;
|
|
477
595
|
config2.model.provider = poolKey;
|
|
478
596
|
await writeConfigYaml(config2);
|
|
597
|
+
// Restart gateway to pick up .env and config.yaml changes
|
|
598
|
+
try {
|
|
599
|
+
await hermesCli.restartGateway();
|
|
600
|
+
}
|
|
601
|
+
catch (e) {
|
|
602
|
+
console.error('[Provider] Gateway restart failed:', e.message);
|
|
603
|
+
}
|
|
479
604
|
ctx.body = { success: true };
|
|
480
605
|
}
|
|
481
606
|
catch (err) {
|