hermes-web-ui 0.4.2 → 0.4.3
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/bin/hermes-web-ui.mjs +76 -9
- package/dist/client/assets/Add-DiTv65Se.js +1 -0
- package/dist/client/assets/{Button-pgPN7RfC.js → Button-vKfgky-Y.js} +3 -3
- package/dist/client/assets/{ChannelsView-qFMvJ9rS.js → ChannelsView-CHWtzAkF.js} +1 -1
- package/dist/client/assets/ChatView--lvFbW3I.js +127 -0
- package/dist/client/assets/ChatView-b0_L3ucp.css +1 -0
- package/dist/client/assets/{Close-CXC76d7R.js → Close-Brba3ay5.js} +2 -2
- package/dist/client/assets/{FormItem-DIcSMjKz.js → FormItem-kXuCeSKq.js} +3 -3
- package/dist/client/assets/GatewaysView-DUPP1UyD.js +1 -0
- package/dist/client/assets/Input-BS71ZaoJ.js +234 -0
- package/dist/client/assets/{InputNumber-Btyn8bQo.js → InputNumber-BXy4reTI.js} +2 -2
- package/dist/client/assets/JobsView-CXa1h0NF.js +2 -0
- package/dist/client/assets/LoginView-Cpod3SAL.js +1 -0
- package/dist/client/assets/LoginView-DfWYTzX1.css +1 -0
- package/dist/client/assets/LogsView-CgxeVMn-.js +1 -0
- package/dist/client/assets/{MarkdownRenderer-G4nly1xR.js → MarkdownRenderer-B7t0ZEw0.js} +9 -9
- package/dist/client/assets/MemoryView-BQjYVDJc.js +7 -0
- package/dist/client/assets/{Modal-rKb1tfQv.js → Modal-6Bv1v4kO.js} +4 -4
- package/dist/client/assets/ModelsView-CVghPzgk.js +1 -0
- package/dist/client/assets/Popconfirm-Bpz_dC1j.js +16 -0
- package/dist/client/assets/Popover-CXrjClQw.js +117 -0
- package/dist/client/assets/ProfilesView-Brpyzl-Q.js +440 -0
- package/dist/client/assets/{Select-CpNY3UGb.js → Select-Z20Mp4nQ.js} +5 -5
- package/dist/client/assets/SettingRow-Cz749k-e.js +1 -0
- package/dist/client/assets/SettingsView-C3HbKb4w.js +352 -0
- package/dist/client/assets/{SettingsView-CX312EGG.css → SettingsView-Cw4iOwXj.css} +1 -1
- package/dist/client/assets/SkillsView-qVZkbI37.js +1 -0
- package/dist/client/assets/Spin-DKwUVt1F.js +43 -0
- package/dist/client/assets/Suffix-C1Mw6UwH.js +90 -0
- package/dist/client/assets/{Switch-haCJAcVn.js → Switch-DG-3EwdR.js} +2 -2
- package/dist/client/assets/{Tag-CKzgX6hi.js → Tag-BWD1u63m.js} +2 -2
- package/dist/client/assets/{TerminalView-CmSON012.js → TerminalView-DTR8bNOy.js} +6 -6
- package/dist/client/assets/Tooltip-Dka1-DNs.js +1 -0
- package/dist/client/assets/UsageView-elTUQCED.js +1 -0
- package/dist/client/assets/{Warning-B2AD2ulw.js → Warning-CCZES7el.js} +1 -1
- package/dist/client/assets/{_plugin-vue_export-helper-DXZ6IauR.js → _plugin-vue_export-helper-Bl_Pm_OI.js} +2 -2
- package/dist/client/assets/app-BMD_VOYs.js +1 -0
- package/dist/client/assets/{app-Du5RZCLa.js → app-y8cD3FPg.js} +1 -1
- package/dist/client/assets/auth-BPBv1nNQ.js +1 -0
- package/dist/client/assets/browser-DlPqZ-4M.js +47 -0
- package/dist/client/assets/chat-3-o9JAHE.js +6 -0
- package/dist/client/assets/composables-CoSqDggz.js +1 -0
- package/dist/client/assets/{fade-in.cssr-CkQxIGt5.js → fade-in.cssr-D0suZaLL.js} +2 -2
- package/dist/client/assets/index-BRXOdlZt.css +1 -0
- package/dist/client/assets/index-CeFFa_Wg.js +284 -0
- package/dist/client/assets/{jobs-BnXwbdOs.js → jobs-DMiCP2Li.js} +1 -1
- package/dist/client/assets/light-BNuYwJcs.js +1 -0
- package/dist/client/assets/{light-BZfVIZ0W.js → light-DLz3o2il.js} +1 -1
- package/dist/client/assets/{light-Bdfae2y3.js → light-DbUQYfY2.js} +1 -1
- package/dist/client/assets/{light-Jn66LOmu.js → light-DcGe0-g1.js} +1 -1
- package/dist/client/assets/{light-Dgzhn0jz.js → light-cIgLWDVk.js} +1 -1
- package/dist/client/assets/{models-mCQyOPuV.js → models-EKNn3-zC.js} +1 -1
- package/dist/client/assets/{pinia-Jb4QOAzE.js → pinia-C8BDM-Fc.js} +1 -1
- package/dist/client/assets/profiles-1vZTTlBc.js +1 -0
- package/dist/client/assets/{router-qWVkGgap.js → router-kR4kqS2l.js} +2 -2
- package/dist/client/assets/{session-browser-prefs-Bh1and1f.js → session-browser-prefs-BI-rZu2N.js} +1 -1
- package/dist/client/assets/sessions-CJI89wYu.js +1 -0
- package/dist/client/assets/{skills-BIFy9nXD.js → skills-DjdZmDZP.js} +1 -1
- package/dist/client/assets/{use-message-Do_vF1fs.js → use-message-sSsrvqld.js} +1 -1
- package/dist/client/assets/{useTheme-BFm8wMmc.js → useTheme-DgQIobZP.js} +1 -1
- package/dist/client/index.html +29 -30
- package/dist/server/index.js +1067 -329
- package/dist/server/index.js.map +4 -4
- package/package.json +2 -2
- package/dist/client/assets/Add-COjp_zn3.js +0 -1
- package/dist/client/assets/ChatView-DVQv3z6i.js +0 -127
- package/dist/client/assets/ChatView-w9DKSiie.css +0 -1
- package/dist/client/assets/GatewaysView-BeaZvbdd.js +0 -1
- package/dist/client/assets/Input-CxMM68Q2.js +0 -234
- package/dist/client/assets/JobsView-DhosFF52.js +0 -2
- package/dist/client/assets/LoginView-DHPxG3eC.css +0 -1
- package/dist/client/assets/LoginView-DrjDX7xD.js +0 -1
- package/dist/client/assets/LogsView-BpW_vgEs.js +0 -1
- package/dist/client/assets/MemoryView-CP3K8mDG.js +0 -7
- package/dist/client/assets/ModelsView-DdQUiiu6.js +0 -1
- package/dist/client/assets/Popconfirm-DnYCLqNF.js +0 -16
- package/dist/client/assets/Popover-DAXLiB78.js +0 -117
- package/dist/client/assets/ProfilesView-Bkhqt3QB.js +0 -440
- package/dist/client/assets/SettingRow-B4_BuJSm.js +0 -1
- package/dist/client/assets/SettingsView-ZwzxJZVc.js +0 -352
- package/dist/client/assets/SkillsView-B3X9y2kZ.js +0 -1
- package/dist/client/assets/Spin-CAOjab_F.js +0 -43
- package/dist/client/assets/Suffix-DQWmhzzW.js +0 -90
- package/dist/client/assets/Tooltip-U6DmMv6W.js +0 -1
- package/dist/client/assets/UsageView-rmIKdR1u.js +0 -1
- package/dist/client/assets/app-lD2ZIoa4.js +0 -1
- package/dist/client/assets/browser-DcLLT85r.js +0 -47
- package/dist/client/assets/chat-DJP53-h1.js +0 -6
- package/dist/client/assets/composables-CjmuafMO.js +0 -1
- package/dist/client/assets/index-BfU2Be-n.css +0 -1
- package/dist/client/assets/index-BqaBjme9.js +0 -284
- package/dist/client/assets/light-ZuZhSzix.js +0 -1
- package/dist/client/assets/light-ba4gwt1q.js +0 -1
- package/dist/client/assets/omit-1BRB6K75.js +0 -1
- package/dist/client/assets/profiles-PisKQKsd.js +0 -1
- package/dist/client/assets/sessions-smjenK_5.js +0 -1
- /package/dist/client/assets/{_common-Yp55QE79.js → _common-D_mHAddk.js} +0 -0
package/dist/server/index.js
CHANGED
|
@@ -33,6 +33,176 @@ var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__
|
|
|
33
33
|
));
|
|
34
34
|
var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
|
|
35
35
|
|
|
36
|
+
// packages/server/src/db/index.ts
|
|
37
|
+
function isSqliteAvailable() {
|
|
38
|
+
return SQLITE_AVAILABLE;
|
|
39
|
+
}
|
|
40
|
+
function getDb() {
|
|
41
|
+
if (!SQLITE_AVAILABLE) return null;
|
|
42
|
+
if (!_db) {
|
|
43
|
+
(0, import_fs.mkdirSync)(DB_DIR, { recursive: true });
|
|
44
|
+
_db = new import_node_sqlite.DatabaseSync(DB_PATH);
|
|
45
|
+
_db.exec("PRAGMA journal_mode=WAL");
|
|
46
|
+
_db.exec("PRAGMA foreign_keys=ON");
|
|
47
|
+
}
|
|
48
|
+
return _db;
|
|
49
|
+
}
|
|
50
|
+
function ensureTable(tableName, schema2) {
|
|
51
|
+
const db = getDb();
|
|
52
|
+
if (!db) return;
|
|
53
|
+
const colDefs = Object.entries(schema2).map(([col, def]) => `"${col}" ${def}`).join(", ");
|
|
54
|
+
db.exec(`CREATE TABLE IF NOT EXISTS "${tableName}" (${colDefs})`);
|
|
55
|
+
const rows = db.prepare(`PRAGMA table_info("${tableName}")`).all();
|
|
56
|
+
const existingCols = new Set(rows.map((r) => r.name));
|
|
57
|
+
const expectedCols = new Set(Object.keys(schema2));
|
|
58
|
+
for (const col of expectedCols) {
|
|
59
|
+
if (!existingCols.has(col)) {
|
|
60
|
+
db.exec(`ALTER TABLE "${tableName}" ADD COLUMN "${col}" ${schema2[col]}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const col of existingCols) {
|
|
64
|
+
if (!expectedCols.has(col)) {
|
|
65
|
+
db.exec(`ALTER TABLE "${tableName}" DROP COLUMN "${col}"`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function readJsonStore() {
|
|
70
|
+
if (!(0, import_fs.existsSync)(JSON_PATH)) return {};
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse((0, import_fs.readFileSync)(JSON_PATH, "utf-8"));
|
|
73
|
+
} catch {
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function writeJsonStore(data) {
|
|
78
|
+
(0, import_fs.mkdirSync)(DB_DIR, { recursive: true });
|
|
79
|
+
(0, import_fs.writeFileSync)(JSON_PATH, JSON.stringify(data, null, 2), "utf-8");
|
|
80
|
+
}
|
|
81
|
+
function jsonGet(table, key) {
|
|
82
|
+
const data = readJsonStore();
|
|
83
|
+
return data[table]?.[key];
|
|
84
|
+
}
|
|
85
|
+
function jsonSet(table, key, value) {
|
|
86
|
+
const data = readJsonStore();
|
|
87
|
+
if (!data[table]) data[table] = {};
|
|
88
|
+
data[table][key] = value;
|
|
89
|
+
writeJsonStore(data);
|
|
90
|
+
}
|
|
91
|
+
function jsonGetAll(table) {
|
|
92
|
+
const data = readJsonStore();
|
|
93
|
+
return data[table] || {};
|
|
94
|
+
}
|
|
95
|
+
function jsonDelete(table, key) {
|
|
96
|
+
const data = readJsonStore();
|
|
97
|
+
if (data[table]) {
|
|
98
|
+
delete data[table][key];
|
|
99
|
+
writeJsonStore(data);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function getStoragePath() {
|
|
103
|
+
return SQLITE_AVAILABLE ? DB_PATH : JSON_PATH;
|
|
104
|
+
}
|
|
105
|
+
var import_node_sqlite, import_fs, import_path, import_os, DB_DIR, DB_PATH, JSON_PATH, SQLITE_AVAILABLE, _db;
|
|
106
|
+
var init_db = __esm({
|
|
107
|
+
"packages/server/src/db/index.ts"() {
|
|
108
|
+
"use strict";
|
|
109
|
+
import_node_sqlite = require("node:sqlite");
|
|
110
|
+
import_fs = require("fs");
|
|
111
|
+
import_path = require("path");
|
|
112
|
+
import_os = require("os");
|
|
113
|
+
DB_DIR = (0, import_path.resolve)((0, import_os.homedir)(), ".hermes-web-ui");
|
|
114
|
+
DB_PATH = (0, import_path.resolve)(DB_DIR, "hermes-web-ui.db");
|
|
115
|
+
JSON_PATH = (0, import_path.resolve)(DB_DIR, "hermes-web-ui.json");
|
|
116
|
+
SQLITE_AVAILABLE = (() => {
|
|
117
|
+
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
118
|
+
return major > 22 || major === 22 && minor >= 5;
|
|
119
|
+
})();
|
|
120
|
+
_db = null;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// packages/server/src/db/hermes/usage-store.ts
|
|
125
|
+
var usage_store_exports = {};
|
|
126
|
+
__export(usage_store_exports, {
|
|
127
|
+
deleteUsage: () => deleteUsage,
|
|
128
|
+
getUsage: () => getUsage,
|
|
129
|
+
getUsageBatch: () => getUsageBatch,
|
|
130
|
+
initUsageStore: () => initUsageStore,
|
|
131
|
+
updateUsage: () => updateUsage
|
|
132
|
+
});
|
|
133
|
+
function initUsageStore() {
|
|
134
|
+
if (isSqliteAvailable()) {
|
|
135
|
+
ensureTable(TABLE, SCHEMA);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function updateUsage(sessionId, inputTokens, outputTokens) {
|
|
139
|
+
const record = { input_tokens: inputTokens, output_tokens: outputTokens, updated_at: Date.now() };
|
|
140
|
+
if (isSqliteAvailable()) {
|
|
141
|
+
const db = getDb();
|
|
142
|
+
db.prepare(
|
|
143
|
+
`INSERT INTO ${TABLE} (session_id, input_tokens, output_tokens, updated_at)
|
|
144
|
+
VALUES (?, ?, ?, ?)
|
|
145
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
146
|
+
input_tokens = excluded.input_tokens,
|
|
147
|
+
output_tokens = excluded.output_tokens,
|
|
148
|
+
updated_at = excluded.updated_at`
|
|
149
|
+
).run(sessionId, inputTokens, outputTokens, record.updated_at);
|
|
150
|
+
} else {
|
|
151
|
+
jsonSet(TABLE, sessionId, record);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function getUsage(sessionId) {
|
|
155
|
+
if (isSqliteAvailable()) {
|
|
156
|
+
return getDb().prepare(
|
|
157
|
+
`SELECT input_tokens, output_tokens FROM ${TABLE} WHERE session_id = ?`
|
|
158
|
+
).get(sessionId);
|
|
159
|
+
}
|
|
160
|
+
const row = jsonGet(TABLE, sessionId);
|
|
161
|
+
if (!row) return void 0;
|
|
162
|
+
return { input_tokens: row.input_tokens ?? 0, output_tokens: row.output_tokens ?? 0 };
|
|
163
|
+
}
|
|
164
|
+
function getUsageBatch(sessionIds) {
|
|
165
|
+
if (sessionIds.length === 0) return {};
|
|
166
|
+
if (isSqliteAvailable()) {
|
|
167
|
+
const db = getDb();
|
|
168
|
+
const placeholders = sessionIds.map(() => "?").join(",");
|
|
169
|
+
const rows = db.prepare(
|
|
170
|
+
`SELECT session_id, input_tokens, output_tokens FROM ${TABLE} WHERE session_id IN (${placeholders})`
|
|
171
|
+
).all(...sessionIds);
|
|
172
|
+
const map3 = {};
|
|
173
|
+
for (const r of rows) map3[r.session_id] = { input_tokens: r.input_tokens, output_tokens: r.output_tokens };
|
|
174
|
+
return map3;
|
|
175
|
+
}
|
|
176
|
+
const all3 = jsonGetAll(TABLE);
|
|
177
|
+
const map2 = {};
|
|
178
|
+
for (const id of sessionIds) {
|
|
179
|
+
const row = all3[id];
|
|
180
|
+
if (row) map2[id] = { input_tokens: row.input_tokens ?? 0, output_tokens: row.output_tokens ?? 0 };
|
|
181
|
+
}
|
|
182
|
+
return map2;
|
|
183
|
+
}
|
|
184
|
+
function deleteUsage(sessionId) {
|
|
185
|
+
if (isSqliteAvailable()) {
|
|
186
|
+
getDb().prepare(`DELETE FROM ${TABLE} WHERE session_id = ?`).run(sessionId);
|
|
187
|
+
} else {
|
|
188
|
+
jsonDelete(TABLE, sessionId);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
var TABLE, SCHEMA;
|
|
192
|
+
var init_usage_store = __esm({
|
|
193
|
+
"packages/server/src/db/hermes/usage-store.ts"() {
|
|
194
|
+
"use strict";
|
|
195
|
+
init_db();
|
|
196
|
+
TABLE = "session_usage";
|
|
197
|
+
SCHEMA = {
|
|
198
|
+
session_id: "TEXT PRIMARY KEY",
|
|
199
|
+
input_tokens: "INTEGER NOT NULL DEFAULT 0",
|
|
200
|
+
output_tokens: "INTEGER NOT NULL DEFAULT 0",
|
|
201
|
+
updated_at: "INTEGER NOT NULL"
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
36
206
|
// node_modules/es-object-atoms/index.js
|
|
37
207
|
var require_es_object_atoms = __commonJS({
|
|
38
208
|
"node_modules/es-object-atoms/index.js"(exports2, module2) {
|
|
@@ -1231,13 +1401,13 @@ var require_common = __commonJS({
|
|
|
1231
1401
|
}
|
|
1232
1402
|
}
|
|
1233
1403
|
}
|
|
1234
|
-
function matchesTemplate(
|
|
1404
|
+
function matchesTemplate(search2, template) {
|
|
1235
1405
|
let searchIndex = 0;
|
|
1236
1406
|
let templateIndex = 0;
|
|
1237
1407
|
let starIndex = -1;
|
|
1238
1408
|
let matchIndex = 0;
|
|
1239
|
-
while (searchIndex <
|
|
1240
|
-
if (templateIndex < template.length && (template[templateIndex] ===
|
|
1409
|
+
while (searchIndex < search2.length) {
|
|
1410
|
+
if (templateIndex < template.length && (template[templateIndex] === search2[searchIndex] || template[templateIndex] === "*")) {
|
|
1241
1411
|
if (template[templateIndex] === "*") {
|
|
1242
1412
|
starIndex = templateIndex;
|
|
1243
1413
|
matchIndex = searchIndex;
|
|
@@ -15038,14 +15208,14 @@ var require_parseurl = __commonJS({
|
|
|
15038
15208
|
}
|
|
15039
15209
|
var pathname = str2;
|
|
15040
15210
|
var query = null;
|
|
15041
|
-
var
|
|
15211
|
+
var search2 = null;
|
|
15042
15212
|
for (var i = 1; i < str2.length; i++) {
|
|
15043
15213
|
switch (str2.charCodeAt(i)) {
|
|
15044
15214
|
case 63:
|
|
15045
|
-
if (
|
|
15215
|
+
if (search2 === null) {
|
|
15046
15216
|
pathname = str2.substring(0, i);
|
|
15047
15217
|
query = str2.substring(i + 1);
|
|
15048
|
-
|
|
15218
|
+
search2 = str2.substring(i);
|
|
15049
15219
|
}
|
|
15050
15220
|
break;
|
|
15051
15221
|
case 9:
|
|
@@ -15069,9 +15239,9 @@ var require_parseurl = __commonJS({
|
|
|
15069
15239
|
url3.path = str2;
|
|
15070
15240
|
url3.href = str2;
|
|
15071
15241
|
url3.pathname = pathname;
|
|
15072
|
-
if (
|
|
15242
|
+
if (search2 !== null) {
|
|
15073
15243
|
url3.query = query;
|
|
15074
|
-
url3.search =
|
|
15244
|
+
url3.search = search2;
|
|
15075
15245
|
}
|
|
15076
15246
|
return url3;
|
|
15077
15247
|
}
|
|
@@ -15788,9 +15958,9 @@ var require_co = __commonJS({
|
|
|
15788
15958
|
function co(gen) {
|
|
15789
15959
|
var ctx = this;
|
|
15790
15960
|
var args2 = slice.call(arguments, 1);
|
|
15791
|
-
return new Promise(function(
|
|
15961
|
+
return new Promise(function(resolve10, reject) {
|
|
15792
15962
|
if (typeof gen === "function") gen = gen.apply(ctx, args2);
|
|
15793
|
-
if (!gen || typeof gen.next !== "function") return
|
|
15963
|
+
if (!gen || typeof gen.next !== "function") return resolve10(gen);
|
|
15794
15964
|
onFulfilled();
|
|
15795
15965
|
function onFulfilled(res) {
|
|
15796
15966
|
var ret;
|
|
@@ -15811,7 +15981,7 @@ var require_co = __commonJS({
|
|
|
15811
15981
|
next(ret);
|
|
15812
15982
|
}
|
|
15813
15983
|
function next(ret) {
|
|
15814
|
-
if (ret.done) return
|
|
15984
|
+
if (ret.done) return resolve10(ret.value);
|
|
15815
15985
|
var value = toPromise.call(ctx, ret.value);
|
|
15816
15986
|
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
|
|
15817
15987
|
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, but the following object was passed: "' + String(ret.value) + '"'));
|
|
@@ -15829,11 +15999,11 @@ var require_co = __commonJS({
|
|
|
15829
15999
|
}
|
|
15830
16000
|
function thunkToPromise(fn2) {
|
|
15831
16001
|
var ctx = this;
|
|
15832
|
-
return new Promise(function(
|
|
16002
|
+
return new Promise(function(resolve10, reject) {
|
|
15833
16003
|
fn2.call(ctx, function(err, res) {
|
|
15834
16004
|
if (err) return reject(err);
|
|
15835
16005
|
if (arguments.length > 2) res = slice.call(arguments, 1);
|
|
15836
|
-
|
|
16006
|
+
resolve10(res);
|
|
15837
16007
|
});
|
|
15838
16008
|
});
|
|
15839
16009
|
}
|
|
@@ -20211,10 +20381,10 @@ var require_raw_body = __commonJS({
|
|
|
20211
20381
|
if (done) {
|
|
20212
20382
|
return readStream2(stream4, encoding, length, limit, wrap(done));
|
|
20213
20383
|
}
|
|
20214
|
-
return new Promise(function executor(
|
|
20384
|
+
return new Promise(function executor(resolve10, reject) {
|
|
20215
20385
|
readStream2(stream4, encoding, length, limit, function onRead(err, buf) {
|
|
20216
20386
|
if (err) return reject(err);
|
|
20217
|
-
|
|
20387
|
+
resolve10(buf);
|
|
20218
20388
|
});
|
|
20219
20389
|
});
|
|
20220
20390
|
}
|
|
@@ -24106,10 +24276,10 @@ var require_resolve_path = __commonJS({
|
|
|
24106
24276
|
"node_modules/resolve-path/index.js"(exports2, module2) {
|
|
24107
24277
|
"use strict";
|
|
24108
24278
|
var createError = require_http_errors4();
|
|
24109
|
-
var
|
|
24279
|
+
var join14 = require("path").join;
|
|
24110
24280
|
var normalize = require("path").normalize;
|
|
24111
24281
|
var pathIsAbsolute = require_path_is_absolute();
|
|
24112
|
-
var
|
|
24282
|
+
var resolve10 = require("path").resolve;
|
|
24113
24283
|
var sep = require("path").sep;
|
|
24114
24284
|
module2.exports = resolvePath;
|
|
24115
24285
|
var UP_PATH_REGEXP = /(?:^|[\\/])\.\.(?:[\\/]|$)/;
|
|
@@ -24141,7 +24311,7 @@ var require_resolve_path = __commonJS({
|
|
|
24141
24311
|
if (UP_PATH_REGEXP.test(normalize("." + sep + path))) {
|
|
24142
24312
|
throw createError(403);
|
|
24143
24313
|
}
|
|
24144
|
-
return normalize(
|
|
24314
|
+
return normalize(join14(resolve10(root), path));
|
|
24145
24315
|
}
|
|
24146
24316
|
}
|
|
24147
24317
|
});
|
|
@@ -24900,7 +25070,7 @@ var require_koa_send = __commonJS({
|
|
|
24900
25070
|
normalize,
|
|
24901
25071
|
basename: basename2,
|
|
24902
25072
|
extname,
|
|
24903
|
-
resolve:
|
|
25073
|
+
resolve: resolve10,
|
|
24904
25074
|
parse: parse2,
|
|
24905
25075
|
sep
|
|
24906
25076
|
} = require("path");
|
|
@@ -24909,7 +25079,7 @@ var require_koa_send = __commonJS({
|
|
|
24909
25079
|
assert(ctx, "koa context required");
|
|
24910
25080
|
assert(path, "pathname required");
|
|
24911
25081
|
debug2('send "%s" %j', path, opts);
|
|
24912
|
-
const root = opts.root ? normalize(
|
|
25082
|
+
const root = opts.root ? normalize(resolve10(opts.root)) : "";
|
|
24913
25083
|
const trailingSlash = path[path.length - 1] === "/";
|
|
24914
25084
|
path = path.substr(parse2(path).root.length);
|
|
24915
25085
|
const index = opts.index;
|
|
@@ -25013,7 +25183,7 @@ var require_koa_static = __commonJS({
|
|
|
25013
25183
|
"node_modules/koa-static/index.js"(exports2, module2) {
|
|
25014
25184
|
"use strict";
|
|
25015
25185
|
var debug2 = require_src2()("koa-static");
|
|
25016
|
-
var { resolve:
|
|
25186
|
+
var { resolve: resolve10 } = require("path");
|
|
25017
25187
|
var assert = require("assert");
|
|
25018
25188
|
var send2 = require_koa_send();
|
|
25019
25189
|
module2.exports = serve2;
|
|
@@ -25021,7 +25191,7 @@ var require_koa_static = __commonJS({
|
|
|
25021
25191
|
opts = Object.assign({}, opts);
|
|
25022
25192
|
assert(root, "root directory is required to serve files");
|
|
25023
25193
|
debug2('static "%s" %j', root, opts);
|
|
25024
|
-
opts.root =
|
|
25194
|
+
opts.root = resolve10(root);
|
|
25025
25195
|
if (opts.index !== false) opts.index = opts.index || "index.html";
|
|
25026
25196
|
if (!opts.defer) {
|
|
25027
25197
|
return async function serve3(ctx, next) {
|
|
@@ -28931,7 +29101,7 @@ var require_sonic_boom = __commonJS({
|
|
|
28931
29101
|
if (!(this instanceof SonicBoom)) {
|
|
28932
29102
|
return new SonicBoom(opts);
|
|
28933
29103
|
}
|
|
28934
|
-
let { fd, dest, minLength, maxLength, maxWrite, periodicFlush, sync, append: append2 = true, mkdir:
|
|
29104
|
+
let { fd, dest, minLength, maxLength, maxWrite, periodicFlush, sync, append: append2 = true, mkdir: mkdir5, retryEAGAIN, fsync, contentMode, mode } = opts || {};
|
|
28935
29105
|
fd = fd || dest;
|
|
28936
29106
|
this._len = 0;
|
|
28937
29107
|
this.fd = -1;
|
|
@@ -28956,7 +29126,7 @@ var require_sonic_boom = __commonJS({
|
|
|
28956
29126
|
this.append = append2 || false;
|
|
28957
29127
|
this.mode = mode;
|
|
28958
29128
|
this.retryEAGAIN = retryEAGAIN || (() => true);
|
|
28959
|
-
this.mkdir =
|
|
29129
|
+
this.mkdir = mkdir5 || false;
|
|
28960
29130
|
let fsWriteSync;
|
|
28961
29131
|
let fsWrite;
|
|
28962
29132
|
if (contentMode === kContentModeBuffer) {
|
|
@@ -29662,7 +29832,7 @@ var require_thread_stream = __commonJS({
|
|
|
29662
29832
|
var { version } = require_package();
|
|
29663
29833
|
var { EventEmitter: EventEmitter2 } = require("events");
|
|
29664
29834
|
var { Worker } = require("worker_threads");
|
|
29665
|
-
var { join:
|
|
29835
|
+
var { join: join14 } = require("path");
|
|
29666
29836
|
var { pathToFileURL } = require("url");
|
|
29667
29837
|
var { wait } = require_wait();
|
|
29668
29838
|
var {
|
|
@@ -29698,7 +29868,7 @@ var require_thread_stream = __commonJS({
|
|
|
29698
29868
|
function createWorker(stream4, opts) {
|
|
29699
29869
|
const { filename, workerData } = opts;
|
|
29700
29870
|
const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
|
|
29701
|
-
const toExecute = bundlerOverrides["thread-stream-worker"] ||
|
|
29871
|
+
const toExecute = bundlerOverrides["thread-stream-worker"] || join14(__dirname, "lib", "worker.js");
|
|
29702
29872
|
const worker = new Worker(toExecute, {
|
|
29703
29873
|
...opts.workerOpts,
|
|
29704
29874
|
trackUnmanagedFds: false,
|
|
@@ -30087,9 +30257,9 @@ var require_transport = __commonJS({
|
|
|
30087
30257
|
"node_modules/pino/lib/transport.js"(exports2, module2) {
|
|
30088
30258
|
"use strict";
|
|
30089
30259
|
var { createRequire } = require("module");
|
|
30090
|
-
var { existsSync:
|
|
30260
|
+
var { existsSync: existsSync14 } = require("node:fs");
|
|
30091
30261
|
var getCallers = require_caller();
|
|
30092
|
-
var { join:
|
|
30262
|
+
var { join: join14, isAbsolute, sep } = require("node:path");
|
|
30093
30263
|
var { fileURLToPath } = require("node:url");
|
|
30094
30264
|
var sleep = require_atomic_sleep();
|
|
30095
30265
|
var onExit = require_on_exit_leak_free();
|
|
@@ -30161,7 +30331,7 @@ var require_transport = __commonJS({
|
|
|
30161
30331
|
return false;
|
|
30162
30332
|
}
|
|
30163
30333
|
}
|
|
30164
|
-
return isAbsolute(path) && !
|
|
30334
|
+
return isAbsolute(path) && !existsSync14(path);
|
|
30165
30335
|
}
|
|
30166
30336
|
function stripQuotes(value) {
|
|
30167
30337
|
const first = value[0];
|
|
@@ -30242,7 +30412,7 @@ var require_transport = __commonJS({
|
|
|
30242
30412
|
throw new Error("only one of target or targets can be specified");
|
|
30243
30413
|
}
|
|
30244
30414
|
if (targets) {
|
|
30245
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
30415
|
+
target = bundlerOverrides["pino-worker"] || join14(__dirname, "worker.js");
|
|
30246
30416
|
options.targets = targets.filter((dest) => dest.target).map((dest) => {
|
|
30247
30417
|
return {
|
|
30248
30418
|
...dest,
|
|
@@ -30260,7 +30430,7 @@ var require_transport = __commonJS({
|
|
|
30260
30430
|
});
|
|
30261
30431
|
});
|
|
30262
30432
|
} else if (pipeline) {
|
|
30263
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
30433
|
+
target = bundlerOverrides["pino-worker"] || join14(__dirname, "worker.js");
|
|
30264
30434
|
options.pipelines = [pipeline.map((dest) => {
|
|
30265
30435
|
return {
|
|
30266
30436
|
...dest,
|
|
@@ -30283,7 +30453,7 @@ var require_transport = __commonJS({
|
|
|
30283
30453
|
return origin2;
|
|
30284
30454
|
}
|
|
30285
30455
|
if (origin2 === "pino/file") {
|
|
30286
|
-
return
|
|
30456
|
+
return join14(__dirname, "..", "file.js");
|
|
30287
30457
|
}
|
|
30288
30458
|
let fixTarget2;
|
|
30289
30459
|
for (const filePath of callers) {
|
|
@@ -31263,7 +31433,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
31263
31433
|
return circularValue;
|
|
31264
31434
|
}
|
|
31265
31435
|
let res = "";
|
|
31266
|
-
let
|
|
31436
|
+
let join14 = ",";
|
|
31267
31437
|
const originalIndentation = indentation;
|
|
31268
31438
|
if (Array.isArray(value)) {
|
|
31269
31439
|
if (value.length === 0) {
|
|
@@ -31277,7 +31447,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
31277
31447
|
indentation += spacer;
|
|
31278
31448
|
res += `
|
|
31279
31449
|
${indentation}`;
|
|
31280
|
-
|
|
31450
|
+
join14 = `,
|
|
31281
31451
|
${indentation}`;
|
|
31282
31452
|
}
|
|
31283
31453
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -31285,13 +31455,13 @@ ${indentation}`;
|
|
|
31285
31455
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
31286
31456
|
const tmp2 = stringifyFnReplacer(String(i), value, stack2, replacer, spacer, indentation);
|
|
31287
31457
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
31288
|
-
res +=
|
|
31458
|
+
res += join14;
|
|
31289
31459
|
}
|
|
31290
31460
|
const tmp = stringifyFnReplacer(String(i), value, stack2, replacer, spacer, indentation);
|
|
31291
31461
|
res += tmp !== void 0 ? tmp : "null";
|
|
31292
31462
|
if (value.length - 1 > maximumBreadth) {
|
|
31293
31463
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
31294
|
-
res += `${
|
|
31464
|
+
res += `${join14}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
31295
31465
|
}
|
|
31296
31466
|
if (spacer !== "") {
|
|
31297
31467
|
res += `
|
|
@@ -31312,7 +31482,7 @@ ${originalIndentation}`;
|
|
|
31312
31482
|
let separator = "";
|
|
31313
31483
|
if (spacer !== "") {
|
|
31314
31484
|
indentation += spacer;
|
|
31315
|
-
|
|
31485
|
+
join14 = `,
|
|
31316
31486
|
${indentation}`;
|
|
31317
31487
|
whitespace = " ";
|
|
31318
31488
|
}
|
|
@@ -31326,13 +31496,13 @@ ${indentation}`;
|
|
|
31326
31496
|
const tmp = stringifyFnReplacer(key2, value, stack2, replacer, spacer, indentation);
|
|
31327
31497
|
if (tmp !== void 0) {
|
|
31328
31498
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
31329
|
-
separator =
|
|
31499
|
+
separator = join14;
|
|
31330
31500
|
}
|
|
31331
31501
|
}
|
|
31332
31502
|
if (keyLength > maximumBreadth) {
|
|
31333
31503
|
const removedKeys = keyLength - maximumBreadth;
|
|
31334
31504
|
res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
|
|
31335
|
-
separator =
|
|
31505
|
+
separator = join14;
|
|
31336
31506
|
}
|
|
31337
31507
|
if (spacer !== "" && separator.length > 1) {
|
|
31338
31508
|
res = `
|
|
@@ -31373,7 +31543,7 @@ ${originalIndentation}`;
|
|
|
31373
31543
|
}
|
|
31374
31544
|
const originalIndentation = indentation;
|
|
31375
31545
|
let res = "";
|
|
31376
|
-
let
|
|
31546
|
+
let join14 = ",";
|
|
31377
31547
|
if (Array.isArray(value)) {
|
|
31378
31548
|
if (value.length === 0) {
|
|
31379
31549
|
return "[]";
|
|
@@ -31386,7 +31556,7 @@ ${originalIndentation}`;
|
|
|
31386
31556
|
indentation += spacer;
|
|
31387
31557
|
res += `
|
|
31388
31558
|
${indentation}`;
|
|
31389
|
-
|
|
31559
|
+
join14 = `,
|
|
31390
31560
|
${indentation}`;
|
|
31391
31561
|
}
|
|
31392
31562
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -31394,13 +31564,13 @@ ${indentation}`;
|
|
|
31394
31564
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
31395
31565
|
const tmp2 = stringifyArrayReplacer(String(i), value[i], stack2, replacer, spacer, indentation);
|
|
31396
31566
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
31397
|
-
res +=
|
|
31567
|
+
res += join14;
|
|
31398
31568
|
}
|
|
31399
31569
|
const tmp = stringifyArrayReplacer(String(i), value[i], stack2, replacer, spacer, indentation);
|
|
31400
31570
|
res += tmp !== void 0 ? tmp : "null";
|
|
31401
31571
|
if (value.length - 1 > maximumBreadth) {
|
|
31402
31572
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
31403
|
-
res += `${
|
|
31573
|
+
res += `${join14}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
31404
31574
|
}
|
|
31405
31575
|
if (spacer !== "") {
|
|
31406
31576
|
res += `
|
|
@@ -31413,7 +31583,7 @@ ${originalIndentation}`;
|
|
|
31413
31583
|
let whitespace = "";
|
|
31414
31584
|
if (spacer !== "") {
|
|
31415
31585
|
indentation += spacer;
|
|
31416
|
-
|
|
31586
|
+
join14 = `,
|
|
31417
31587
|
${indentation}`;
|
|
31418
31588
|
whitespace = " ";
|
|
31419
31589
|
}
|
|
@@ -31422,7 +31592,7 @@ ${indentation}`;
|
|
|
31422
31592
|
const tmp = stringifyArrayReplacer(key2, value[key2], stack2, replacer, spacer, indentation);
|
|
31423
31593
|
if (tmp !== void 0) {
|
|
31424
31594
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
31425
|
-
separator =
|
|
31595
|
+
separator = join14;
|
|
31426
31596
|
}
|
|
31427
31597
|
}
|
|
31428
31598
|
if (spacer !== "" && separator.length > 1) {
|
|
@@ -31480,20 +31650,20 @@ ${originalIndentation}`;
|
|
|
31480
31650
|
indentation += spacer;
|
|
31481
31651
|
let res2 = `
|
|
31482
31652
|
${indentation}`;
|
|
31483
|
-
const
|
|
31653
|
+
const join15 = `,
|
|
31484
31654
|
${indentation}`;
|
|
31485
31655
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
31486
31656
|
let i = 0;
|
|
31487
31657
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
31488
31658
|
const tmp2 = stringifyIndent(String(i), value[i], stack2, spacer, indentation);
|
|
31489
31659
|
res2 += tmp2 !== void 0 ? tmp2 : "null";
|
|
31490
|
-
res2 +=
|
|
31660
|
+
res2 += join15;
|
|
31491
31661
|
}
|
|
31492
31662
|
const tmp = stringifyIndent(String(i), value[i], stack2, spacer, indentation);
|
|
31493
31663
|
res2 += tmp !== void 0 ? tmp : "null";
|
|
31494
31664
|
if (value.length - 1 > maximumBreadth) {
|
|
31495
31665
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
31496
|
-
res2 += `${
|
|
31666
|
+
res2 += `${join15}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
31497
31667
|
}
|
|
31498
31668
|
res2 += `
|
|
31499
31669
|
${originalIndentation}`;
|
|
@@ -31509,16 +31679,16 @@ ${originalIndentation}`;
|
|
|
31509
31679
|
return '"[Object]"';
|
|
31510
31680
|
}
|
|
31511
31681
|
indentation += spacer;
|
|
31512
|
-
const
|
|
31682
|
+
const join14 = `,
|
|
31513
31683
|
${indentation}`;
|
|
31514
31684
|
let res = "";
|
|
31515
31685
|
let separator = "";
|
|
31516
31686
|
let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
|
|
31517
31687
|
if (isTypedArrayWithEntries(value)) {
|
|
31518
|
-
res += stringifyTypedArray(value,
|
|
31688
|
+
res += stringifyTypedArray(value, join14, maximumBreadth);
|
|
31519
31689
|
keys = keys.slice(value.length);
|
|
31520
31690
|
maximumPropertiesToStringify -= value.length;
|
|
31521
|
-
separator =
|
|
31691
|
+
separator = join14;
|
|
31522
31692
|
}
|
|
31523
31693
|
if (deterministic) {
|
|
31524
31694
|
keys = sort(keys, comparator);
|
|
@@ -31529,13 +31699,13 @@ ${indentation}`;
|
|
|
31529
31699
|
const tmp = stringifyIndent(key2, value[key2], stack2, spacer, indentation);
|
|
31530
31700
|
if (tmp !== void 0) {
|
|
31531
31701
|
res += `${separator}${strEscape(key2)}: ${tmp}`;
|
|
31532
|
-
separator =
|
|
31702
|
+
separator = join14;
|
|
31533
31703
|
}
|
|
31534
31704
|
}
|
|
31535
31705
|
if (keyLength > maximumBreadth) {
|
|
31536
31706
|
const removedKeys = keyLength - maximumBreadth;
|
|
31537
31707
|
res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
|
|
31538
|
-
separator =
|
|
31708
|
+
separator = join14;
|
|
31539
31709
|
}
|
|
31540
31710
|
if (separator !== "") {
|
|
31541
31711
|
res = `
|
|
@@ -32062,28 +32232,28 @@ var require_pino = __commonJS({
|
|
|
32062
32232
|
});
|
|
32063
32233
|
|
|
32064
32234
|
// packages/server/src/services/logger.ts
|
|
32065
|
-
var import_pino,
|
|
32235
|
+
var import_pino, import_path4, import_fs2, import_os4, MAX_LOG_SIZE, logDir, logFile, logger;
|
|
32066
32236
|
var init_logger = __esm({
|
|
32067
32237
|
"packages/server/src/services/logger.ts"() {
|
|
32068
32238
|
"use strict";
|
|
32069
32239
|
import_pino = __toESM(require_pino());
|
|
32070
|
-
|
|
32071
|
-
|
|
32072
|
-
|
|
32240
|
+
import_path4 = require("path");
|
|
32241
|
+
import_fs2 = require("fs");
|
|
32242
|
+
import_os4 = require("os");
|
|
32073
32243
|
MAX_LOG_SIZE = 3 * 1024 * 1024;
|
|
32074
|
-
logDir = (0,
|
|
32075
|
-
(0,
|
|
32076
|
-
logFile = (0,
|
|
32244
|
+
logDir = (0, import_path4.resolve)((0, import_os4.homedir)(), ".hermes-web-ui", "logs");
|
|
32245
|
+
(0, import_fs2.mkdirSync)(logDir, { recursive: true });
|
|
32246
|
+
logFile = (0, import_path4.resolve)(logDir, "server.log");
|
|
32077
32247
|
try {
|
|
32078
|
-
const stat2 = (0,
|
|
32248
|
+
const stat2 = (0, import_fs2.statSync)(logFile);
|
|
32079
32249
|
if (stat2.size > MAX_LOG_SIZE) {
|
|
32080
32250
|
const keepSize = Math.floor(MAX_LOG_SIZE / 2);
|
|
32081
|
-
const fd = (0,
|
|
32251
|
+
const fd = (0, import_fs2.openSync)(logFile, "r");
|
|
32082
32252
|
const buf = Buffer.alloc(keepSize);
|
|
32083
|
-
(0,
|
|
32084
|
-
(0,
|
|
32085
|
-
(0,
|
|
32086
|
-
(0,
|
|
32253
|
+
(0, import_fs2.readSync)(fd, buf, 0, keepSize, stat2.size - keepSize);
|
|
32254
|
+
(0, import_fs2.closeSync)(fd);
|
|
32255
|
+
(0, import_fs2.truncateSync)(logFile, 0);
|
|
32256
|
+
(0, import_fs2.writeFileSync)(logFile, buf);
|
|
32087
32257
|
}
|
|
32088
32258
|
} catch {
|
|
32089
32259
|
}
|
|
@@ -32101,24 +32271,24 @@ var gateway_manager_exports = {};
|
|
|
32101
32271
|
__export(gateway_manager_exports, {
|
|
32102
32272
|
GatewayManager: () => GatewayManager
|
|
32103
32273
|
});
|
|
32104
|
-
var import_child_process,
|
|
32274
|
+
var import_child_process, import_path5, import_os5, import_fs3, import_child_process2, import_util, import_net, execFileAsync, HERMES_BASE, HERMES_BIN, isWsl, isDocker, needsRunMode, GatewayManager;
|
|
32105
32275
|
var init_gateway_manager = __esm({
|
|
32106
32276
|
"packages/server/src/services/hermes/gateway-manager.ts"() {
|
|
32107
32277
|
"use strict";
|
|
32108
32278
|
import_child_process = require("child_process");
|
|
32109
|
-
|
|
32110
|
-
|
|
32111
|
-
|
|
32279
|
+
import_path5 = require("path");
|
|
32280
|
+
import_os5 = require("os");
|
|
32281
|
+
import_fs3 = require("fs");
|
|
32112
32282
|
import_child_process2 = require("child_process");
|
|
32113
32283
|
import_util = require("util");
|
|
32114
32284
|
import_net = require("net");
|
|
32115
32285
|
init_js_yaml();
|
|
32116
32286
|
init_logger();
|
|
32117
32287
|
execFileAsync = (0, import_util.promisify)(import_child_process2.execFile);
|
|
32118
|
-
HERMES_BASE = (0,
|
|
32288
|
+
HERMES_BASE = (0, import_path5.resolve)((0, import_os5.homedir)(), ".hermes");
|
|
32119
32289
|
HERMES_BIN = process.env.HERMES_BIN?.trim() || "hermes";
|
|
32120
|
-
isWsl = (0,
|
|
32121
|
-
isDocker = (0,
|
|
32290
|
+
isWsl = (0, import_fs3.existsSync)("/proc/version") && (0, import_fs3.readFileSync)("/proc/version", "utf-8").toLowerCase().includes("microsoft");
|
|
32291
|
+
isDocker = (0, import_fs3.existsSync)("/.dockerenv");
|
|
32122
32292
|
needsRunMode = isWsl || isDocker;
|
|
32123
32293
|
GatewayManager = class {
|
|
32124
32294
|
/** 已注册的网关:profile name → { pid, port, host, url } */
|
|
@@ -32136,17 +32306,17 @@ var init_gateway_manager = __esm({
|
|
|
32136
32306
|
/** 获取 profile 的 home 目录路径 */
|
|
32137
32307
|
profileDir(name) {
|
|
32138
32308
|
if (name === "default") return HERMES_BASE;
|
|
32139
|
-
return (0,
|
|
32309
|
+
return (0, import_path5.join)(HERMES_BASE, "profiles", name);
|
|
32140
32310
|
}
|
|
32141
32311
|
/**
|
|
32142
32312
|
* 从 profile 的 config.yaml 读取 api_server 端口和主机
|
|
32143
32313
|
* 读取路径:platforms.api_server.extra.port / extra.host
|
|
32144
32314
|
*/
|
|
32145
32315
|
readProfilePort(name) {
|
|
32146
|
-
const configPath3 = (0,
|
|
32147
|
-
if (!(0,
|
|
32316
|
+
const configPath3 = (0, import_path5.join)(this.profileDir(name), "config.yaml");
|
|
32317
|
+
if (!(0, import_fs3.existsSync)(configPath3)) return { port: 8642, host: "127.0.0.1" };
|
|
32148
32318
|
try {
|
|
32149
|
-
const content = (0,
|
|
32319
|
+
const content = (0, import_fs3.readFileSync)(configPath3, "utf-8");
|
|
32150
32320
|
const cfg = jsYaml.load(content) || {};
|
|
32151
32321
|
const extra = cfg?.platforms?.api_server?.extra;
|
|
32152
32322
|
const rawPort = extra?.port || 8642;
|
|
@@ -32159,10 +32329,10 @@ var init_gateway_manager = __esm({
|
|
|
32159
32329
|
}
|
|
32160
32330
|
/** 从 profile 的 gateway.pid 文件读取 PID(JSON 格式 { "pid": 12345 }) */
|
|
32161
32331
|
readPidFile(name) {
|
|
32162
|
-
const pidPath = (0,
|
|
32163
|
-
if (!(0,
|
|
32332
|
+
const pidPath = (0, import_path5.join)(this.profileDir(name), "gateway.pid");
|
|
32333
|
+
if (!(0, import_fs3.existsSync)(pidPath)) return null;
|
|
32164
32334
|
try {
|
|
32165
|
-
const content = (0,
|
|
32335
|
+
const content = (0, import_fs3.readFileSync)(pidPath, "utf-8").trim();
|
|
32166
32336
|
const data = JSON.parse(content);
|
|
32167
32337
|
return typeof data.pid === "number" ? data.pid : parseInt(data.pid, 10) || null;
|
|
32168
32338
|
} catch {
|
|
@@ -32195,22 +32365,22 @@ var init_gateway_manager = __esm({
|
|
|
32195
32365
|
/** 尝试绑定端口,检测端口是否被系统级进程占用 */
|
|
32196
32366
|
checkPortAvailable(port, host) {
|
|
32197
32367
|
if (port < 0 || port > 65535) return Promise.resolve(false);
|
|
32198
|
-
return new Promise((
|
|
32368
|
+
return new Promise((resolve10) => {
|
|
32199
32369
|
const server2 = (0, import_net.createServer)();
|
|
32200
32370
|
server2.once("error", () => {
|
|
32201
32371
|
server2.close();
|
|
32202
|
-
|
|
32372
|
+
resolve10(false);
|
|
32203
32373
|
});
|
|
32204
32374
|
server2.once("listening", () => {
|
|
32205
32375
|
server2.close();
|
|
32206
|
-
|
|
32376
|
+
resolve10(true);
|
|
32207
32377
|
});
|
|
32208
32378
|
server2.listen(port, host);
|
|
32209
32379
|
});
|
|
32210
32380
|
}
|
|
32211
32381
|
/** 从 base 端口开始递增查找空闲端口(上限 65535) */
|
|
32212
32382
|
findFreePort(base, host = "127.0.0.1") {
|
|
32213
|
-
return new Promise((
|
|
32383
|
+
return new Promise((resolve10, reject) => {
|
|
32214
32384
|
const tryPort = (port) => {
|
|
32215
32385
|
if (port > 65535) {
|
|
32216
32386
|
reject(new Error(`No free port found in range ${base}-65535`));
|
|
@@ -32223,7 +32393,7 @@ var init_gateway_manager = __esm({
|
|
|
32223
32393
|
});
|
|
32224
32394
|
server2.once("listening", () => {
|
|
32225
32395
|
server2.close();
|
|
32226
|
-
|
|
32396
|
+
resolve10(port);
|
|
32227
32397
|
});
|
|
32228
32398
|
server2.listen(port, host);
|
|
32229
32399
|
};
|
|
@@ -32247,9 +32417,9 @@ var init_gateway_manager = __esm({
|
|
|
32247
32417
|
* 同时清理旧的顶层 port/host(避免 Hermes 读取错误)
|
|
32248
32418
|
*/
|
|
32249
32419
|
writeProfilePort(name, port, host) {
|
|
32250
|
-
const configPath3 = (0,
|
|
32420
|
+
const configPath3 = (0, import_path5.join)(this.profileDir(name), "config.yaml");
|
|
32251
32421
|
try {
|
|
32252
|
-
const content = (0,
|
|
32422
|
+
const content = (0, import_fs3.existsSync)(configPath3) ? (0, import_fs3.readFileSync)(configPath3, "utf-8") : "";
|
|
32253
32423
|
const cfg = jsYaml.load(content) || {};
|
|
32254
32424
|
if (!cfg.platforms) cfg.platforms = {};
|
|
32255
32425
|
if (!cfg.platforms.api_server) cfg.platforms.api_server = {};
|
|
@@ -32265,7 +32435,7 @@ var init_gateway_manager = __esm({
|
|
|
32265
32435
|
if (cfg.platforms.api_server.host !== void 0) {
|
|
32266
32436
|
delete cfg.platforms.api_server.host;
|
|
32267
32437
|
}
|
|
32268
|
-
(0,
|
|
32438
|
+
(0, import_fs3.writeFileSync)(configPath3, jsYaml.dump(cfg, { lineWidth: -1 }), "utf-8");
|
|
32269
32439
|
logger.debug("Updated %s: api_server.extra.port = %d", configPath3, port);
|
|
32270
32440
|
} catch (err) {
|
|
32271
32441
|
logger.error(err, 'Failed to write config for profile "%s"', name);
|
|
@@ -32324,9 +32494,9 @@ var init_gateway_manager = __esm({
|
|
|
32324
32494
|
getApiKey(profileName) {
|
|
32325
32495
|
const name = profileName || this.activeProfile;
|
|
32326
32496
|
try {
|
|
32327
|
-
const envPath3 = (0,
|
|
32328
|
-
if (!(0,
|
|
32329
|
-
const content = (0,
|
|
32497
|
+
const envPath3 = (0, import_path5.join)(this.profileDir(name), ".env");
|
|
32498
|
+
if (!(0, import_fs3.existsSync)(envPath3)) return null;
|
|
32499
|
+
const content = (0, import_fs3.readFileSync)(envPath3, "utf-8");
|
|
32330
32500
|
const match = content.match(/^API_SERVER_KEY\s*=\s*"?([^"\n]+)"?/m);
|
|
32331
32501
|
return match?.[1]?.trim() || null;
|
|
32332
32502
|
} catch {
|
|
@@ -32355,10 +32525,10 @@ var init_gateway_manager = __esm({
|
|
|
32355
32525
|
return profiles;
|
|
32356
32526
|
} catch {
|
|
32357
32527
|
const profiles = ["default"];
|
|
32358
|
-
const profilesDir = (0,
|
|
32359
|
-
if ((0,
|
|
32360
|
-
for (const entry of (0,
|
|
32361
|
-
if (entry.isDirectory() && (0,
|
|
32528
|
+
const profilesDir = (0, import_path5.join)(HERMES_BASE, "profiles");
|
|
32529
|
+
if ((0, import_fs3.existsSync)(profilesDir)) {
|
|
32530
|
+
for (const entry of (0, import_fs3.readdirSync)(profilesDir, { withFileTypes: true })) {
|
|
32531
|
+
if (entry.isDirectory() && (0, import_fs3.existsSync)((0, import_path5.join)(profilesDir, entry.name, "config.yaml"))) {
|
|
32362
32532
|
profiles.push(entry.name);
|
|
32363
32533
|
}
|
|
32364
32534
|
}
|
|
@@ -32404,7 +32574,7 @@ var init_gateway_manager = __esm({
|
|
|
32404
32574
|
const hermesHome = this.profileDir(name);
|
|
32405
32575
|
const url2 = `http://${host}:${port}`;
|
|
32406
32576
|
if (needsRunMode) {
|
|
32407
|
-
return new Promise((
|
|
32577
|
+
return new Promise((resolve10, reject) => {
|
|
32408
32578
|
const env2 = { ...process.env, HERMES_HOME: hermesHome };
|
|
32409
32579
|
const child = (0, import_child_process.spawn)(HERMES_BIN, ["gateway", "run", "--replace"], {
|
|
32410
32580
|
detached: true,
|
|
@@ -32415,7 +32585,7 @@ var init_gateway_manager = __esm({
|
|
|
32415
32585
|
child.unref();
|
|
32416
32586
|
const pid = child.pid ?? 0;
|
|
32417
32587
|
logger.info('Starting gateway for profile "%s" (run mode, PID: %d, port: %d)', name, pid, port);
|
|
32418
|
-
this.waitForReady(name, pid, port, host, url2).then(
|
|
32588
|
+
this.waitForReady(name, pid, port, host, url2).then(resolve10).catch(reject);
|
|
32419
32589
|
});
|
|
32420
32590
|
}
|
|
32421
32591
|
logger.info('Starting gateway for profile "%s" (start mode, port: %d)', name, port);
|
|
@@ -32579,30 +32749,30 @@ __export(hermes_profile_exports, {
|
|
|
32579
32749
|
getProfileDir: () => getProfileDir
|
|
32580
32750
|
});
|
|
32581
32751
|
function getActiveProfileDir() {
|
|
32582
|
-
const activeFile = (0,
|
|
32752
|
+
const activeFile = (0, import_path6.join)(HERMES_BASE2, "active_profile");
|
|
32583
32753
|
try {
|
|
32584
|
-
const name = (0,
|
|
32754
|
+
const name = (0, import_fs4.readFileSync)(activeFile, "utf-8").trim();
|
|
32585
32755
|
if (name && name !== "default") {
|
|
32586
|
-
const dir = (0,
|
|
32587
|
-
if ((0,
|
|
32756
|
+
const dir = (0, import_path6.join)(HERMES_BASE2, "profiles", name);
|
|
32757
|
+
if ((0, import_fs4.existsSync)(dir)) return dir;
|
|
32588
32758
|
}
|
|
32589
32759
|
} catch {
|
|
32590
32760
|
}
|
|
32591
32761
|
return HERMES_BASE2;
|
|
32592
32762
|
}
|
|
32593
32763
|
function getActiveConfigPath() {
|
|
32594
|
-
return (0,
|
|
32764
|
+
return (0, import_path6.join)(getActiveProfileDir(), "config.yaml");
|
|
32595
32765
|
}
|
|
32596
32766
|
function getActiveAuthPath() {
|
|
32597
|
-
return (0,
|
|
32767
|
+
return (0, import_path6.join)(getActiveProfileDir(), "auth.json");
|
|
32598
32768
|
}
|
|
32599
32769
|
function getActiveEnvPath() {
|
|
32600
|
-
return (0,
|
|
32770
|
+
return (0, import_path6.join)(getActiveProfileDir(), ".env");
|
|
32601
32771
|
}
|
|
32602
32772
|
function getActiveProfileName() {
|
|
32603
|
-
const activeFile = (0,
|
|
32773
|
+
const activeFile = (0, import_path6.join)(HERMES_BASE2, "active_profile");
|
|
32604
32774
|
try {
|
|
32605
|
-
const name = (0,
|
|
32775
|
+
const name = (0, import_fs4.readFileSync)(activeFile, "utf-8").trim();
|
|
32606
32776
|
return name || "default";
|
|
32607
32777
|
} catch {
|
|
32608
32778
|
return "default";
|
|
@@ -32610,17 +32780,17 @@ function getActiveProfileName() {
|
|
|
32610
32780
|
}
|
|
32611
32781
|
function getProfileDir(name) {
|
|
32612
32782
|
if (!name || name === "default") return HERMES_BASE2;
|
|
32613
|
-
const dir = (0,
|
|
32614
|
-
return (0,
|
|
32783
|
+
const dir = (0, import_path6.join)(HERMES_BASE2, "profiles", name);
|
|
32784
|
+
return (0, import_fs4.existsSync)(dir) ? dir : HERMES_BASE2;
|
|
32615
32785
|
}
|
|
32616
|
-
var
|
|
32786
|
+
var import_path6, import_os6, import_fs4, HERMES_BASE2;
|
|
32617
32787
|
var init_hermes_profile = __esm({
|
|
32618
32788
|
"packages/server/src/services/hermes/hermes-profile.ts"() {
|
|
32619
32789
|
"use strict";
|
|
32620
|
-
|
|
32621
|
-
|
|
32622
|
-
|
|
32623
|
-
HERMES_BASE2 = (0,
|
|
32790
|
+
import_path6 = require("path");
|
|
32791
|
+
import_os6 = require("os");
|
|
32792
|
+
import_fs4 = require("fs");
|
|
32793
|
+
HERMES_BASE2 = (0, import_path6.resolve)((0, import_os6.homedir)(), ".hermes");
|
|
32624
32794
|
}
|
|
32625
32795
|
});
|
|
32626
32796
|
|
|
@@ -34827,7 +34997,7 @@ var require_websocket = __commonJS({
|
|
|
34827
34997
|
var http3 = require("http");
|
|
34828
34998
|
var net = require("net");
|
|
34829
34999
|
var tls = require("tls");
|
|
34830
|
-
var { randomBytes:
|
|
35000
|
+
var { randomBytes: randomBytes4, createHash } = require("crypto");
|
|
34831
35001
|
var { Duplex, Readable: Readable2 } = require("stream");
|
|
34832
35002
|
var { URL: URL2 } = require("url");
|
|
34833
35003
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -35357,7 +35527,7 @@ var require_websocket = __commonJS({
|
|
|
35357
35527
|
}
|
|
35358
35528
|
}
|
|
35359
35529
|
const defaultPort = isSecure ? 443 : 80;
|
|
35360
|
-
const key =
|
|
35530
|
+
const key = randomBytes4(16).toString("base64");
|
|
35361
35531
|
const request = isSecure ? https2.request : http3.request;
|
|
35362
35532
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
35363
35533
|
let perMessageDeflate;
|
|
@@ -38110,23 +38280,23 @@ function bodyParserWrapper(opts = {}) {
|
|
|
38110
38280
|
}
|
|
38111
38281
|
|
|
38112
38282
|
// packages/server/src/config.ts
|
|
38113
|
-
var
|
|
38114
|
-
var
|
|
38283
|
+
var import_path2 = require("path");
|
|
38284
|
+
var import_os2 = require("os");
|
|
38115
38285
|
var config = {
|
|
38116
38286
|
port: parseInt(process.env.PORT || "8648", 10),
|
|
38117
38287
|
upstream: process.env.UPSTREAM || "http://127.0.0.1:8642",
|
|
38118
|
-
uploadDir: process.env.UPLOAD_DIR || (0,
|
|
38119
|
-
dataDir: (0,
|
|
38288
|
+
uploadDir: process.env.UPLOAD_DIR || (0, import_path2.resolve)((0, import_os2.tmpdir)(), "hermes-uploads"),
|
|
38289
|
+
dataDir: (0, import_path2.resolve)(__dirname, "..", "data"),
|
|
38120
38290
|
corsOrigins: process.env.CORS_ORIGINS || "*"
|
|
38121
38291
|
};
|
|
38122
38292
|
|
|
38123
38293
|
// packages/server/src/services/auth.ts
|
|
38124
38294
|
var import_promises = require("fs/promises");
|
|
38125
|
-
var
|
|
38295
|
+
var import_path3 = require("path");
|
|
38126
38296
|
var import_crypto = require("crypto");
|
|
38127
|
-
var
|
|
38128
|
-
var APP_HOME = (0,
|
|
38129
|
-
var TOKEN_FILE = (0,
|
|
38297
|
+
var import_os3 = require("os");
|
|
38298
|
+
var APP_HOME = (0, import_path3.join)((0, import_os3.homedir)(), ".hermes-web-ui");
|
|
38299
|
+
var TOKEN_FILE = (0, import_path3.join)(APP_HOME, ".token");
|
|
38130
38300
|
function generateToken() {
|
|
38131
38301
|
return (0, import_crypto.randomBytes)(32).toString("hex");
|
|
38132
38302
|
}
|
|
@@ -38195,10 +38365,10 @@ function bindShutdown(server2) {
|
|
|
38195
38365
|
logger.info("Shutting down (%s)...", signal);
|
|
38196
38366
|
try {
|
|
38197
38367
|
if (server2) {
|
|
38198
|
-
await new Promise((
|
|
38368
|
+
await new Promise((resolve10) => {
|
|
38199
38369
|
server2.close(() => {
|
|
38200
38370
|
logger.info("HTTP server closed");
|
|
38201
|
-
|
|
38371
|
+
resolve10();
|
|
38202
38372
|
});
|
|
38203
38373
|
});
|
|
38204
38374
|
}
|
|
@@ -38224,25 +38394,25 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
|
38224
38394
|
var wrapper_default = import_websocket.default;
|
|
38225
38395
|
|
|
38226
38396
|
// packages/server/src/routes/hermes/terminal.ts
|
|
38227
|
-
var
|
|
38228
|
-
var
|
|
38397
|
+
var import_fs5 = require("fs");
|
|
38398
|
+
var import_path7 = require("path");
|
|
38229
38399
|
init_logger();
|
|
38230
38400
|
var pty = null;
|
|
38231
38401
|
function ensureNodePtySpawnHelperExecutable() {
|
|
38232
38402
|
if (process.platform !== "darwin") return;
|
|
38233
38403
|
try {
|
|
38234
|
-
const nodePtyRoot = (0,
|
|
38404
|
+
const nodePtyRoot = (0, import_path7.dirname)(require.resolve("node-pty/package.json"));
|
|
38235
38405
|
const helperCandidates = [
|
|
38236
|
-
(0,
|
|
38237
|
-
(0,
|
|
38238
|
-
(0,
|
|
38406
|
+
(0, import_path7.join)(nodePtyRoot, "build", "Release", "spawn-helper"),
|
|
38407
|
+
(0, import_path7.join)(nodePtyRoot, "build", "Debug", "spawn-helper"),
|
|
38408
|
+
(0, import_path7.join)(nodePtyRoot, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper")
|
|
38239
38409
|
];
|
|
38240
38410
|
for (const helperPath of helperCandidates) {
|
|
38241
|
-
if (!(0,
|
|
38411
|
+
if (!(0, import_fs5.existsSync)(helperPath)) continue;
|
|
38242
38412
|
try {
|
|
38243
|
-
(0,
|
|
38413
|
+
(0, import_fs5.accessSync)(helperPath, import_fs5.constants.X_OK);
|
|
38244
38414
|
} catch {
|
|
38245
|
-
(0,
|
|
38415
|
+
(0, import_fs5.chmodSync)(helperPath, 493);
|
|
38246
38416
|
logger.debug("Restored execute bit for node-pty helper: %s", helperPath);
|
|
38247
38417
|
}
|
|
38248
38418
|
}
|
|
@@ -38265,7 +38435,7 @@ function findShell() {
|
|
|
38265
38435
|
process.platform === "win32" ? "cmd.exe" : null
|
|
38266
38436
|
].filter(Boolean);
|
|
38267
38437
|
for (const shell of candidates) {
|
|
38268
|
-
if ((0,
|
|
38438
|
+
if ((0, import_fs5.existsSync)(shell)) return shell;
|
|
38269
38439
|
}
|
|
38270
38440
|
return "/bin/bash";
|
|
38271
38441
|
}
|
|
@@ -40033,12 +40203,12 @@ for (const httpMethod of httpMethods) {
|
|
|
40033
40203
|
|
|
40034
40204
|
// packages/server/src/services/hermes/hermes-cli.ts
|
|
40035
40205
|
var import_child_process3 = require("child_process");
|
|
40036
|
-
var
|
|
40206
|
+
var import_fs6 = require("fs");
|
|
40037
40207
|
var import_util2 = require("util");
|
|
40038
40208
|
init_logger();
|
|
40039
40209
|
var execFileAsync2 = (0, import_util2.promisify)(import_child_process3.execFile);
|
|
40040
40210
|
var execOpts = { windowsHide: true };
|
|
40041
|
-
var isDocker2 = (0,
|
|
40211
|
+
var isDocker2 = (0, import_fs6.existsSync)("/.dockerenv");
|
|
40042
40212
|
function resolveHermesBin() {
|
|
40043
40213
|
const envBin = process.env.HERMES_BIN?.trim();
|
|
40044
40214
|
if (envBin) return envBin;
|
|
@@ -40420,11 +40590,11 @@ async function importProfile(archivePath, name) {
|
|
|
40420
40590
|
}
|
|
40421
40591
|
|
|
40422
40592
|
// packages/server/src/controllers/health.ts
|
|
40423
|
-
var LOCAL_VERSION = true ? "0.4.
|
|
40593
|
+
var LOCAL_VERSION = true ? "0.4.3" : (() => {
|
|
40424
40594
|
try {
|
|
40425
|
-
const { readFileSync:
|
|
40426
|
-
const { resolve:
|
|
40427
|
-
return JSON.parse(
|
|
40595
|
+
const { readFileSync: readFileSync10 } = null;
|
|
40596
|
+
const { resolve: resolve10 } = null;
|
|
40597
|
+
return JSON.parse(readFileSync10(resolve10(__dirname, "../../package.json"), "utf-8")).version;
|
|
40428
40598
|
} catch {
|
|
40429
40599
|
return "0.0.0";
|
|
40430
40600
|
}
|
|
@@ -40432,8 +40602,8 @@ var LOCAL_VERSION = true ? "0.4.2" : (() => {
|
|
|
40432
40602
|
var cachedLatestVersion = "";
|
|
40433
40603
|
async function checkLatestVersion() {
|
|
40434
40604
|
try {
|
|
40435
|
-
const { readFileSync:
|
|
40436
|
-
const pkg = JSON.parse(
|
|
40605
|
+
const { readFileSync: readFileSync10 } = require("fs");
|
|
40606
|
+
const pkg = JSON.parse(readFileSync10(resolve6(require("path").join(__dirname, "../../package.json")), "utf-8"));
|
|
40437
40607
|
const name = pkg.name;
|
|
40438
40608
|
const res = await fetch(`https://registry.npmjs.org/${name}/latest`, { signal: AbortSignal.timeout(1e4) });
|
|
40439
40609
|
if (res.ok) {
|
|
@@ -40471,7 +40641,7 @@ async function healthCheck(ctx) {
|
|
|
40471
40641
|
webui_update_available: cachedLatestVersion && cachedLatestVersion !== LOCAL_VERSION
|
|
40472
40642
|
};
|
|
40473
40643
|
}
|
|
40474
|
-
function
|
|
40644
|
+
function resolve6(p) {
|
|
40475
40645
|
return p;
|
|
40476
40646
|
}
|
|
40477
40647
|
|
|
@@ -40661,19 +40831,57 @@ uploadRoutes.post("/upload", handleUpload);
|
|
|
40661
40831
|
|
|
40662
40832
|
// packages/server/src/controllers/update.ts
|
|
40663
40833
|
var import_child_process4 = require("child_process");
|
|
40834
|
+
var import_path8 = require("path");
|
|
40835
|
+
function getNodeBinDir() {
|
|
40836
|
+
return (0, import_path8.dirname)(process.execPath);
|
|
40837
|
+
}
|
|
40838
|
+
function getNpmBin() {
|
|
40839
|
+
return (0, import_path8.join)(getNodeBinDir(), process.platform === "win32" ? "npm.cmd" : "npm");
|
|
40840
|
+
}
|
|
40841
|
+
function getCliBin() {
|
|
40842
|
+
return (0, import_path8.join)(getNodeBinDir(), process.platform === "win32" ? "hermes-web-ui.cmd" : "hermes-web-ui");
|
|
40843
|
+
}
|
|
40844
|
+
function getWindowsShell() {
|
|
40845
|
+
return process.env.ComSpec || "cmd.exe";
|
|
40846
|
+
}
|
|
40847
|
+
function quoteForWindowsCommand(value) {
|
|
40848
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
40849
|
+
}
|
|
40850
|
+
function runUpdateInstall() {
|
|
40851
|
+
if (process.platform === "win32") {
|
|
40852
|
+
return (0, import_child_process4.execFileSync)(getWindowsShell(), ["/d", "/s", "/c", `${quoteForWindowsCommand(getNpmBin())} install -g hermes-web-ui@latest`], {
|
|
40853
|
+
encoding: "utf-8",
|
|
40854
|
+
timeout: 12e4,
|
|
40855
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
40856
|
+
windowsHide: true
|
|
40857
|
+
});
|
|
40858
|
+
}
|
|
40859
|
+
return (0, import_child_process4.execFileSync)(getNpmBin(), ["install", "-g", "hermes-web-ui@latest"], {
|
|
40860
|
+
encoding: "utf-8",
|
|
40861
|
+
timeout: 12e4,
|
|
40862
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
40863
|
+
});
|
|
40864
|
+
}
|
|
40865
|
+
function spawnRestart(port) {
|
|
40866
|
+
if (process.platform === "win32") {
|
|
40867
|
+
return (0, import_child_process4.spawn)(getWindowsShell(), ["/d", "/s", "/c", `${quoteForWindowsCommand(getCliBin())} restart --port ${port}`], {
|
|
40868
|
+
detached: true,
|
|
40869
|
+
stdio: "ignore",
|
|
40870
|
+
windowsHide: true
|
|
40871
|
+
});
|
|
40872
|
+
}
|
|
40873
|
+
return (0, import_child_process4.spawn)(getCliBin(), ["restart", "--port", port], {
|
|
40874
|
+
detached: true,
|
|
40875
|
+
stdio: "ignore",
|
|
40876
|
+
windowsHide: true
|
|
40877
|
+
});
|
|
40878
|
+
}
|
|
40664
40879
|
async function handleUpdate(ctx) {
|
|
40665
|
-
const isWin = process.platform === "win32";
|
|
40666
|
-
const cmd = isWin ? "cmd /c npm install -g hermes-web-ui@latest" : "npm install -g hermes-web-ui@latest";
|
|
40667
40880
|
try {
|
|
40668
|
-
const
|
|
40669
|
-
const output = execSync(cmd, { encoding: "utf-8", timeout: 12e4, stdio: ["pipe", "pipe", "pipe"] });
|
|
40881
|
+
const output = runUpdateInstall();
|
|
40670
40882
|
ctx.body = { success: true, message: output.trim() };
|
|
40671
40883
|
setTimeout(() => {
|
|
40672
|
-
(
|
|
40673
|
-
detached: true,
|
|
40674
|
-
stdio: "ignore",
|
|
40675
|
-
windowsHide: true
|
|
40676
|
-
}).unref();
|
|
40884
|
+
spawnRestart(process.env.PORT || "8648").unref();
|
|
40677
40885
|
process.exit(0);
|
|
40678
40886
|
}, 2e3);
|
|
40679
40887
|
} catch (err) {
|
|
@@ -40686,6 +40894,169 @@ async function handleUpdate(ctx) {
|
|
|
40686
40894
|
var updateRoutes = new router_default();
|
|
40687
40895
|
updateRoutes.post("/api/hermes/update", handleUpdate);
|
|
40688
40896
|
|
|
40897
|
+
// packages/server/src/services/credentials.ts
|
|
40898
|
+
var import_promises3 = require("fs/promises");
|
|
40899
|
+
var import_fs7 = require("fs");
|
|
40900
|
+
var import_path9 = require("path");
|
|
40901
|
+
var import_os7 = require("os");
|
|
40902
|
+
var import_node_crypto = require("node:crypto");
|
|
40903
|
+
var APP_HOME2 = (0, import_path9.join)((0, import_os7.homedir)(), ".hermes-web-ui");
|
|
40904
|
+
var CREDENTIALS_FILE = (0, import_path9.join)(APP_HOME2, ".credentials");
|
|
40905
|
+
var SCRYPT_OPTIONS = { N: 16384, r: 8, p: 1, maxmem: 64 * 1024 * 1024 };
|
|
40906
|
+
function hashPassword(password, salt) {
|
|
40907
|
+
return (0, import_node_crypto.scryptSync)(password, salt, 64, SCRYPT_OPTIONS).toString("hex");
|
|
40908
|
+
}
|
|
40909
|
+
async function getCredentials() {
|
|
40910
|
+
try {
|
|
40911
|
+
const data = await (0, import_promises3.readFile)(CREDENTIALS_FILE, "utf-8");
|
|
40912
|
+
return JSON.parse(data);
|
|
40913
|
+
} catch {
|
|
40914
|
+
return null;
|
|
40915
|
+
}
|
|
40916
|
+
}
|
|
40917
|
+
async function setCredentials(username, password) {
|
|
40918
|
+
const salt = (0, import_node_crypto.randomBytes)(16).toString("hex");
|
|
40919
|
+
const password_hash = hashPassword(password, salt);
|
|
40920
|
+
const cred = { username, password_hash, salt, created_at: Date.now() };
|
|
40921
|
+
await (0, import_promises3.mkdir)(APP_HOME2, { recursive: true });
|
|
40922
|
+
await (0, import_promises3.writeFile)(CREDENTIALS_FILE, JSON.stringify(cred, null, 2), { mode: 384 });
|
|
40923
|
+
return cred;
|
|
40924
|
+
}
|
|
40925
|
+
async function deleteCredentials() {
|
|
40926
|
+
try {
|
|
40927
|
+
await (0, import_promises3.unlink)(CREDENTIALS_FILE);
|
|
40928
|
+
} catch {
|
|
40929
|
+
}
|
|
40930
|
+
}
|
|
40931
|
+
async function verifyCredentials(username, password) {
|
|
40932
|
+
const cred = await getCredentials();
|
|
40933
|
+
if (!cred) return false;
|
|
40934
|
+
if (cred.username !== username) return false;
|
|
40935
|
+
const computed = hashPassword(password, cred.salt);
|
|
40936
|
+
return computed === cred.password_hash;
|
|
40937
|
+
}
|
|
40938
|
+
function credentialsFileExists() {
|
|
40939
|
+
return (0, import_fs7.existsSync)(CREDENTIALS_FILE);
|
|
40940
|
+
}
|
|
40941
|
+
|
|
40942
|
+
// packages/server/src/controllers/auth.ts
|
|
40943
|
+
async function authStatus(ctx) {
|
|
40944
|
+
const cred = await getCredentials();
|
|
40945
|
+
ctx.body = {
|
|
40946
|
+
hasPasswordLogin: !!cred,
|
|
40947
|
+
username: cred?.username || null
|
|
40948
|
+
};
|
|
40949
|
+
}
|
|
40950
|
+
async function login(ctx) {
|
|
40951
|
+
const { username, password } = ctx.request.body;
|
|
40952
|
+
if (!username || !password) {
|
|
40953
|
+
ctx.status = 400;
|
|
40954
|
+
ctx.body = { error: "Username and password are required" };
|
|
40955
|
+
return;
|
|
40956
|
+
}
|
|
40957
|
+
const valid = await verifyCredentials(username, password);
|
|
40958
|
+
if (!valid) {
|
|
40959
|
+
ctx.status = 401;
|
|
40960
|
+
ctx.body = { error: "Invalid username or password" };
|
|
40961
|
+
return;
|
|
40962
|
+
}
|
|
40963
|
+
const token = await getToken();
|
|
40964
|
+
if (!token) {
|
|
40965
|
+
ctx.status = 500;
|
|
40966
|
+
ctx.body = { error: "Auth is disabled on this server" };
|
|
40967
|
+
return;
|
|
40968
|
+
}
|
|
40969
|
+
ctx.body = { token };
|
|
40970
|
+
}
|
|
40971
|
+
async function setupPassword(ctx) {
|
|
40972
|
+
const { username, password } = ctx.request.body;
|
|
40973
|
+
if (!username || !password) {
|
|
40974
|
+
ctx.status = 400;
|
|
40975
|
+
ctx.body = { error: "Username and password are required" };
|
|
40976
|
+
return;
|
|
40977
|
+
}
|
|
40978
|
+
if (username.length < 2) {
|
|
40979
|
+
ctx.status = 400;
|
|
40980
|
+
ctx.body = { error: "Username must be at least 2 characters" };
|
|
40981
|
+
return;
|
|
40982
|
+
}
|
|
40983
|
+
if (password.length < 6) {
|
|
40984
|
+
ctx.status = 400;
|
|
40985
|
+
ctx.body = { error: "Password must be at least 6 characters" };
|
|
40986
|
+
return;
|
|
40987
|
+
}
|
|
40988
|
+
await setCredentials(username, password);
|
|
40989
|
+
ctx.body = { success: true };
|
|
40990
|
+
}
|
|
40991
|
+
async function changePassword(ctx) {
|
|
40992
|
+
const { currentPassword, newPassword } = ctx.request.body;
|
|
40993
|
+
if (!currentPassword || !newPassword) {
|
|
40994
|
+
ctx.status = 400;
|
|
40995
|
+
ctx.body = { error: "Current password and new password are required" };
|
|
40996
|
+
return;
|
|
40997
|
+
}
|
|
40998
|
+
if (newPassword.length < 6) {
|
|
40999
|
+
ctx.status = 400;
|
|
41000
|
+
ctx.body = { error: "New password must be at least 6 characters" };
|
|
41001
|
+
return;
|
|
41002
|
+
}
|
|
41003
|
+
const cred = await getCredentials();
|
|
41004
|
+
if (!cred) {
|
|
41005
|
+
ctx.status = 400;
|
|
41006
|
+
ctx.body = { error: "Password login not configured" };
|
|
41007
|
+
return;
|
|
41008
|
+
}
|
|
41009
|
+
const valid = await verifyCredentials(cred.username, currentPassword);
|
|
41010
|
+
if (!valid) {
|
|
41011
|
+
ctx.status = 400;
|
|
41012
|
+
ctx.body = { error: "Current password is incorrect" };
|
|
41013
|
+
return;
|
|
41014
|
+
}
|
|
41015
|
+
await setCredentials(cred.username, newPassword);
|
|
41016
|
+
ctx.body = { success: true };
|
|
41017
|
+
}
|
|
41018
|
+
async function changeUsername(ctx) {
|
|
41019
|
+
const { currentPassword, newUsername } = ctx.request.body;
|
|
41020
|
+
if (!currentPassword || !newUsername) {
|
|
41021
|
+
ctx.status = 400;
|
|
41022
|
+
ctx.body = { error: "Current password and new username are required" };
|
|
41023
|
+
return;
|
|
41024
|
+
}
|
|
41025
|
+
if (newUsername.length < 2) {
|
|
41026
|
+
ctx.status = 400;
|
|
41027
|
+
ctx.body = { error: "Username must be at least 2 characters" };
|
|
41028
|
+
return;
|
|
41029
|
+
}
|
|
41030
|
+
const cred = await getCredentials();
|
|
41031
|
+
if (!cred) {
|
|
41032
|
+
ctx.status = 400;
|
|
41033
|
+
ctx.body = { error: "Password login not configured" };
|
|
41034
|
+
return;
|
|
41035
|
+
}
|
|
41036
|
+
const valid = await verifyCredentials(cred.username, currentPassword);
|
|
41037
|
+
if (!valid) {
|
|
41038
|
+
ctx.status = 400;
|
|
41039
|
+
ctx.body = { error: "Current password is incorrect" };
|
|
41040
|
+
return;
|
|
41041
|
+
}
|
|
41042
|
+
await setCredentials(newUsername, currentPassword);
|
|
41043
|
+
ctx.body = { success: true };
|
|
41044
|
+
}
|
|
41045
|
+
async function removePassword(ctx) {
|
|
41046
|
+
await deleteCredentials();
|
|
41047
|
+
ctx.body = { success: true };
|
|
41048
|
+
}
|
|
41049
|
+
|
|
41050
|
+
// packages/server/src/routes/auth.ts
|
|
41051
|
+
var authPublicRoutes = new router_default();
|
|
41052
|
+
authPublicRoutes.get("/api/auth/status", authStatus);
|
|
41053
|
+
authPublicRoutes.post("/api/auth/login", login);
|
|
41054
|
+
var authProtectedRoutes = new router_default();
|
|
41055
|
+
authProtectedRoutes.post("/api/auth/setup", setupPassword);
|
|
41056
|
+
authProtectedRoutes.post("/api/auth/change-password", changePassword);
|
|
41057
|
+
authProtectedRoutes.post("/api/auth/change-username", changeUsername);
|
|
41058
|
+
authProtectedRoutes.delete("/api/auth/password", removePassword);
|
|
41059
|
+
|
|
40689
41060
|
// packages/server/src/services/hermes/conversations.ts
|
|
40690
41061
|
var LINEAGE_TOLERANCE_SECONDS = 3;
|
|
40691
41062
|
var LIVE_WINDOW_SECONDS = 300;
|
|
@@ -40989,9 +41360,9 @@ async function getConversationDetail(sessionId, options = {}) {
|
|
|
40989
41360
|
};
|
|
40990
41361
|
}
|
|
40991
41362
|
|
|
40992
|
-
// packages/server/src/
|
|
41363
|
+
// packages/server/src/db/hermes/sessions-db.ts
|
|
40993
41364
|
init_hermes_profile();
|
|
40994
|
-
var
|
|
41365
|
+
var SQLITE_AVAILABLE2 = (() => {
|
|
40995
41366
|
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
40996
41367
|
return major > 22 || major === 22 && minor >= 5;
|
|
40997
41368
|
})();
|
|
@@ -41041,64 +41412,301 @@ function mapRow(row) {
|
|
|
41041
41412
|
last_active: normalizeNumber(row.last_active, startedAt)
|
|
41042
41413
|
};
|
|
41043
41414
|
}
|
|
41044
|
-
var
|
|
41045
|
-
|
|
41046
|
-
|
|
41047
|
-
|
|
41048
|
-
|
|
41049
|
-
|
|
41050
|
-
|
|
41051
|
-
|
|
41052
|
-
|
|
41053
|
-
|
|
41054
|
-
|
|
41055
|
-
|
|
41056
|
-
|
|
41057
|
-
|
|
41058
|
-
|
|
41059
|
-
|
|
41060
|
-
|
|
41061
|
-
|
|
41062
|
-
|
|
41063
|
-
|
|
41064
|
-
|
|
41065
|
-
|
|
41066
|
-
(
|
|
41067
|
-
|
|
41068
|
-
|
|
41069
|
-
|
|
41070
|
-
|
|
41071
|
-
|
|
41072
|
-
|
|
41073
|
-
|
|
41074
|
-
|
|
41075
|
-
|
|
41415
|
+
var SESSION_SELECT = `
|
|
41416
|
+
s.id,
|
|
41417
|
+
s.source,
|
|
41418
|
+
COALESCE(s.user_id, '') AS user_id,
|
|
41419
|
+
COALESCE(s.model, '') AS model,
|
|
41420
|
+
COALESCE(s.title, '') AS title,
|
|
41421
|
+
COALESCE(s.started_at, 0) AS started_at,
|
|
41422
|
+
s.ended_at AS ended_at,
|
|
41423
|
+
COALESCE(s.end_reason, '') AS end_reason,
|
|
41424
|
+
COALESCE(s.message_count, 0) AS message_count,
|
|
41425
|
+
COALESCE(s.tool_call_count, 0) AS tool_call_count,
|
|
41426
|
+
COALESCE(s.input_tokens, 0) AS input_tokens,
|
|
41427
|
+
COALESCE(s.output_tokens, 0) AS output_tokens,
|
|
41428
|
+
COALESCE(s.cache_read_tokens, 0) AS cache_read_tokens,
|
|
41429
|
+
COALESCE(s.cache_write_tokens, 0) AS cache_write_tokens,
|
|
41430
|
+
COALESCE(s.reasoning_tokens, 0) AS reasoning_tokens,
|
|
41431
|
+
COALESCE(s.billing_provider, '') AS billing_provider,
|
|
41432
|
+
COALESCE(s.estimated_cost_usd, 0) AS estimated_cost_usd,
|
|
41433
|
+
s.actual_cost_usd AS actual_cost_usd,
|
|
41434
|
+
COALESCE(s.cost_status, '') AS cost_status,
|
|
41435
|
+
COALESCE(
|
|
41436
|
+
(
|
|
41437
|
+
SELECT SUBSTR(REPLACE(REPLACE(m.content, CHAR(10), ' '), CHAR(13), ' '), 1, 63)
|
|
41438
|
+
FROM messages m
|
|
41439
|
+
WHERE m.session_id = s.id AND m.role = 'user' AND m.content IS NOT NULL
|
|
41440
|
+
ORDER BY m.timestamp, m.id
|
|
41441
|
+
LIMIT 1
|
|
41442
|
+
),
|
|
41443
|
+
''
|
|
41444
|
+
) AS preview,
|
|
41445
|
+
COALESCE((SELECT MAX(m2.timestamp) FROM messages m2 WHERE m2.session_id = s.id), s.started_at) AS last_active
|
|
41446
|
+
`;
|
|
41447
|
+
var SESSION_FROM = `
|
|
41076
41448
|
FROM sessions s
|
|
41077
41449
|
WHERE s.parent_session_id IS NULL
|
|
41078
41450
|
AND s.source != 'tool'
|
|
41079
41451
|
`;
|
|
41452
|
+
function buildBaseSessionSql(source) {
|
|
41453
|
+
const sql = source ? `SELECT ${SESSION_SELECT}${SESSION_FROM}
|
|
41454
|
+
AND s.source = ?` : `SELECT ${SESSION_SELECT}${SESSION_FROM}`;
|
|
41455
|
+
return { sql, params: source ? [source] : [] };
|
|
41456
|
+
}
|
|
41457
|
+
function buildListSessionSql(source, limit = 2e3) {
|
|
41458
|
+
const base = buildBaseSessionSql(source);
|
|
41459
|
+
return {
|
|
41460
|
+
sql: `${base.sql}
|
|
41461
|
+
ORDER BY s.started_at DESC
|
|
41462
|
+
LIMIT ?`,
|
|
41463
|
+
params: [...base.params, limit]
|
|
41464
|
+
};
|
|
41465
|
+
}
|
|
41466
|
+
function containsCjk(text) {
|
|
41467
|
+
for (const ch of text) {
|
|
41468
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
41469
|
+
if (cp >= 19968 && cp <= 40959 || cp >= 13312 && cp <= 19903 || cp >= 131072 && cp <= 173791 || cp >= 12288 && cp <= 12351 || cp >= 12352 && cp <= 12447 || cp >= 12448 && cp <= 12543 || cp >= 44032 && cp <= 55215) {
|
|
41470
|
+
return true;
|
|
41471
|
+
}
|
|
41472
|
+
}
|
|
41473
|
+
return false;
|
|
41474
|
+
}
|
|
41475
|
+
function sanitizeFtsQuery(query) {
|
|
41476
|
+
const quotedParts = [];
|
|
41477
|
+
const preserved = query.replace(/"[^"]*"/g, (match) => {
|
|
41478
|
+
quotedParts.push(match);
|
|
41479
|
+
return `\0Q${quotedParts.length - 1}\0`;
|
|
41480
|
+
});
|
|
41481
|
+
let sanitized = preserved.replace(/[+{}()"^]/g, " ");
|
|
41482
|
+
sanitized = sanitized.replace(/\*+/g, "*");
|
|
41483
|
+
sanitized = sanitized.replace(/(^|\s)\*/g, "$1");
|
|
41484
|
+
sanitized = sanitized.trim().replace(/^(AND|OR|NOT)\b\s*/i, "");
|
|
41485
|
+
sanitized = sanitized.trim().replace(/\s+(AND|OR|NOT)\s*$/i, "");
|
|
41486
|
+
sanitized = sanitized.replace(/\b(\w+(?:[.-]\w+)+)\b/g, '"$1"');
|
|
41487
|
+
for (let i = 0; i < quotedParts.length; i += 1) {
|
|
41488
|
+
sanitized = sanitized.replace(`\0Q${i}\0`, quotedParts[i]);
|
|
41489
|
+
}
|
|
41490
|
+
return sanitized.trim();
|
|
41491
|
+
}
|
|
41492
|
+
function toPrefixQuery(query) {
|
|
41493
|
+
const tokens = query.match(/"[^"]*"|\S+/g);
|
|
41494
|
+
if (!tokens) return "";
|
|
41495
|
+
return tokens.map((token) => {
|
|
41496
|
+
if (token === "AND" || token === "OR" || token === "NOT") return token;
|
|
41497
|
+
if (token.startsWith('"') && token.endsWith('"')) return token;
|
|
41498
|
+
if (token.endsWith("*")) return token;
|
|
41499
|
+
return `${token}*`;
|
|
41500
|
+
}).join(" ");
|
|
41501
|
+
}
|
|
41502
|
+
function mapSearchRow(row) {
|
|
41503
|
+
return {
|
|
41504
|
+
...mapRow(row),
|
|
41505
|
+
matched_message_id: normalizeNullableNumber(row.matched_message_id),
|
|
41506
|
+
snippet: String(row.snippet || row.preview || ""),
|
|
41507
|
+
rank: Number.isFinite(Number(row.rank)) ? Number(row.rank) : 0
|
|
41508
|
+
};
|
|
41509
|
+
}
|
|
41080
41510
|
async function listSessionSummaries(source, limit = 2e3) {
|
|
41081
|
-
if (!
|
|
41511
|
+
if (!SQLITE_AVAILABLE2) {
|
|
41082
41512
|
throw new Error(`node:sqlite requires Node >= 22.5, current: ${process.versions.node}`);
|
|
41083
41513
|
}
|
|
41084
|
-
const { DatabaseSync } = await import("node:sqlite");
|
|
41085
|
-
const db = new
|
|
41514
|
+
const { DatabaseSync: DatabaseSync2 } = await import("node:sqlite");
|
|
41515
|
+
const db = new DatabaseSync2(sessionDbPath(), { open: true, readOnly: true });
|
|
41086
41516
|
try {
|
|
41087
|
-
const sql = source
|
|
41088
|
-
AND s.source = ?
|
|
41089
|
-
ORDER BY s.started_at DESC
|
|
41090
|
-
LIMIT ?` : `${BASE_SELECT}
|
|
41091
|
-
ORDER BY s.started_at DESC
|
|
41092
|
-
LIMIT ?`;
|
|
41517
|
+
const { sql, params } = buildListSessionSql(source, limit);
|
|
41093
41518
|
const statement = db.prepare(sql);
|
|
41094
|
-
const rows =
|
|
41519
|
+
const rows = statement.all(...params);
|
|
41095
41520
|
return rows.map(mapRow);
|
|
41096
41521
|
} finally {
|
|
41097
41522
|
db.close();
|
|
41098
41523
|
}
|
|
41099
41524
|
}
|
|
41525
|
+
async function searchSessionSummaries(query, source, limit = 20) {
|
|
41526
|
+
if (!SQLITE_AVAILABLE2) {
|
|
41527
|
+
throw new Error(`node:sqlite requires Node >= 22.5, current: ${process.versions.node}`);
|
|
41528
|
+
}
|
|
41529
|
+
const trimmed = query.trim();
|
|
41530
|
+
if (!trimmed) {
|
|
41531
|
+
const recent = await listSessionSummaries(source, limit);
|
|
41532
|
+
return recent.map((row) => ({
|
|
41533
|
+
...row,
|
|
41534
|
+
matched_message_id: null,
|
|
41535
|
+
snippet: row.preview,
|
|
41536
|
+
rank: 0
|
|
41537
|
+
}));
|
|
41538
|
+
}
|
|
41539
|
+
const { DatabaseSync: DatabaseSync2 } = await import("node:sqlite");
|
|
41540
|
+
const db = new DatabaseSync2(sessionDbPath(), { open: true, readOnly: true });
|
|
41541
|
+
const normalized = sanitizeFtsQuery(trimmed);
|
|
41542
|
+
const prefixQuery = toPrefixQuery(normalized);
|
|
41543
|
+
try {
|
|
41544
|
+
const titleBase = buildBaseSessionSql(source);
|
|
41545
|
+
const contentBase = buildBaseSessionSql(source);
|
|
41546
|
+
const titleSql = `
|
|
41547
|
+
WITH base AS (
|
|
41548
|
+
${titleBase.sql}
|
|
41549
|
+
)
|
|
41550
|
+
SELECT
|
|
41551
|
+
base.*,
|
|
41552
|
+
NULL AS matched_message_id,
|
|
41553
|
+
CASE
|
|
41554
|
+
WHEN base.title IS NOT NULL AND base.title != '' THEN base.title
|
|
41555
|
+
ELSE base.preview
|
|
41556
|
+
END AS snippet,
|
|
41557
|
+
0 AS rank
|
|
41558
|
+
FROM base
|
|
41559
|
+
WHERE LOWER(COALESCE(base.title, '')) LIKE ?
|
|
41560
|
+
ORDER BY base.last_active DESC
|
|
41561
|
+
LIMIT ?
|
|
41562
|
+
`;
|
|
41563
|
+
const titleStatement = db.prepare(titleSql);
|
|
41564
|
+
const titleRows = titleStatement.all(...titleBase.params, `%${trimmed.toLowerCase()}%`, limit);
|
|
41565
|
+
const contentSql = `
|
|
41566
|
+
WITH base AS (
|
|
41567
|
+
${contentBase.sql}
|
|
41568
|
+
)
|
|
41569
|
+
SELECT
|
|
41570
|
+
base.*,
|
|
41571
|
+
m.id AS matched_message_id,
|
|
41572
|
+
snippet(messages_fts, 0, '>>>', '<<<', '...', 40) AS snippet,
|
|
41573
|
+
bm25(messages_fts) AS rank
|
|
41574
|
+
FROM messages_fts
|
|
41575
|
+
JOIN messages m ON m.id = messages_fts.rowid
|
|
41576
|
+
JOIN base ON base.id = m.session_id
|
|
41577
|
+
WHERE messages_fts MATCH ?
|
|
41578
|
+
ORDER BY rank, base.last_active DESC
|
|
41579
|
+
LIMIT ?
|
|
41580
|
+
`;
|
|
41581
|
+
const contentRows = prefixQuery ? db.prepare(contentSql).all(...contentBase.params, prefixQuery, limit * 4) : [];
|
|
41582
|
+
const merged = /* @__PURE__ */ new Map();
|
|
41583
|
+
for (const row of titleRows) {
|
|
41584
|
+
const mapped = mapSearchRow(row);
|
|
41585
|
+
merged.set(mapped.id, mapped);
|
|
41586
|
+
}
|
|
41587
|
+
for (const row of contentRows) {
|
|
41588
|
+
const mapped = mapSearchRow(row);
|
|
41589
|
+
if (!merged.has(mapped.id)) {
|
|
41590
|
+
merged.set(mapped.id, mapped);
|
|
41591
|
+
}
|
|
41592
|
+
}
|
|
41593
|
+
const items = [...merged.values()];
|
|
41594
|
+
items.sort((a, b) => {
|
|
41595
|
+
if (a.rank !== b.rank) return a.rank - b.rank;
|
|
41596
|
+
return b.last_active - a.last_active;
|
|
41597
|
+
});
|
|
41598
|
+
return items.slice(0, limit);
|
|
41599
|
+
} catch (err) {
|
|
41600
|
+
if (containsCjk(normalized)) {
|
|
41601
|
+
const likeBase = buildBaseSessionSql(source);
|
|
41602
|
+
const likeSql = `
|
|
41603
|
+
WITH base AS (
|
|
41604
|
+
${likeBase.sql}
|
|
41605
|
+
)
|
|
41606
|
+
SELECT
|
|
41607
|
+
base.*,
|
|
41608
|
+
m.id AS matched_message_id,
|
|
41609
|
+
substr(
|
|
41610
|
+
m.content,
|
|
41611
|
+
max(1, instr(m.content, ?) - 40),
|
|
41612
|
+
120
|
|
41613
|
+
) AS snippet,
|
|
41614
|
+
0 AS rank
|
|
41615
|
+
FROM base
|
|
41616
|
+
JOIN messages m ON m.session_id = base.id
|
|
41617
|
+
WHERE m.content LIKE ?
|
|
41618
|
+
ORDER BY base.last_active DESC, m.timestamp DESC
|
|
41619
|
+
`;
|
|
41620
|
+
const likeStatement = db.prepare(likeSql);
|
|
41621
|
+
const likeRows = likeStatement.all(...likeBase.params, trimmed, `%${trimmed}%`);
|
|
41622
|
+
const merged = /* @__PURE__ */ new Map();
|
|
41623
|
+
for (const row of likeRows) {
|
|
41624
|
+
const mapped = mapSearchRow(row);
|
|
41625
|
+
if (!merged.has(mapped.id)) {
|
|
41626
|
+
merged.set(mapped.id, mapped);
|
|
41627
|
+
}
|
|
41628
|
+
}
|
|
41629
|
+
return [...merged.values()].slice(0, limit);
|
|
41630
|
+
}
|
|
41631
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
41632
|
+
throw new Error(`Failed to search sessions: ${message2}`);
|
|
41633
|
+
} finally {
|
|
41634
|
+
db.close();
|
|
41635
|
+
}
|
|
41636
|
+
}
|
|
41637
|
+
|
|
41638
|
+
// packages/server/src/services/hermes/model-context.ts
|
|
41639
|
+
var import_path10 = require("path");
|
|
41640
|
+
var import_os8 = require("os");
|
|
41641
|
+
var import_fs8 = require("fs");
|
|
41642
|
+
var HERMES_BASE3 = (0, import_path10.resolve)((0, import_os8.homedir)(), ".hermes");
|
|
41643
|
+
var MODELS_DEV_CACHE = (0, import_path10.resolve)(HERMES_BASE3, "models_dev_cache.json");
|
|
41644
|
+
var DEFAULT_CONTEXT_LENGTH = 2e5;
|
|
41645
|
+
var _cache = null;
|
|
41646
|
+
var _cacheMtime = 0;
|
|
41647
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
41648
|
+
var _cacheLoadedAt = 0;
|
|
41649
|
+
function loadModelsDevCache() {
|
|
41650
|
+
if (!(0, import_fs8.existsSync)(MODELS_DEV_CACHE)) return null;
|
|
41651
|
+
try {
|
|
41652
|
+
const stat2 = (0, import_fs8.statSync)(MODELS_DEV_CACHE);
|
|
41653
|
+
const now = Date.now();
|
|
41654
|
+
if (_cache && stat2.mtimeMs === _cacheMtime && now - _cacheLoadedAt < CACHE_TTL_MS) {
|
|
41655
|
+
return _cache;
|
|
41656
|
+
}
|
|
41657
|
+
const raw = (0, import_fs8.readFileSync)(MODELS_DEV_CACHE, "utf-8");
|
|
41658
|
+
_cache = JSON.parse(raw);
|
|
41659
|
+
_cacheMtime = stat2.mtimeMs;
|
|
41660
|
+
_cacheLoadedAt = now;
|
|
41661
|
+
return _cache;
|
|
41662
|
+
} catch {
|
|
41663
|
+
return _cache;
|
|
41664
|
+
}
|
|
41665
|
+
}
|
|
41666
|
+
function getProfileDir2(profile) {
|
|
41667
|
+
if (!profile || profile === "default") return HERMES_BASE3;
|
|
41668
|
+
const dir = (0, import_path10.join)(HERMES_BASE3, "profiles", profile);
|
|
41669
|
+
return (0, import_fs8.existsSync)(dir) ? dir : HERMES_BASE3;
|
|
41670
|
+
}
|
|
41671
|
+
function getDefaultModel(profileDir) {
|
|
41672
|
+
const configPath3 = (0, import_path10.join)(profileDir, "config.yaml");
|
|
41673
|
+
if (!(0, import_fs8.existsSync)(configPath3)) return null;
|
|
41674
|
+
try {
|
|
41675
|
+
const content = (0, import_fs8.readFileSync)(configPath3, "utf-8");
|
|
41676
|
+
const match = content.match(/^model:\s*\n\s+default:\s*(.+)$/m);
|
|
41677
|
+
return match ? match[1].trim() : null;
|
|
41678
|
+
} catch {
|
|
41679
|
+
return null;
|
|
41680
|
+
}
|
|
41681
|
+
}
|
|
41682
|
+
function lookupContextFromCache(modelName) {
|
|
41683
|
+
const data = loadModelsDevCache();
|
|
41684
|
+
if (!data) return null;
|
|
41685
|
+
for (const prov of Object.values(data)) {
|
|
41686
|
+
const models = prov.models || {};
|
|
41687
|
+
const entry = models[modelName];
|
|
41688
|
+
if (entry?.limit?.context) return entry.limit.context;
|
|
41689
|
+
}
|
|
41690
|
+
const lower = modelName.toLowerCase();
|
|
41691
|
+
for (const prov of Object.values(data)) {
|
|
41692
|
+
const models = prov.models || {};
|
|
41693
|
+
for (const [name, entry] of Object.entries(models)) {
|
|
41694
|
+
if (name.toLowerCase() === lower && entry?.limit?.context) {
|
|
41695
|
+
return entry.limit.context;
|
|
41696
|
+
}
|
|
41697
|
+
}
|
|
41698
|
+
}
|
|
41699
|
+
return null;
|
|
41700
|
+
}
|
|
41701
|
+
function getModelContextLength(profile) {
|
|
41702
|
+
const profileDir = getProfileDir2(profile);
|
|
41703
|
+
const model = getDefaultModel(profileDir);
|
|
41704
|
+
if (!model) return DEFAULT_CONTEXT_LENGTH;
|
|
41705
|
+
return lookupContextFromCache(model) || DEFAULT_CONTEXT_LENGTH;
|
|
41706
|
+
}
|
|
41100
41707
|
|
|
41101
41708
|
// packages/server/src/controllers/hermes/sessions.ts
|
|
41709
|
+
init_usage_store();
|
|
41102
41710
|
init_logger();
|
|
41103
41711
|
function parseHumanOnly(value) {
|
|
41104
41712
|
if (typeof value !== "string") return true;
|
|
@@ -41140,6 +41748,19 @@ async function list(ctx) {
|
|
|
41140
41748
|
const sessions2 = await listSessions(source, limit);
|
|
41141
41749
|
ctx.body = { sessions: sessions2 };
|
|
41142
41750
|
}
|
|
41751
|
+
async function search(ctx) {
|
|
41752
|
+
const q = typeof ctx.query.q === "string" ? ctx.query.q : "";
|
|
41753
|
+
const source = typeof ctx.query.source === "string" && ctx.query.source.trim() ? ctx.query.source.trim() : void 0;
|
|
41754
|
+
const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : void 0;
|
|
41755
|
+
try {
|
|
41756
|
+
const results = await searchSessionSummaries(q, source, limit && limit > 0 ? limit : 20);
|
|
41757
|
+
ctx.body = { results };
|
|
41758
|
+
} catch (err) {
|
|
41759
|
+
logger.error(err, "Hermes Session DB: search failed");
|
|
41760
|
+
ctx.status = 500;
|
|
41761
|
+
ctx.body = { error: "Failed to search sessions" };
|
|
41762
|
+
}
|
|
41763
|
+
}
|
|
41143
41764
|
async function get(ctx) {
|
|
41144
41765
|
const session = await getSession(ctx.params.id);
|
|
41145
41766
|
if (!session) {
|
|
@@ -41156,8 +41777,26 @@ async function remove(ctx) {
|
|
|
41156
41777
|
ctx.body = { error: "Failed to delete session" };
|
|
41157
41778
|
return;
|
|
41158
41779
|
}
|
|
41780
|
+
deleteUsage(ctx.params.id);
|
|
41159
41781
|
ctx.body = { ok: true };
|
|
41160
41782
|
}
|
|
41783
|
+
async function usageBatch(ctx) {
|
|
41784
|
+
const ids = ctx.query.ids;
|
|
41785
|
+
if (!ids) {
|
|
41786
|
+
ctx.body = {};
|
|
41787
|
+
return;
|
|
41788
|
+
}
|
|
41789
|
+
const idList = ids.split(",").filter(Boolean);
|
|
41790
|
+
ctx.body = getUsageBatch(idList);
|
|
41791
|
+
}
|
|
41792
|
+
async function usageSingle(ctx) {
|
|
41793
|
+
const result = getUsage(ctx.params.id);
|
|
41794
|
+
if (!result) {
|
|
41795
|
+
ctx.body = { input_tokens: 0, output_tokens: 0 };
|
|
41796
|
+
return;
|
|
41797
|
+
}
|
|
41798
|
+
ctx.body = result;
|
|
41799
|
+
}
|
|
41161
41800
|
async function rename(ctx) {
|
|
41162
41801
|
const { title } = ctx.request.body;
|
|
41163
41802
|
if (!title || typeof title !== "string") {
|
|
@@ -41173,21 +41812,30 @@ async function rename(ctx) {
|
|
|
41173
41812
|
}
|
|
41174
41813
|
ctx.body = { ok: true };
|
|
41175
41814
|
}
|
|
41815
|
+
async function contextLength(ctx) {
|
|
41816
|
+
const profile = ctx.query.profile || void 0;
|
|
41817
|
+
ctx.body = { context_length: getModelContextLength(profile) };
|
|
41818
|
+
}
|
|
41176
41819
|
|
|
41177
41820
|
// packages/server/src/routes/hermes/sessions.ts
|
|
41178
41821
|
var sessionRoutes = new router_default();
|
|
41179
41822
|
sessionRoutes.get("/api/hermes/sessions/conversations", listConversations);
|
|
41180
41823
|
sessionRoutes.get("/api/hermes/sessions/conversations/:id/messages", getConversationMessages);
|
|
41181
41824
|
sessionRoutes.get("/api/hermes/sessions", list);
|
|
41825
|
+
sessionRoutes.get("/api/hermes/search/sessions", search);
|
|
41826
|
+
sessionRoutes.get("/api/hermes/sessions/search", search);
|
|
41827
|
+
sessionRoutes.get("/api/hermes/sessions/usage", usageBatch);
|
|
41828
|
+
sessionRoutes.get("/api/hermes/sessions/context-length", contextLength);
|
|
41182
41829
|
sessionRoutes.get("/api/hermes/sessions/:id", get);
|
|
41830
|
+
sessionRoutes.get("/api/hermes/sessions/:id/usage", usageSingle);
|
|
41183
41831
|
sessionRoutes.delete("/api/hermes/sessions/:id", remove);
|
|
41184
41832
|
sessionRoutes.post("/api/hermes/sessions/:id/rename", rename);
|
|
41185
41833
|
|
|
41186
41834
|
// packages/server/src/controllers/hermes/profiles.ts
|
|
41187
|
-
var
|
|
41188
|
-
var
|
|
41189
|
-
var
|
|
41190
|
-
var
|
|
41835
|
+
var import_fs9 = require("fs");
|
|
41836
|
+
var import_promises4 = require("fs/promises");
|
|
41837
|
+
var import_path11 = require("path");
|
|
41838
|
+
var import_os9 = require("os");
|
|
41191
41839
|
init_logger();
|
|
41192
41840
|
async function list2(ctx) {
|
|
41193
41841
|
try {
|
|
@@ -41294,15 +41942,15 @@ async function switchProfile(ctx) {
|
|
|
41294
41942
|
try {
|
|
41295
41943
|
const detail = await getProfile(name);
|
|
41296
41944
|
logger.debug("Profile detail.path = %s", detail.path);
|
|
41297
|
-
if (!(0,
|
|
41945
|
+
if (!(0, import_fs9.existsSync)((0, import_path11.join)(detail.path, "config.yaml"))) {
|
|
41298
41946
|
try {
|
|
41299
41947
|
await setupReset();
|
|
41300
41948
|
} catch {
|
|
41301
41949
|
}
|
|
41302
41950
|
}
|
|
41303
|
-
const profileEnv = (0,
|
|
41304
|
-
if (!(0,
|
|
41305
|
-
(0,
|
|
41951
|
+
const profileEnv = (0, import_path11.join)(detail.path, ".env");
|
|
41952
|
+
if (!(0, import_fs9.existsSync)(profileEnv)) {
|
|
41953
|
+
(0, import_fs9.writeFileSync)(profileEnv, "# Hermes Agent Environment Configuration\n", "utf-8");
|
|
41306
41954
|
logger.info("Created .env for: %s", detail.path);
|
|
41307
41955
|
}
|
|
41308
41956
|
} catch (err) {
|
|
@@ -41316,21 +41964,21 @@ async function switchProfile(ctx) {
|
|
|
41316
41964
|
}
|
|
41317
41965
|
async function exportProfile2(ctx) {
|
|
41318
41966
|
const { name } = ctx.params;
|
|
41319
|
-
const outputPath = (0,
|
|
41967
|
+
const outputPath = (0, import_path11.join)((0, import_os9.tmpdir)(), `hermes-profile-${name}.tar.gz`);
|
|
41320
41968
|
try {
|
|
41321
41969
|
await exportProfile(name, outputPath);
|
|
41322
|
-
if (!(0,
|
|
41970
|
+
if (!(0, import_fs9.existsSync)(outputPath)) {
|
|
41323
41971
|
ctx.status = 500;
|
|
41324
41972
|
ctx.body = { error: "Export file not found" };
|
|
41325
41973
|
return;
|
|
41326
41974
|
}
|
|
41327
|
-
const filename = (0,
|
|
41975
|
+
const filename = (0, import_path11.basename)(outputPath);
|
|
41328
41976
|
ctx.set("Content-Disposition", `attachment; filename="${filename}"`);
|
|
41329
41977
|
ctx.set("Content-Type", "application/gzip");
|
|
41330
|
-
ctx.body = (0,
|
|
41978
|
+
ctx.body = (0, import_fs9.createReadStream)(outputPath);
|
|
41331
41979
|
ctx.res.on("finish", () => {
|
|
41332
41980
|
try {
|
|
41333
|
-
(0,
|
|
41981
|
+
(0, import_fs9.unlinkSync)(outputPath);
|
|
41334
41982
|
} catch {
|
|
41335
41983
|
}
|
|
41336
41984
|
});
|
|
@@ -41352,8 +42000,8 @@ async function importProfile2(ctx) {
|
|
|
41352
42000
|
ctx.body = { error: "Missing boundary" };
|
|
41353
42001
|
return;
|
|
41354
42002
|
}
|
|
41355
|
-
const tmpDir = (0,
|
|
41356
|
-
await (0,
|
|
42003
|
+
const tmpDir = (0, import_path11.join)((0, import_os9.tmpdir)(), "hermes-import");
|
|
42004
|
+
await (0, import_promises4.mkdir)(tmpDir, { recursive: true });
|
|
41357
42005
|
const chunks = [];
|
|
41358
42006
|
for await (const chunk of ctx.req) chunks.push(chunk);
|
|
41359
42007
|
const body = Buffer.concat(chunks).toString("latin1");
|
|
@@ -41369,8 +42017,8 @@ async function importProfile2(ctx) {
|
|
|
41369
42017
|
const filename = filenameMatch[1];
|
|
41370
42018
|
const ext = filename.includes(".") ? "." + filename.split(".").pop() : "";
|
|
41371
42019
|
if (![".gz", ".tar.gz", ".zip", ".tgz"].includes(ext)) continue;
|
|
41372
|
-
archivePath = (0,
|
|
41373
|
-
await (0,
|
|
42020
|
+
archivePath = (0, import_path11.join)(tmpDir, filename);
|
|
42021
|
+
await (0, import_promises4.writeFile)(archivePath, Buffer.from(data, "binary"));
|
|
41374
42022
|
break;
|
|
41375
42023
|
}
|
|
41376
42024
|
if (!archivePath) {
|
|
@@ -41381,13 +42029,13 @@ async function importProfile2(ctx) {
|
|
|
41381
42029
|
try {
|
|
41382
42030
|
const result = await importProfile(archivePath);
|
|
41383
42031
|
try {
|
|
41384
|
-
(0,
|
|
42032
|
+
(0, import_fs9.unlinkSync)(archivePath);
|
|
41385
42033
|
} catch {
|
|
41386
42034
|
}
|
|
41387
42035
|
ctx.body = { success: true, message: result.trim() };
|
|
41388
42036
|
} catch (err) {
|
|
41389
42037
|
try {
|
|
41390
|
-
(0,
|
|
42038
|
+
(0, import_fs9.unlinkSync)(archivePath);
|
|
41391
42039
|
} catch {
|
|
41392
42040
|
}
|
|
41393
42041
|
ctx.status = 500;
|
|
@@ -41407,9 +42055,9 @@ profileRoutes.post("/api/hermes/profiles/:name/export", exportProfile2);
|
|
|
41407
42055
|
profileRoutes.post("/api/hermes/profiles/import", importProfile2);
|
|
41408
42056
|
|
|
41409
42057
|
// packages/server/src/services/config-helpers.ts
|
|
41410
|
-
var import_promises4 = require("fs/promises");
|
|
41411
42058
|
var import_promises5 = require("fs/promises");
|
|
41412
|
-
var
|
|
42059
|
+
var import_promises6 = require("fs/promises");
|
|
42060
|
+
var import_path12 = require("path");
|
|
41413
42061
|
init_js_yaml();
|
|
41414
42062
|
init_hermes_profile();
|
|
41415
42063
|
init_logger();
|
|
@@ -41443,19 +42091,19 @@ async function readConfigYaml() {
|
|
|
41443
42091
|
}
|
|
41444
42092
|
async function writeConfigYaml(config2) {
|
|
41445
42093
|
const cp = configPath();
|
|
41446
|
-
await (0,
|
|
42094
|
+
await (0, import_promises5.copyFile)(cp, cp + ".bak");
|
|
41447
42095
|
const yamlStr = jsYaml.dump(config2, {
|
|
41448
42096
|
lineWidth: -1,
|
|
41449
42097
|
noRefs: true,
|
|
41450
42098
|
quotingType: '"'
|
|
41451
42099
|
});
|
|
41452
|
-
await (0,
|
|
42100
|
+
await (0, import_promises5.writeFile)(cp, yamlStr, "utf-8");
|
|
41453
42101
|
}
|
|
41454
42102
|
async function saveEnvValue(key, value) {
|
|
41455
42103
|
const envPath3 = getActiveEnvPath();
|
|
41456
42104
|
let raw;
|
|
41457
42105
|
try {
|
|
41458
|
-
raw = await (0,
|
|
42106
|
+
raw = await (0, import_promises5.readFile)(envPath3, "utf-8");
|
|
41459
42107
|
} catch {
|
|
41460
42108
|
raw = "";
|
|
41461
42109
|
}
|
|
@@ -41482,22 +42130,22 @@ async function saveEnvValue(key, value) {
|
|
|
41482
42130
|
result.push(`${key}=${value}`);
|
|
41483
42131
|
}
|
|
41484
42132
|
let output = result.join("\n").replace(/\n{3,}/g, "\n\n").replace(/\n+$/, "") + "\n";
|
|
41485
|
-
await (0,
|
|
42133
|
+
await (0, import_promises5.writeFile)(envPath3, output, "utf-8");
|
|
41486
42134
|
try {
|
|
41487
|
-
await (0,
|
|
42135
|
+
await (0, import_promises5.chmod)(envPath3, 384);
|
|
41488
42136
|
} catch {
|
|
41489
42137
|
}
|
|
41490
42138
|
}
|
|
41491
42139
|
async function safeReadFile(filePath) {
|
|
41492
42140
|
try {
|
|
41493
|
-
return await (0,
|
|
42141
|
+
return await (0, import_promises5.readFile)(filePath, "utf-8");
|
|
41494
42142
|
} catch {
|
|
41495
42143
|
return null;
|
|
41496
42144
|
}
|
|
41497
42145
|
}
|
|
41498
42146
|
async function safeStat(filePath) {
|
|
41499
42147
|
try {
|
|
41500
|
-
const s = await (0,
|
|
42148
|
+
const s = await (0, import_promises6.stat)(filePath);
|
|
41501
42149
|
return { mtime: Math.round(s.mtimeMs) };
|
|
41502
42150
|
} catch {
|
|
41503
42151
|
return null;
|
|
@@ -41529,14 +42177,14 @@ async function listFilesRecursive(dir, prefix) {
|
|
|
41529
42177
|
const result = [];
|
|
41530
42178
|
let entries;
|
|
41531
42179
|
try {
|
|
41532
|
-
entries = await (0,
|
|
42180
|
+
entries = await (0, import_promises6.readdir)(dir, { withFileTypes: true });
|
|
41533
42181
|
} catch {
|
|
41534
42182
|
return result;
|
|
41535
42183
|
}
|
|
41536
42184
|
for (const entry of entries) {
|
|
41537
42185
|
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
41538
42186
|
if (entry.isDirectory()) {
|
|
41539
|
-
result.push(...await listFilesRecursive((0,
|
|
42187
|
+
result.push(...await listFilesRecursive((0, import_path12.join)(dir, entry.name), relPath));
|
|
41540
42188
|
} else {
|
|
41541
42189
|
result.push({ path: relPath, name: entry.name });
|
|
41542
42190
|
}
|
|
@@ -41596,25 +42244,25 @@ function buildModelGroups(config2) {
|
|
|
41596
42244
|
var getHermesDir = () => getActiveProfileDir();
|
|
41597
42245
|
|
|
41598
42246
|
// packages/server/src/controllers/hermes/skills.ts
|
|
41599
|
-
var
|
|
41600
|
-
var
|
|
42247
|
+
var import_promises7 = require("fs/promises");
|
|
42248
|
+
var import_path13 = require("path");
|
|
41601
42249
|
async function list3(ctx) {
|
|
41602
|
-
const skillsDir = (0,
|
|
42250
|
+
const skillsDir = (0, import_path13.join)(getHermesDir(), "skills");
|
|
41603
42251
|
try {
|
|
41604
42252
|
const config2 = await readConfigYaml();
|
|
41605
42253
|
const disabledList = config2.skills?.disabled || [];
|
|
41606
|
-
const entries = await (0,
|
|
42254
|
+
const entries = await (0, import_promises7.readdir)(skillsDir, { withFileTypes: true });
|
|
41607
42255
|
const categories = [];
|
|
41608
42256
|
for (const entry of entries) {
|
|
41609
42257
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
41610
|
-
const catDir = (0,
|
|
41611
|
-
const catDesc = await safeReadFile((0,
|
|
42258
|
+
const catDir = (0, import_path13.join)(skillsDir, entry.name);
|
|
42259
|
+
const catDesc = await safeReadFile((0, import_path13.join)(catDir, "DESCRIPTION.md"));
|
|
41612
42260
|
const catDescription = catDesc ? catDesc.trim().split("\n")[0].replace(/^#+\s*/, "").slice(0, 100) : "";
|
|
41613
|
-
const skillEntries = await (0,
|
|
42261
|
+
const skillEntries = await (0, import_promises7.readdir)(catDir, { withFileTypes: true });
|
|
41614
42262
|
const skills = [];
|
|
41615
42263
|
for (const se of skillEntries) {
|
|
41616
42264
|
if (!se.isDirectory()) continue;
|
|
41617
|
-
const skillMd = await safeReadFile((0,
|
|
42265
|
+
const skillMd = await safeReadFile((0, import_path13.join)(catDir, se.name, "SKILL.md"));
|
|
41618
42266
|
if (skillMd) {
|
|
41619
42267
|
skills.push({ name: se.name, description: extractDescription(skillMd), enabled: !disabledList.includes(se.name) });
|
|
41620
42268
|
}
|
|
@@ -41660,7 +42308,7 @@ async function toggle(ctx) {
|
|
|
41660
42308
|
}
|
|
41661
42309
|
async function listFiles(ctx) {
|
|
41662
42310
|
const { category, skill } = ctx.params;
|
|
41663
|
-
const skillDir = (0,
|
|
42311
|
+
const skillDir = (0, import_path13.join)(getHermesDir(), "skills", category, skill);
|
|
41664
42312
|
try {
|
|
41665
42313
|
const allFiles = await listFilesRecursive(skillDir, "");
|
|
41666
42314
|
const files = allFiles.filter((f) => f.path !== "SKILL.md");
|
|
@@ -41673,8 +42321,8 @@ async function listFiles(ctx) {
|
|
|
41673
42321
|
async function readFile_(ctx) {
|
|
41674
42322
|
const filePath = ctx.params.path;
|
|
41675
42323
|
const hd = getHermesDir();
|
|
41676
|
-
const fullPath = (0,
|
|
41677
|
-
if (!fullPath.startsWith((0,
|
|
42324
|
+
const fullPath = (0, import_path13.resolve)((0, import_path13.join)(hd, "skills", filePath));
|
|
42325
|
+
if (!fullPath.startsWith((0, import_path13.join)(hd, "skills"))) {
|
|
41678
42326
|
ctx.status = 403;
|
|
41679
42327
|
ctx.body = { error: "Access denied" };
|
|
41680
42328
|
return;
|
|
@@ -41696,13 +42344,13 @@ skillRoutes.get("/api/hermes/skills/:category/:skill/files", listFiles);
|
|
|
41696
42344
|
skillRoutes.get("/api/hermes/skills/{*path}", readFile_);
|
|
41697
42345
|
|
|
41698
42346
|
// packages/server/src/controllers/hermes/memory.ts
|
|
41699
|
-
var
|
|
41700
|
-
var
|
|
42347
|
+
var import_promises8 = require("fs/promises");
|
|
42348
|
+
var import_path14 = require("path");
|
|
41701
42349
|
async function get3(ctx) {
|
|
41702
42350
|
const hd = getHermesDir();
|
|
41703
|
-
const memoryPath = (0,
|
|
41704
|
-
const userPath = (0,
|
|
41705
|
-
const soulPath = (0,
|
|
42351
|
+
const memoryPath = (0, import_path14.join)(hd, "memories", "MEMORY.md");
|
|
42352
|
+
const userPath = (0, import_path14.join)(hd, "memories", "USER.md");
|
|
42353
|
+
const soulPath = (0, import_path14.join)(hd, "SOUL.md");
|
|
41706
42354
|
const [memory, user, soul, memoryStat, userStat, soulStat] = await Promise.all([
|
|
41707
42355
|
safeReadFile(memoryPath),
|
|
41708
42356
|
safeReadFile(userPath),
|
|
@@ -41734,13 +42382,13 @@ async function save(ctx) {
|
|
|
41734
42382
|
}
|
|
41735
42383
|
let filePath;
|
|
41736
42384
|
if (section === "soul") {
|
|
41737
|
-
filePath = (0,
|
|
42385
|
+
filePath = (0, import_path14.join)(getHermesDir(), "SOUL.md");
|
|
41738
42386
|
} else {
|
|
41739
42387
|
const fileName = section === "memory" ? "MEMORY.md" : "USER.md";
|
|
41740
|
-
filePath = (0,
|
|
42388
|
+
filePath = (0, import_path14.join)(getHermesDir(), "memories", fileName);
|
|
41741
42389
|
}
|
|
41742
42390
|
try {
|
|
41743
|
-
await (0,
|
|
42391
|
+
await (0, import_promises8.writeFile)(filePath, content, "utf-8");
|
|
41744
42392
|
ctx.body = { success: true };
|
|
41745
42393
|
} catch (err) {
|
|
41746
42394
|
ctx.status = 500;
|
|
@@ -42009,8 +42657,8 @@ function buildProviderModelMap() {
|
|
|
42009
42657
|
}
|
|
42010
42658
|
|
|
42011
42659
|
// packages/server/src/controllers/hermes/models.ts
|
|
42012
|
-
var
|
|
42013
|
-
var
|
|
42660
|
+
var import_promises9 = require("fs/promises");
|
|
42661
|
+
var import_fs11 = require("fs");
|
|
42014
42662
|
init_hermes_profile();
|
|
42015
42663
|
var PROVIDER_MODEL_CATALOG = buildProviderModelMap();
|
|
42016
42664
|
async function getAvailable(ctx) {
|
|
@@ -42029,7 +42677,7 @@ async function getAvailable(ctx) {
|
|
|
42029
42677
|
const seenProviders = /* @__PURE__ */ new Set();
|
|
42030
42678
|
let envContent = "";
|
|
42031
42679
|
try {
|
|
42032
|
-
envContent = await (0,
|
|
42680
|
+
envContent = await (0, import_promises9.readFile)(getActiveEnvPath(), "utf-8");
|
|
42033
42681
|
} catch {
|
|
42034
42682
|
}
|
|
42035
42683
|
const envHasValue = (key) => {
|
|
@@ -42050,8 +42698,8 @@ async function getAvailable(ctx) {
|
|
|
42050
42698
|
const isOAuthAuthorized = (providerKey) => {
|
|
42051
42699
|
try {
|
|
42052
42700
|
const authPath = getActiveAuthPath();
|
|
42053
|
-
if (!(0,
|
|
42054
|
-
const auth = JSON.parse((0,
|
|
42701
|
+
if (!(0, import_fs11.existsSync)(authPath)) return false;
|
|
42702
|
+
const auth = JSON.parse((0, import_fs11.readFileSync)(authPath, "utf-8"));
|
|
42055
42703
|
return !!auth.providers?.[providerKey]?.tokens?.access_token;
|
|
42056
42704
|
} catch {
|
|
42057
42705
|
return false;
|
|
@@ -42152,8 +42800,8 @@ modelRoutes.get("/api/hermes/config/models", getConfigModels);
|
|
|
42152
42800
|
modelRoutes.put("/api/hermes/config/model", setConfigModel);
|
|
42153
42801
|
|
|
42154
42802
|
// packages/server/src/controllers/hermes/providers.ts
|
|
42155
|
-
var
|
|
42156
|
-
var
|
|
42803
|
+
var import_fs12 = require("fs");
|
|
42804
|
+
var import_promises10 = require("fs/promises");
|
|
42157
42805
|
init_hermes_profile();
|
|
42158
42806
|
init_logger();
|
|
42159
42807
|
async function create2(ctx) {
|
|
@@ -42303,15 +42951,15 @@ async function remove3(ctx) {
|
|
|
42303
42951
|
} else if (!envMapping?.api_key_env) {
|
|
42304
42952
|
try {
|
|
42305
42953
|
const authPath = getActiveAuthPath();
|
|
42306
|
-
if ((0,
|
|
42307
|
-
const auth = JSON.parse((0,
|
|
42954
|
+
if ((0, import_fs12.existsSync)(authPath)) {
|
|
42955
|
+
const auth = JSON.parse((0, import_fs12.readFileSync)(authPath, "utf-8"));
|
|
42308
42956
|
if (auth.providers?.[poolKey]) {
|
|
42309
42957
|
delete auth.providers[poolKey];
|
|
42310
42958
|
}
|
|
42311
42959
|
if (auth.credential_pool?.[poolKey]) {
|
|
42312
42960
|
delete auth.credential_pool[poolKey];
|
|
42313
42961
|
}
|
|
42314
|
-
await (0,
|
|
42962
|
+
await (0, import_promises10.writeFile)(authPath, JSON.stringify(auth, null, 2) + "\n", "utf-8");
|
|
42315
42963
|
}
|
|
42316
42964
|
} catch (err) {
|
|
42317
42965
|
logger.error(err, "Failed to clear OAuth tokens for %s", poolKey);
|
|
@@ -42357,7 +43005,7 @@ providerRoutes.put("/api/hermes/config/providers/:poolKey", update);
|
|
|
42357
43005
|
providerRoutes.delete("/api/hermes/config/providers/:poolKey", remove3);
|
|
42358
43006
|
|
|
42359
43007
|
// packages/server/src/controllers/hermes/config.ts
|
|
42360
|
-
var
|
|
43008
|
+
var import_promises11 = require("fs/promises");
|
|
42361
43009
|
init_js_yaml();
|
|
42362
43010
|
init_hermes_profile();
|
|
42363
43011
|
var PLATFORM_SECTIONS = /* @__PURE__ */ new Set([
|
|
@@ -42430,7 +43078,7 @@ function deepMerge2(target, source) {
|
|
|
42430
43078
|
}
|
|
42431
43079
|
async function readEnvPlatforms() {
|
|
42432
43080
|
try {
|
|
42433
|
-
const raw = await (0,
|
|
43081
|
+
const raw = await (0, import_promises11.readFile)(envPath(), "utf-8");
|
|
42434
43082
|
const env = parseEnv(raw);
|
|
42435
43083
|
const platforms = {};
|
|
42436
43084
|
for (const [envKey, [platform, cfgPath]] of Object.entries(envPlatformMap)) {
|
|
@@ -42447,14 +43095,14 @@ async function readEnvPlatforms() {
|
|
|
42447
43095
|
}
|
|
42448
43096
|
}
|
|
42449
43097
|
async function readConfig() {
|
|
42450
|
-
const raw = await (0,
|
|
43098
|
+
const raw = await (0, import_promises11.readFile)(configPath2(), "utf-8");
|
|
42451
43099
|
return jsYaml.load(raw) || {};
|
|
42452
43100
|
}
|
|
42453
43101
|
async function writeConfig(data) {
|
|
42454
43102
|
const cp = configPath2();
|
|
42455
|
-
await (0,
|
|
43103
|
+
await (0, import_promises11.copyFile)(cp, cp + ".bak");
|
|
42456
43104
|
const yamlStr = jsYaml.dump(data, { lineWidth: -1, noRefs: true, quotingType: '"', forceQuotes: false });
|
|
42457
|
-
await (0,
|
|
43105
|
+
await (0, import_promises11.writeFile)(cp, yamlStr, "utf-8");
|
|
42458
43106
|
}
|
|
42459
43107
|
async function getConfig(ctx) {
|
|
42460
43108
|
try {
|
|
@@ -42578,11 +43226,11 @@ configRoutes.put("/api/hermes/config", updateConfig);
|
|
|
42578
43226
|
configRoutes.put("/api/hermes/config/credentials", updateCredentials);
|
|
42579
43227
|
|
|
42580
43228
|
// packages/server/src/controllers/hermes/logs.ts
|
|
42581
|
-
var
|
|
42582
|
-
var
|
|
42583
|
-
var
|
|
42584
|
-
var
|
|
42585
|
-
var WEBUI_LOG_FILE = (0,
|
|
43229
|
+
var import_fs13 = require("fs");
|
|
43230
|
+
var import_promises12 = require("fs/promises");
|
|
43231
|
+
var import_path15 = require("path");
|
|
43232
|
+
var import_os10 = require("os");
|
|
43233
|
+
var WEBUI_LOG_FILE = (0, import_path15.join)((0, import_os10.homedir)(), ".hermes-web-ui", "logs", "server.log");
|
|
42586
43234
|
function parseLine(line) {
|
|
42587
43235
|
try {
|
|
42588
43236
|
const obj = JSON.parse(line);
|
|
@@ -42605,9 +43253,9 @@ function parseLine(line) {
|
|
|
42605
43253
|
}
|
|
42606
43254
|
async function list4(ctx) {
|
|
42607
43255
|
const files = await listLogFiles();
|
|
42608
|
-
if ((0,
|
|
43256
|
+
if ((0, import_fs13.existsSync)(WEBUI_LOG_FILE)) {
|
|
42609
43257
|
try {
|
|
42610
|
-
const stat2 = (0,
|
|
43258
|
+
const stat2 = (0, import_fs13.statSync)(WEBUI_LOG_FILE);
|
|
42611
43259
|
const size = stat2.size > 1024 * 1024 ? `${(stat2.size / 1024 / 1024).toFixed(1)}MB` : `${(stat2.size / 1024).toFixed(1)}KB`;
|
|
42612
43260
|
const modified = stat2.mtime.toLocaleString();
|
|
42613
43261
|
files.push({ name: "webui", size, modified });
|
|
@@ -42624,11 +43272,11 @@ async function read(ctx) {
|
|
|
42624
43272
|
const since = ctx.query.since || void 0;
|
|
42625
43273
|
if (logName === "webui") {
|
|
42626
43274
|
try {
|
|
42627
|
-
if (!(0,
|
|
43275
|
+
if (!(0, import_fs13.existsSync)(WEBUI_LOG_FILE)) {
|
|
42628
43276
|
ctx.body = { entries: [] };
|
|
42629
43277
|
return;
|
|
42630
43278
|
}
|
|
42631
|
-
const content = await (0,
|
|
43279
|
+
const content = await (0, import_promises12.readFile)(WEBUI_LOG_FILE, "utf-8");
|
|
42632
43280
|
const rawLines = content.split("\n");
|
|
42633
43281
|
const sliced = rawLines.length > lines ? rawLines.slice(-lines) : rawLines;
|
|
42634
43282
|
const entries = [];
|
|
@@ -42665,9 +43313,9 @@ logRoutes.get("/api/hermes/logs/:name", read);
|
|
|
42665
43313
|
|
|
42666
43314
|
// packages/server/src/controllers/hermes/codex-auth.ts
|
|
42667
43315
|
var import_crypto3 = require("crypto");
|
|
42668
|
-
var
|
|
42669
|
-
var
|
|
42670
|
-
var
|
|
43316
|
+
var import_path16 = require("path");
|
|
43317
|
+
var import_os11 = require("os");
|
|
43318
|
+
var import_fs14 = require("fs");
|
|
42671
43319
|
init_hermes_profile();
|
|
42672
43320
|
init_logger();
|
|
42673
43321
|
var CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
@@ -42677,7 +43325,7 @@ var CODEX_OAUTH_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
|
42677
43325
|
var CODEX_DEFAULT_BASE_URL = "https://chatgpt.com/backend-api/codex";
|
|
42678
43326
|
var CODEX_REDIRECT_URI = "https://auth.openai.com/deviceauth/callback";
|
|
42679
43327
|
var CODEX_VERIFICATION_URL = "https://auth.openai.com/codex/device";
|
|
42680
|
-
var CODEX_HOME = (0,
|
|
43328
|
+
var CODEX_HOME = (0, import_path16.join)((0, import_os11.homedir)(), ".codex");
|
|
42681
43329
|
var POLL_MAX_DURATION = 15 * 60 * 1e3;
|
|
42682
43330
|
var POLL_DEFAULT_INTERVAL = 5e3;
|
|
42683
43331
|
var sessions = /* @__PURE__ */ new Map();
|
|
@@ -42691,7 +43339,7 @@ function cleanupExpiredSessions() {
|
|
|
42691
43339
|
}
|
|
42692
43340
|
function loadAuthJson(authPath) {
|
|
42693
43341
|
try {
|
|
42694
|
-
return JSON.parse((0,
|
|
43342
|
+
return JSON.parse((0, import_fs14.readFileSync)(authPath, "utf-8"));
|
|
42695
43343
|
} catch {
|
|
42696
43344
|
return { version: 1 };
|
|
42697
43345
|
}
|
|
@@ -42699,15 +43347,15 @@ function loadAuthJson(authPath) {
|
|
|
42699
43347
|
function saveAuthJson(authPath, data) {
|
|
42700
43348
|
data.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
42701
43349
|
const dir = authPath.substring(0, authPath.lastIndexOf("/"));
|
|
42702
|
-
if (!(0,
|
|
42703
|
-
(0,
|
|
43350
|
+
if (!(0, import_fs14.existsSync)(dir)) (0, import_fs14.mkdirSync)(dir, { recursive: true });
|
|
43351
|
+
(0, import_fs14.writeFileSync)(authPath, JSON.stringify(data, null, 2) + "\n", { mode: 384 });
|
|
42704
43352
|
}
|
|
42705
43353
|
function saveCodexCliTokens(accessToken, refreshToken) {
|
|
42706
43354
|
const codexHome = process.env.CODEX_HOME || CODEX_HOME;
|
|
42707
|
-
const codexAuthPath = (0,
|
|
43355
|
+
const codexAuthPath = (0, import_path16.join)(codexHome, "auth.json");
|
|
42708
43356
|
const dir = codexAuthPath.substring(0, codexAuthPath.lastIndexOf("/"));
|
|
42709
|
-
if (!(0,
|
|
42710
|
-
(0,
|
|
43357
|
+
if (!(0, import_fs14.existsSync)(dir)) (0, import_fs14.mkdirSync)(dir, { recursive: true });
|
|
43358
|
+
(0, import_fs14.writeFileSync)(codexAuthPath, JSON.stringify({ tokens: { access_token: accessToken, refresh_token: refreshToken }, last_refresh: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n", { mode: 384 });
|
|
42711
43359
|
}
|
|
42712
43360
|
function decodeJwtExp(token) {
|
|
42713
43361
|
try {
|
|
@@ -42724,7 +43372,7 @@ async function codexLoginWorker(session, authPath) {
|
|
|
42724
43372
|
const startTime = Date.now();
|
|
42725
43373
|
const interval = POLL_DEFAULT_INTERVAL;
|
|
42726
43374
|
while (Date.now() - startTime < POLL_MAX_DURATION) {
|
|
42727
|
-
await new Promise((
|
|
43375
|
+
await new Promise((resolve10) => setTimeout(resolve10, interval));
|
|
42728
43376
|
if (session.status !== "pending") return;
|
|
42729
43377
|
try {
|
|
42730
43378
|
const pollRes = await fetch(CODEX_DEVICE_TOKEN_URL, {
|
|
@@ -44293,10 +44941,10 @@ var CanceledError = class extends AxiosError_default {
|
|
|
44293
44941
|
var CanceledError_default = CanceledError;
|
|
44294
44942
|
|
|
44295
44943
|
// node_modules/axios/lib/core/settle.js
|
|
44296
|
-
function settle(
|
|
44944
|
+
function settle(resolve10, reject, response) {
|
|
44297
44945
|
const validateStatus2 = response.config.validateStatus;
|
|
44298
44946
|
if (!response.status || !validateStatus2 || validateStatus2(response.status)) {
|
|
44299
|
-
|
|
44947
|
+
resolve10(response);
|
|
44300
44948
|
} else {
|
|
44301
44949
|
reject(
|
|
44302
44950
|
new AxiosError_default(
|
|
@@ -45083,7 +45731,7 @@ function setProxy(options, configProxy, location) {
|
|
|
45083
45731
|
}
|
|
45084
45732
|
var isHttpAdapterSupported = typeof process !== "undefined" && utils_default.kindOf(process) === "process";
|
|
45085
45733
|
var wrapAsync = (asyncExecutor) => {
|
|
45086
|
-
return new Promise((
|
|
45734
|
+
return new Promise((resolve10, reject) => {
|
|
45087
45735
|
let onDone;
|
|
45088
45736
|
let isDone;
|
|
45089
45737
|
const done = (value, isRejected) => {
|
|
@@ -45093,7 +45741,7 @@ var wrapAsync = (asyncExecutor) => {
|
|
|
45093
45741
|
};
|
|
45094
45742
|
const _resolve = (value) => {
|
|
45095
45743
|
done(value);
|
|
45096
|
-
|
|
45744
|
+
resolve10(value);
|
|
45097
45745
|
};
|
|
45098
45746
|
const _reject = (reason) => {
|
|
45099
45747
|
done(reason, true);
|
|
@@ -45140,7 +45788,7 @@ var http2Transport = {
|
|
|
45140
45788
|
}
|
|
45141
45789
|
};
|
|
45142
45790
|
var http_default = isHttpAdapterSupported && function httpAdapter(config2) {
|
|
45143
|
-
return wrapAsync(async function dispatchHttpRequest(
|
|
45791
|
+
return wrapAsync(async function dispatchHttpRequest(resolve10, reject, onDone) {
|
|
45144
45792
|
let { data, lookup, family, httpVersion = 1, http2Options } = config2;
|
|
45145
45793
|
const { responseType, responseEncoding } = config2;
|
|
45146
45794
|
const method = config2.method.toUpperCase();
|
|
@@ -45230,7 +45878,7 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config2) {
|
|
|
45230
45878
|
}
|
|
45231
45879
|
let convertedData;
|
|
45232
45880
|
if (method !== "GET") {
|
|
45233
|
-
return settle(
|
|
45881
|
+
return settle(resolve10, reject, {
|
|
45234
45882
|
status: 405,
|
|
45235
45883
|
statusText: "method not allowed",
|
|
45236
45884
|
headers: {},
|
|
@@ -45252,7 +45900,7 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config2) {
|
|
|
45252
45900
|
} else if (responseType === "stream") {
|
|
45253
45901
|
convertedData = import_stream5.default.Readable.from(convertedData);
|
|
45254
45902
|
}
|
|
45255
|
-
return settle(
|
|
45903
|
+
return settle(resolve10, reject, {
|
|
45256
45904
|
data: convertedData,
|
|
45257
45905
|
status: 200,
|
|
45258
45906
|
statusText: "OK",
|
|
@@ -45493,7 +46141,7 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config2) {
|
|
|
45493
46141
|
};
|
|
45494
46142
|
if (responseType === "stream") {
|
|
45495
46143
|
response.data = responseStream;
|
|
45496
|
-
settle(
|
|
46144
|
+
settle(resolve10, reject, response);
|
|
45497
46145
|
} else {
|
|
45498
46146
|
const responseBuffer = [];
|
|
45499
46147
|
let totalResponseBytes = 0;
|
|
@@ -45543,7 +46191,7 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config2) {
|
|
|
45543
46191
|
} catch (err) {
|
|
45544
46192
|
return reject(AxiosError_default.from(err, null, config2, response.request, response));
|
|
45545
46193
|
}
|
|
45546
|
-
settle(
|
|
46194
|
+
settle(resolve10, reject, response);
|
|
45547
46195
|
});
|
|
45548
46196
|
}
|
|
45549
46197
|
abortEmitter.once("abort", (err) => {
|
|
@@ -45804,7 +46452,7 @@ var resolveConfig_default = (config2) => {
|
|
|
45804
46452
|
// node_modules/axios/lib/adapters/xhr.js
|
|
45805
46453
|
var isXHRAdapterSupported = typeof XMLHttpRequest !== "undefined";
|
|
45806
46454
|
var xhr_default = isXHRAdapterSupported && function(config2) {
|
|
45807
|
-
return new Promise(function dispatchXhrRequest(
|
|
46455
|
+
return new Promise(function dispatchXhrRequest(resolve10, reject) {
|
|
45808
46456
|
const _config = resolveConfig_default(config2);
|
|
45809
46457
|
let requestData = _config.data;
|
|
45810
46458
|
const requestHeaders = AxiosHeaders_default.from(_config.headers).normalize();
|
|
@@ -45839,7 +46487,7 @@ var xhr_default = isXHRAdapterSupported && function(config2) {
|
|
|
45839
46487
|
};
|
|
45840
46488
|
settle(
|
|
45841
46489
|
function _resolve(value) {
|
|
45842
|
-
|
|
46490
|
+
resolve10(value);
|
|
45843
46491
|
done();
|
|
45844
46492
|
},
|
|
45845
46493
|
function _reject(err) {
|
|
@@ -46239,8 +46887,8 @@ var factory = (env) => {
|
|
|
46239
46887
|
config2
|
|
46240
46888
|
);
|
|
46241
46889
|
!isStreamResponse && unsubscribe && unsubscribe();
|
|
46242
|
-
return await new Promise((
|
|
46243
|
-
settle(
|
|
46890
|
+
return await new Promise((resolve10, reject) => {
|
|
46891
|
+
settle(resolve10, reject, {
|
|
46244
46892
|
data: responseData,
|
|
46245
46893
|
headers: AxiosHeaders_default.from(response.headers),
|
|
46246
46894
|
status: response.status,
|
|
@@ -46666,8 +47314,8 @@ var CancelToken = class _CancelToken {
|
|
|
46666
47314
|
throw new TypeError("executor must be a function.");
|
|
46667
47315
|
}
|
|
46668
47316
|
let resolvePromise;
|
|
46669
|
-
this.promise = new Promise(function promiseExecutor(
|
|
46670
|
-
resolvePromise =
|
|
47317
|
+
this.promise = new Promise(function promiseExecutor(resolve10) {
|
|
47318
|
+
resolvePromise = resolve10;
|
|
46671
47319
|
});
|
|
46672
47320
|
const token = this;
|
|
46673
47321
|
this.promise.then((cancel) => {
|
|
@@ -46680,9 +47328,9 @@ var CancelToken = class _CancelToken {
|
|
|
46680
47328
|
});
|
|
46681
47329
|
this.promise.then = (onfulfilled) => {
|
|
46682
47330
|
let _resolve;
|
|
46683
|
-
const promise = new Promise((
|
|
46684
|
-
token.subscribe(
|
|
46685
|
-
_resolve =
|
|
47331
|
+
const promise = new Promise((resolve10) => {
|
|
47332
|
+
token.subscribe(resolve10);
|
|
47333
|
+
_resolve = resolve10;
|
|
46686
47334
|
}).then(onfulfilled);
|
|
46687
47335
|
promise.cancel = function reject() {
|
|
46688
47336
|
token.unsubscribe(_resolve);
|
|
@@ -46900,7 +47548,7 @@ var {
|
|
|
46900
47548
|
} = axios_default;
|
|
46901
47549
|
|
|
46902
47550
|
// packages/server/src/controllers/hermes/weixin.ts
|
|
46903
|
-
var
|
|
47551
|
+
var import_promises13 = require("fs/promises");
|
|
46904
47552
|
init_hermes_profile();
|
|
46905
47553
|
var ILINK_BASE = "https://ilinkai.weixin.qq.com";
|
|
46906
47554
|
var envPath2 = () => getActiveEnvPath();
|
|
@@ -46950,7 +47598,7 @@ async function save2(ctx) {
|
|
|
46950
47598
|
try {
|
|
46951
47599
|
let raw;
|
|
46952
47600
|
try {
|
|
46953
|
-
raw = await (0,
|
|
47601
|
+
raw = await (0, import_promises13.readFile)(envPath2(), "utf-8");
|
|
46954
47602
|
} catch {
|
|
46955
47603
|
raw = "";
|
|
46956
47604
|
}
|
|
@@ -46983,9 +47631,9 @@ async function save2(ctx) {
|
|
|
46983
47631
|
}
|
|
46984
47632
|
let output = result.join("\n").replace(/\n{3,}/g, "\n\n").replace(/\n+$/, "") + "\n";
|
|
46985
47633
|
const ep = envPath2();
|
|
46986
|
-
await (0,
|
|
47634
|
+
await (0, import_promises13.writeFile)(ep, output, "utf-8");
|
|
46987
47635
|
try {
|
|
46988
|
-
await (0,
|
|
47636
|
+
await (0, import_promises13.chmod)(ep, 384);
|
|
46989
47637
|
} catch {
|
|
46990
47638
|
}
|
|
46991
47639
|
await restartGateway();
|
|
@@ -47003,9 +47651,18 @@ weixinRoutes.get("/api/hermes/weixin/qrcode/status", pollStatus);
|
|
|
47003
47651
|
weixinRoutes.post("/api/hermes/weixin/save", save2);
|
|
47004
47652
|
|
|
47005
47653
|
// packages/server/src/routes/hermes/proxy-handler.ts
|
|
47654
|
+
init_usage_store();
|
|
47006
47655
|
function getGatewayManager() {
|
|
47007
47656
|
return getGatewayManagerInstance();
|
|
47008
47657
|
}
|
|
47658
|
+
var runSessionMap = /* @__PURE__ */ new Map();
|
|
47659
|
+
function setRunSession(runId, sessionId) {
|
|
47660
|
+
runSessionMap.set(runId, sessionId);
|
|
47661
|
+
setTimeout(() => runSessionMap.delete(runId), 30 * 60 * 1e3);
|
|
47662
|
+
}
|
|
47663
|
+
function getSessionForRun(runId) {
|
|
47664
|
+
return runSessionMap.get(runId);
|
|
47665
|
+
}
|
|
47009
47666
|
function isTransientGatewayError(err) {
|
|
47010
47667
|
const msg = String(err?.message || "");
|
|
47011
47668
|
const causeCode = String(err?.cause?.code || "");
|
|
@@ -47023,7 +47680,7 @@ async function waitForGatewayReady(upstream, timeoutMs = 5e3) {
|
|
|
47023
47680
|
if (res.ok) return true;
|
|
47024
47681
|
} catch {
|
|
47025
47682
|
}
|
|
47026
|
-
await new Promise((
|
|
47683
|
+
await new Promise((resolve10) => setTimeout(resolve10, 250));
|
|
47027
47684
|
}
|
|
47028
47685
|
return false;
|
|
47029
47686
|
}
|
|
@@ -47041,14 +47698,7 @@ function resolveUpstream(ctx) {
|
|
|
47041
47698
|
}
|
|
47042
47699
|
return config.upstream.replace(/\/$/, "");
|
|
47043
47700
|
}
|
|
47044
|
-
|
|
47045
|
-
const profile = resolveProfile(ctx);
|
|
47046
|
-
const upstream = resolveUpstream(ctx);
|
|
47047
|
-
const upstreamPath = ctx.path.replace(/^\/api\/hermes\/v1/, "/v1").replace(/^\/api\/hermes/, "/api");
|
|
47048
|
-
const params = new URLSearchParams(ctx.search || "");
|
|
47049
|
-
params.delete("token");
|
|
47050
|
-
const search = params.toString();
|
|
47051
|
-
const url2 = `${upstream}${upstreamPath}${search ? `?${search}` : ""}`;
|
|
47701
|
+
function buildProxyHeaders(ctx, upstream) {
|
|
47052
47702
|
const headers = {};
|
|
47053
47703
|
for (const [key, value] of Object.entries(ctx.headers)) {
|
|
47054
47704
|
if (value == null) continue;
|
|
@@ -47064,21 +47714,80 @@ async function proxy(ctx) {
|
|
|
47064
47714
|
}
|
|
47065
47715
|
const mgr = getGatewayManager();
|
|
47066
47716
|
if (mgr) {
|
|
47067
|
-
const apiKey = mgr.getApiKey(
|
|
47717
|
+
const apiKey = mgr.getApiKey(resolveProfile(ctx));
|
|
47068
47718
|
if (apiKey) {
|
|
47069
47719
|
headers["authorization"] = `Bearer ${apiKey}`;
|
|
47070
47720
|
}
|
|
47071
47721
|
}
|
|
47722
|
+
return headers;
|
|
47723
|
+
}
|
|
47724
|
+
var SSE_EVENTS_PATH = /^\/v1\/runs\/([^/]+)\/events$/;
|
|
47725
|
+
function extractRunCompletedFromChunk(chunk) {
|
|
47726
|
+
const lines = chunk.split("\n");
|
|
47727
|
+
for (const line of lines) {
|
|
47728
|
+
if (!line.startsWith("data: ")) continue;
|
|
47729
|
+
try {
|
|
47730
|
+
const data = JSON.parse(line.slice(6));
|
|
47731
|
+
if (data.event === "run.completed" && data.usage && data.run_id) {
|
|
47732
|
+
const sessionId = getSessionForRun(data.run_id);
|
|
47733
|
+
if (sessionId) {
|
|
47734
|
+
updateUsage(sessionId, data.usage.input_tokens, data.usage.output_tokens);
|
|
47735
|
+
return data.run_id;
|
|
47736
|
+
}
|
|
47737
|
+
}
|
|
47738
|
+
} catch {
|
|
47739
|
+
}
|
|
47740
|
+
}
|
|
47741
|
+
return null;
|
|
47742
|
+
}
|
|
47743
|
+
async function streamSSE(ctx, res) {
|
|
47744
|
+
if (!res.body) {
|
|
47745
|
+
ctx.res.end();
|
|
47746
|
+
return;
|
|
47747
|
+
}
|
|
47748
|
+
const reader = res.body.getReader();
|
|
47749
|
+
const decoder = new TextDecoder();
|
|
47750
|
+
let buffer = "";
|
|
47751
|
+
try {
|
|
47752
|
+
while (true) {
|
|
47753
|
+
const { done, value } = await reader.read();
|
|
47754
|
+
if (done) break;
|
|
47755
|
+
ctx.res.write(value);
|
|
47756
|
+
buffer += decoder.decode(value, { stream: true });
|
|
47757
|
+
let newlineIdx;
|
|
47758
|
+
while ((newlineIdx = buffer.indexOf("\n\n")) !== -1) {
|
|
47759
|
+
const eventBlock = buffer.slice(0, newlineIdx);
|
|
47760
|
+
buffer = buffer.slice(newlineIdx + 2);
|
|
47761
|
+
extractRunCompletedFromChunk(eventBlock);
|
|
47762
|
+
}
|
|
47763
|
+
}
|
|
47764
|
+
if (buffer.trim()) {
|
|
47765
|
+
extractRunCompletedFromChunk(buffer);
|
|
47766
|
+
}
|
|
47767
|
+
} finally {
|
|
47768
|
+
ctx.res.end();
|
|
47769
|
+
}
|
|
47770
|
+
}
|
|
47771
|
+
async function proxy(ctx) {
|
|
47772
|
+
const profile = resolveProfile(ctx);
|
|
47773
|
+
const upstream = resolveUpstream(ctx);
|
|
47774
|
+
const upstreamPath = ctx.path.replace(/^\/api\/hermes\/v1/, "/v1").replace(/^\/api\/hermes/, "/api");
|
|
47775
|
+
const params = new URLSearchParams(ctx.search || "");
|
|
47776
|
+
params.delete("token");
|
|
47777
|
+
const search2 = params.toString();
|
|
47778
|
+
const url2 = `${upstream}${upstreamPath}${search2 ? `?${search2}` : ""}`;
|
|
47779
|
+
const headers = buildProxyHeaders(ctx, upstream);
|
|
47072
47780
|
try {
|
|
47073
47781
|
let body;
|
|
47074
47782
|
if (ctx.req.method !== "GET" && ctx.req.method !== "HEAD") {
|
|
47075
|
-
|
|
47783
|
+
const parsed = ctx.request.body;
|
|
47784
|
+
if (typeof parsed === "string") {
|
|
47785
|
+
body = parsed;
|
|
47786
|
+
} else if (parsed && typeof parsed === "object") {
|
|
47787
|
+
body = JSON.stringify(parsed);
|
|
47788
|
+
}
|
|
47076
47789
|
}
|
|
47077
|
-
const requestInit = {
|
|
47078
|
-
method: ctx.req.method,
|
|
47079
|
-
headers,
|
|
47080
|
-
body
|
|
47081
|
-
};
|
|
47790
|
+
const requestInit = { method: ctx.req.method, headers, body };
|
|
47082
47791
|
let res;
|
|
47083
47792
|
try {
|
|
47084
47793
|
res = await fetch(url2, requestInit);
|
|
@@ -47096,6 +47805,30 @@ async function proxy(ctx) {
|
|
|
47096
47805
|
}
|
|
47097
47806
|
});
|
|
47098
47807
|
ctx.status = res.status;
|
|
47808
|
+
if (ctx.req.method === "POST" && /\/v1\/runs$/.test(upstreamPath) && body) {
|
|
47809
|
+
try {
|
|
47810
|
+
const parsed = JSON.parse(body);
|
|
47811
|
+
if (parsed.session_id) {
|
|
47812
|
+
const resBody = await res.text();
|
|
47813
|
+
ctx.res.write(resBody);
|
|
47814
|
+
ctx.res.end();
|
|
47815
|
+
try {
|
|
47816
|
+
const result = JSON.parse(resBody);
|
|
47817
|
+
if (result.run_id) {
|
|
47818
|
+
setRunSession(result.run_id, parsed.session_id);
|
|
47819
|
+
}
|
|
47820
|
+
} catch {
|
|
47821
|
+
}
|
|
47822
|
+
return;
|
|
47823
|
+
}
|
|
47824
|
+
} catch {
|
|
47825
|
+
}
|
|
47826
|
+
}
|
|
47827
|
+
const sseMatch = upstreamPath.match(SSE_EVENTS_PATH);
|
|
47828
|
+
if (sseMatch) {
|
|
47829
|
+
await streamSSE(ctx, res);
|
|
47830
|
+
return;
|
|
47831
|
+
}
|
|
47099
47832
|
if (res.body) {
|
|
47100
47833
|
const reader = res.body.getReader();
|
|
47101
47834
|
const pump = async () => {
|
|
@@ -47136,7 +47869,9 @@ async function proxyMiddleware(ctx, next) {
|
|
|
47136
47869
|
function registerRoutes(app, requireAuth2) {
|
|
47137
47870
|
app.use(healthRoutes.routes());
|
|
47138
47871
|
app.use(webhookRoutes.routes());
|
|
47872
|
+
app.use(authPublicRoutes.routes());
|
|
47139
47873
|
app.use(requireAuth2);
|
|
47874
|
+
app.use(authProtectedRoutes.routes());
|
|
47140
47875
|
app.use(uploadRoutes.routes());
|
|
47141
47876
|
app.use(updateRoutes.routes());
|
|
47142
47877
|
app.use(sessionRoutes.routes());
|
|
@@ -47158,14 +47893,14 @@ function registerRoutes(app, requireAuth2) {
|
|
|
47158
47893
|
var import_cors = __toESM(require_cors());
|
|
47159
47894
|
var import_koa_static = __toESM(require_koa_static());
|
|
47160
47895
|
var import_koa_send = __toESM(require_koa_send());
|
|
47161
|
-
var
|
|
47162
|
-
var
|
|
47163
|
-
var
|
|
47164
|
-
var
|
|
47896
|
+
var import_os12 = __toESM(require("os"));
|
|
47897
|
+
var import_path17 = require("path");
|
|
47898
|
+
var import_promises14 = require("fs/promises");
|
|
47899
|
+
var import_fs15 = require("fs");
|
|
47165
47900
|
init_logger();
|
|
47166
|
-
var APP_VERSION = true ? "0.4.
|
|
47901
|
+
var APP_VERSION = true ? "0.4.3" : (() => {
|
|
47167
47902
|
try {
|
|
47168
|
-
return JSON.parse(
|
|
47903
|
+
return JSON.parse(readFileSync9((0, import_path17.resolve)(__dirname, "../../package.json"), "utf-8")).version;
|
|
47169
47904
|
} catch {
|
|
47170
47905
|
return "dev";
|
|
47171
47906
|
}
|
|
@@ -47180,12 +47915,15 @@ process.on("unhandledRejection", (reason) => {
|
|
|
47180
47915
|
var server = null;
|
|
47181
47916
|
async function bootstrap() {
|
|
47182
47917
|
console.log(`hermes-web-ui v${APP_VERSION} starting...`);
|
|
47183
|
-
await (0,
|
|
47184
|
-
await (0,
|
|
47918
|
+
await (0, import_promises14.mkdir)(config.uploadDir, { recursive: true });
|
|
47919
|
+
await (0, import_promises14.mkdir)(config.dataDir, { recursive: true });
|
|
47185
47920
|
const authToken = await getToken();
|
|
47186
47921
|
const app = new koa_default();
|
|
47187
47922
|
await initGatewayManager();
|
|
47188
47923
|
console.log("[bootstrap] gateway manager initialized");
|
|
47924
|
+
const { initUsageStore: initUsageStore2 } = await Promise.resolve().then(() => (init_usage_store(), usage_store_exports));
|
|
47925
|
+
initUsageStore2();
|
|
47926
|
+
console.log("[bootstrap] usage store initialized");
|
|
47189
47927
|
app.use((0, import_cors.default)({ origin: config.corsOrigins }));
|
|
47190
47928
|
app.use(bodyParserWrapper());
|
|
47191
47929
|
console.log("[bootstrap] cors + bodyParser registered");
|
|
@@ -47196,7 +47934,7 @@ async function bootstrap() {
|
|
|
47196
47934
|
console.log(`Auth enabled \u2014 token: ${authToken}`);
|
|
47197
47935
|
logger.info("Auth enabled \u2014 token: %s", authToken);
|
|
47198
47936
|
}
|
|
47199
|
-
const distDir = (0,
|
|
47937
|
+
const distDir = (0, import_path17.resolve)(__dirname, "..", "client");
|
|
47200
47938
|
app.use((0, import_koa_static.default)(distDir));
|
|
47201
47939
|
app.use(async (ctx) => {
|
|
47202
47940
|
if (!ctx.path.startsWith("/api") && ctx.path !== "/health" && ctx.path !== "/upload" && ctx.path !== "/webhook") {
|
|
@@ -47210,7 +47948,7 @@ async function bootstrap() {
|
|
|
47210
47948
|
setupTerminalWebSocket(server);
|
|
47211
47949
|
console.log("[bootstrap] terminal websocket setup");
|
|
47212
47950
|
server.on("listening", () => {
|
|
47213
|
-
const interfaces =
|
|
47951
|
+
const interfaces = import_os12.default.networkInterfaces();
|
|
47214
47952
|
const localIp = Object.values(interfaces).flat().find((i) => i?.family === "IPv4" && !i?.internal)?.address || "localhost";
|
|
47215
47953
|
console.log(`Server: http://localhost:${config.port} (LAN: http://${localIp}:${config.port})`);
|
|
47216
47954
|
console.log(`Upstream: ${config.upstream}`);
|