hermium 0.1.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +56 -0
  2. package/bin/hermium.mjs +185 -164
  3. package/dist/api.mjs +3513 -0
  4. package/dist/public/assets/css/index-Dfs9RUU9.css +1 -0
  5. package/dist/public/assets/css/styles-B8p6jk5Z.css +1 -0
  6. package/dist/public/assets/js/ChatInputBlock-Bw7AL70H.js +1 -0
  7. package/dist/public/assets/js/MarkdownMessage-8d7Y6VL-.js +1 -0
  8. package/dist/public/assets/js/base-ui-BvQbAt_1.js +1 -0
  9. package/dist/public/assets/js/chat._sessionId-BG6lVraH.js +1 -0
  10. package/dist/public/assets/js/chat.index-D2zdMPTT.js +1 -0
  11. package/dist/public/assets/js/index-C0AK45FU.js +60 -0
  12. package/dist/public/assets/js/index-Cx5En4FK.js +1 -0
  13. package/dist/public/assets/js/memory-CeSRdTkW.js +3 -0
  14. package/dist/public/assets/js/router-8uDKazL-.js +1 -0
  15. package/dist/public/assets/js/settings-Bc3Y5zXO.js +1 -0
  16. package/dist/public/assets/js/skills-DZv7sA_5.js +1 -0
  17. package/dist/public/assets/js/theme-CPkdkpaj.js +1 -0
  18. package/dist/public/assets/js/usage-DXQsT9_b.js +1 -0
  19. package/dist/public/assets/woff2/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  20. package/dist/public/assets/woff2/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  21. package/dist/public/assets/woff2/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  22. package/dist/public/assets/woff2/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  23. package/dist/public/assets/woff2/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  24. package/dist/public/favicon.ico +0 -0
  25. package/dist/public/logo.png +0 -0
  26. package/package.json +1 -1
  27. package/dist/public/assets/IconAlertCircle-BHkmI3j7.js +0 -1
  28. package/dist/public/assets/IconAlertTriangle-wCJudlVg.js +0 -1
  29. package/dist/public/assets/IconCheck-CFuEh_p7.js +0 -1
  30. package/dist/public/assets/IconLoader2-BIx3OuF9.js +0 -1
  31. package/dist/public/assets/IconRefresh-Dgm93w3T.js +0 -1
  32. package/dist/public/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
  33. package/dist/public/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
  34. package/dist/public/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
  35. package/dist/public/assets/index-Bbz3abmO.js +0 -14
  36. package/dist/public/assets/index-CWUaRwcE.js +0 -1
  37. package/dist/public/assets/index-CinLq3cd.js +0 -1
  38. package/dist/public/assets/index-CrQs9n6q.js +0 -29
  39. package/dist/public/assets/index-CtacpN3I.js +0 -1
  40. package/dist/public/assets/index-DY7aE-9s.js +0 -2
  41. package/dist/public/assets/index-DkYGodJj.js +0 -94
  42. package/dist/public/assets/index-DvDLadUx.js +0 -1
  43. package/dist/public/assets/index-U6RcWedt.js +0 -1
  44. package/dist/public/assets/index-_6iFZ0fh.js +0 -1
  45. package/dist/public/assets/index-enFS26SU.js +0 -1
  46. package/dist/public/assets/input-eNcwlDHp.js +0 -1
  47. package/dist/public/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  48. package/dist/public/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  49. package/dist/public/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  50. package/dist/public/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  51. package/dist/public/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  52. package/dist/public/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  53. package/dist/public/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  54. package/dist/public/assets/queries-iHRgZzw2.js +0 -1
  55. package/dist/public/assets/styles-KcflDlA_.css +0 -1
  56. package/dist/public/assets/switch-B1DcZLwL.js +0 -1
  57. package/dist/public/assets/syntax-highlighter-DWPF-A_h.js +0 -6
  58. package/dist/public/assets/textarea-Di_syYTS.js +0 -1
  59. package/dist/public/favicon.png +0 -0
  60. package/dist/public/nous-logo.png +0 -0
  61. package/dist/server/index.mjs +0 -244
  62. package/dist/web-server/__23tanstack-start-plugin-adapters-Cwee5PKy.mjs +0 -6
  63. package/dist/web-server/_chunks/ssr-renderer.mjs +0 -22
  64. package/dist/web-server/_libs/babel__runtime.mjs +0 -237
  65. package/dist/web-server/_libs/bail.mjs +0 -8
  66. package/dist/web-server/_libs/base-ui__react.mjs +0 -9554
  67. package/dist/web-server/_libs/base-ui__utils.mjs +0 -1101
  68. package/dist/web-server/_libs/ccount.mjs +0 -16
  69. package/dist/web-server/_libs/character-entities-legacy.mjs +0 -111
  70. package/dist/web-server/_libs/character-entities.mjs +0 -2130
  71. package/dist/web-server/_libs/character-reference-invalid.mjs +0 -33
  72. package/dist/web-server/_libs/class-variance-authority.mjs +0 -44
  73. package/dist/web-server/_libs/clsx.mjs +0 -16
  74. package/dist/web-server/_libs/comma-separated-tokens.mjs +0 -31
  75. package/dist/web-server/_libs/cookie-es.mjs +0 -44
  76. package/dist/web-server/_libs/croner.mjs +0 -1
  77. package/dist/web-server/_libs/crossws.mjs +0 -1
  78. package/dist/web-server/_libs/decode-named-character-reference+[...].mjs +0 -8
  79. package/dist/web-server/_libs/devlop.mjs +0 -8
  80. package/dist/web-server/_libs/escape-string-regexp.mjs +0 -9
  81. package/dist/web-server/_libs/estree-util-is-identifier-name.mjs +0 -11
  82. package/dist/web-server/_libs/extend.mjs +0 -97
  83. package/dist/web-server/_libs/fault.mjs +0 -1
  84. package/dist/web-server/_libs/floating-ui__core.mjs +0 -663
  85. package/dist/web-server/_libs/floating-ui__dom.mjs +0 -624
  86. package/dist/web-server/_libs/floating-ui__react-dom.mjs +0 -279
  87. package/dist/web-server/_libs/floating-ui__utils.mjs +0 -322
  88. package/dist/web-server/_libs/format.mjs +0 -1
  89. package/dist/web-server/_libs/h3.mjs +0 -408
  90. package/dist/web-server/_libs/hast-util-parse-selector.mjs +0 -39
  91. package/dist/web-server/_libs/hast-util-to-jsx-runtime.mjs +0 -388
  92. package/dist/web-server/_libs/hast-util-whitespace.mjs +0 -10
  93. package/dist/web-server/_libs/hastscript.mjs +0 -200
  94. package/dist/web-server/_libs/highlight.js.mjs +0 -1
  95. package/dist/web-server/_libs/hookable.mjs +0 -1
  96. package/dist/web-server/_libs/html-url-attributes.mjs +0 -26
  97. package/dist/web-server/_libs/inline-style-parser.mjs +0 -142
  98. package/dist/web-server/_libs/is-alphabetical.mjs +0 -7
  99. package/dist/web-server/_libs/is-alphanumerical.mjs +0 -8
  100. package/dist/web-server/_libs/is-decimal.mjs +0 -7
  101. package/dist/web-server/_libs/is-hexadecimal.mjs +0 -7
  102. package/dist/web-server/_libs/is-plain-obj.mjs +0 -10
  103. package/dist/web-server/_libs/isbot.mjs +0 -21
  104. package/dist/web-server/_libs/longest-streak.mjs +0 -25
  105. package/dist/web-server/_libs/lowlight.mjs +0 -1
  106. package/dist/web-server/_libs/markdown-table.mjs +0 -142
  107. package/dist/web-server/_libs/mdast-util-find-and-replace.mjs +0 -109
  108. package/dist/web-server/_libs/mdast-util-from-markdown.mjs +0 -717
  109. package/dist/web-server/_libs/mdast-util-gfm-autolink-literal+[...].mjs +0 -156
  110. package/dist/web-server/_libs/mdast-util-gfm-footnote.mjs +0 -117
  111. package/dist/web-server/_libs/mdast-util-gfm-strikethrough.mjs +0 -54
  112. package/dist/web-server/_libs/mdast-util-gfm-table.mjs +0 -157
  113. package/dist/web-server/_libs/mdast-util-gfm-task-list-item.mjs +0 -77
  114. package/dist/web-server/_libs/mdast-util-gfm.mjs +0 -29
  115. package/dist/web-server/_libs/mdast-util-phrasing.mjs +0 -30
  116. package/dist/web-server/_libs/mdast-util-to-hast.mjs +0 -710
  117. package/dist/web-server/_libs/mdast-util-to-markdown.mjs +0 -798
  118. package/dist/web-server/_libs/mdast-util-to-string.mjs +0 -38
  119. package/dist/web-server/_libs/micromark-core-commonmark.mjs +0 -2259
  120. package/dist/web-server/_libs/micromark-extension-gfm-autolink-literal+[...].mjs +0 -344
  121. package/dist/web-server/_libs/micromark-extension-gfm-footnote+[...].mjs +0 -279
  122. package/dist/web-server/_libs/micromark-extension-gfm-strikethrough+[...].mjs +0 -98
  123. package/dist/web-server/_libs/micromark-extension-gfm-table.mjs +0 -491
  124. package/dist/web-server/_libs/micromark-extension-gfm-tagfilter+[...].mjs +0 -1
  125. package/dist/web-server/_libs/micromark-extension-gfm-task-list-item+[...].mjs +0 -77
  126. package/dist/web-server/_libs/micromark-extension-gfm.mjs +0 -18
  127. package/dist/web-server/_libs/micromark-factory-destination.mjs +0 -94
  128. package/dist/web-server/_libs/micromark-factory-label.mjs +0 -63
  129. package/dist/web-server/_libs/micromark-factory-space.mjs +0 -24
  130. package/dist/web-server/_libs/micromark-factory-title.mjs +0 -65
  131. package/dist/web-server/_libs/micromark-factory-whitespace.mjs +0 -22
  132. package/dist/web-server/_libs/micromark-util-character.mjs +0 -44
  133. package/dist/web-server/_libs/micromark-util-chunked.mjs +0 -36
  134. package/dist/web-server/_libs/micromark-util-classify-character+[...].mjs +0 -12
  135. package/dist/web-server/_libs/micromark-util-combine-extensions+[...].mjs +0 -41
  136. package/dist/web-server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +0 -19
  137. package/dist/web-server/_libs/micromark-util-decode-string.mjs +0 -21
  138. package/dist/web-server/_libs/micromark-util-encode.mjs +0 -1
  139. package/dist/web-server/_libs/micromark-util-html-tag-name.mjs +0 -69
  140. package/dist/web-server/_libs/micromark-util-normalize-identifier+[...].mjs +0 -6
  141. package/dist/web-server/_libs/micromark-util-resolve-all.mjs +0 -15
  142. package/dist/web-server/_libs/micromark-util-sanitize-uri.mjs +0 -41
  143. package/dist/web-server/_libs/micromark-util-subtokenize.mjs +0 -346
  144. package/dist/web-server/_libs/micromark.mjs +0 -906
  145. package/dist/web-server/_libs/ocache.mjs +0 -1
  146. package/dist/web-server/_libs/ohash.mjs +0 -1
  147. package/dist/web-server/_libs/parse-entities.mjs +0 -245
  148. package/dist/web-server/_libs/property-information.mjs +0 -1210
  149. package/dist/web-server/_libs/react-dom.mjs +0 -10779
  150. package/dist/web-server/_libs/react-markdown.mjs +0 -147
  151. package/dist/web-server/_libs/react-syntax-highlighter.mjs +0 -941
  152. package/dist/web-server/_libs/react.mjs +0 -513
  153. package/dist/web-server/_libs/refractor.mjs +0 -2425
  154. package/dist/web-server/_libs/remark-gfm.mjs +0 -20
  155. package/dist/web-server/_libs/remark-parse.mjs +0 -19
  156. package/dist/web-server/_libs/remark-rehype.mjs +0 -21
  157. package/dist/web-server/_libs/reselect.mjs +0 -1
  158. package/dist/web-server/_libs/rou3.mjs +0 -8
  159. package/dist/web-server/_libs/seroval-plugins.mjs +0 -58
  160. package/dist/web-server/_libs/seroval.mjs +0 -1775
  161. package/dist/web-server/_libs/space-separated-tokens.mjs +0 -11
  162. package/dist/web-server/_libs/srvx.mjs +0 -781
  163. package/dist/web-server/_libs/style-to-js.mjs +0 -72
  164. package/dist/web-server/_libs/style-to-object.mjs +0 -38
  165. package/dist/web-server/_libs/tabler__icons-react.mjs +0 -230
  166. package/dist/web-server/_libs/tanstack__history.mjs +0 -204
  167. package/dist/web-server/_libs/tanstack__query-core.mjs +0 -2552
  168. package/dist/web-server/_libs/tanstack__react-query.mjs +0 -190
  169. package/dist/web-server/_libs/tanstack__react-router.mjs +0 -1120
  170. package/dist/web-server/_libs/tanstack__react-store.mjs +0 -2
  171. package/dist/web-server/_libs/tanstack__router-core.mjs +0 -4288
  172. package/dist/web-server/_libs/tanstack__store.mjs +0 -1
  173. package/dist/web-server/_libs/trim-lines.mjs +0 -41
  174. package/dist/web-server/_libs/trough.mjs +0 -85
  175. package/dist/web-server/_libs/ufo.mjs +0 -54
  176. package/dist/web-server/_libs/unctx.mjs +0 -1
  177. package/dist/web-server/_libs/ungap__structured-clone.mjs +0 -224
  178. package/dist/web-server/_libs/unified.mjs +0 -661
  179. package/dist/web-server/_libs/unist-util-is.mjs +0 -100
  180. package/dist/web-server/_libs/unist-util-position.mjs +0 -27
  181. package/dist/web-server/_libs/unist-util-stringify-position.mjs +0 -27
  182. package/dist/web-server/_libs/unist-util-visit-parents.mjs +0 -83
  183. package/dist/web-server/_libs/unist-util-visit.mjs +0 -24
  184. package/dist/web-server/_libs/unstorage.mjs +0 -1
  185. package/dist/web-server/_libs/use-sync-external-store.mjs +0 -139
  186. package/dist/web-server/_libs/vfile-message.mjs +0 -138
  187. package/dist/web-server/_libs/vfile.mjs +0 -467
  188. package/dist/web-server/_libs/zod.mjs +0 -3915
  189. package/dist/web-server/_libs/zustand.mjs +0 -343
  190. package/dist/web-server/_libs/zwitch.mjs +0 -1
  191. package/dist/web-server/_ssr/index-0n2Z3BPQ.mjs +0 -369
  192. package/dist/web-server/_ssr/index-6itDALOw.mjs +0 -339
  193. package/dist/web-server/_ssr/index-BIRTrOmp.mjs +0 -449
  194. package/dist/web-server/_ssr/index-BPzfADac.mjs +0 -66
  195. package/dist/web-server/_ssr/index-BQE3bF14.mjs +0 -1870
  196. package/dist/web-server/_ssr/index-C5HpvlUP.mjs +0 -190
  197. package/dist/web-server/_ssr/index-C_ZxnypN.mjs +0 -213
  198. package/dist/web-server/_ssr/index-Ca8JFH8f.mjs +0 -612
  199. package/dist/web-server/_ssr/index-DNVESZiA.mjs +0 -513
  200. package/dist/web-server/_ssr/index.mjs +0 -1558
  201. package/dist/web-server/_ssr/input-CqXjTRQg.mjs +0 -20
  202. package/dist/web-server/_ssr/queries-3H_19mUt.mjs +0 -16
  203. package/dist/web-server/_ssr/router-sbsNus0Y.mjs +0 -2093
  204. package/dist/web-server/_ssr/start-HYkvq4Ni.mjs +0 -4
  205. package/dist/web-server/_ssr/switch-usf2F1UM.mjs +0 -33
  206. package/dist/web-server/_ssr/syntax-highlighter-5vezNTce.mjs +0 -62
  207. package/dist/web-server/_ssr/textarea-DfRheWY0.mjs +0 -18
  208. package/dist/web-server/_tanstack-start-manifest_v-DqW-pKEH.mjs +0 -4
  209. package/dist/web-server/index.mjs +0 -597
package/dist/api.mjs ADDED
@@ -0,0 +1,3513 @@
1
+ // @bun
2
+ // src/index.ts
3
+ import { mkdir as mkdir4 } from "fs/promises";
4
+
5
+ // src/config.ts
6
+ import { join, resolve } from "path";
7
+ import { homedir } from "os";
8
+ function getWebUiHome() {
9
+ const envHome = process.env.HERMES_WEB_UI_HOME?.trim() || process.env.HERMES_WEBUI_STATE_DIR?.trim();
10
+ return envHome ? resolve(envHome) : join(homedir(), ".hermium-web-ui");
11
+ }
12
+ var appHome = getWebUiHome();
13
+ var config = {
14
+ port: parseInt(process.env.PORT || "4000", 10),
15
+ host: process.env.BIND_HOST?.trim() || "0.0.0.0",
16
+ appHome,
17
+ uploadDir: process.env.UPLOAD_DIR || join(appHome, "upload"),
18
+ dataDir: join(appHome, "data"),
19
+ dbPath: join(appHome, "data", "web-ui.db"),
20
+ corsOrigins: process.env.CORS_ORIGINS || "*",
21
+ authDisabled: process.env.AUTH_DISABLED === "1" || process.env.AUTH_DISABLED === "true",
22
+ authToken: process.env.AUTH_TOKEN || undefined,
23
+ profile: process.env.PROFILE || "default",
24
+ logLevel: process.env.LOG_LEVEL || "info",
25
+ maxDownloadSize: process.env.MAX_DOWNLOAD_SIZE || "200MB",
26
+ maxEditSize: process.env.MAX_EDIT_SIZE || "10MB",
27
+ workspaceBase: process.env.WORKSPACE_BASE || "/opt/data/workspace",
28
+ gatewayHost: process.env.GATEWAY_HOST || "127.0.0.1",
29
+ stopGatewaysOnShutdown: process.env.HERMES_WEB_UI_STOP_GATEWAYS_ON_SHUTDOWN
30
+ };
31
+
32
+ // src/services/auth.ts
33
+ import { mkdir, readFile, writeFile } from "fs/promises";
34
+ import { join as join2 } from "path";
35
+ import { randomBytes } from "crypto";
36
+ var TOKEN_FILE = join2(config.appHome, ".token");
37
+ async function getToken() {
38
+ if (config.authToken)
39
+ return config.authToken;
40
+ try {
41
+ const buf = await readFile(TOKEN_FILE, "utf-8");
42
+ return buf.trim();
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+ async function ensureToken() {
48
+ if (config.authToken)
49
+ return config.authToken;
50
+ const existing = await getToken();
51
+ if (existing)
52
+ return existing;
53
+ const token = randomBytes(32).toString("hex");
54
+ await mkdir(config.appHome, { recursive: true });
55
+ await writeFile(TOKEN_FILE, token + `
56
+ `, { mode: 384 });
57
+ return token;
58
+ }
59
+
60
+ // src/lib/logger.ts
61
+ var LEVELS = {
62
+ trace: 0,
63
+ debug: 1,
64
+ info: 2,
65
+ warn: 3,
66
+ error: 4,
67
+ fatal: 5
68
+ };
69
+ var currentLevel = LEVELS[process.env.LOG_LEVEL || "info"];
70
+ function log(level, message, meta) {
71
+ if (LEVELS[level] < currentLevel)
72
+ return;
73
+ const ts = new Date().toISOString();
74
+ const payload = meta ? ` ${JSON.stringify(meta)}` : "";
75
+ const output = `[${ts}] [${level.toUpperCase()}] ${message}${payload}`;
76
+ if (level === "error" || level === "fatal") {
77
+ console.error(output);
78
+ } else {
79
+ console.log(output);
80
+ }
81
+ }
82
+ var logger = {
83
+ trace: (msg, meta) => log("trace", msg, meta),
84
+ debug: (msg, meta) => log("debug", msg, meta),
85
+ info: (msg, meta) => log("info", msg, meta),
86
+ warn: (msg, meta) => log("warn", msg, meta),
87
+ error: (msg, meta) => log("error", msg, meta),
88
+ fatal: (msg, meta) => log("fatal", msg, meta)
89
+ };
90
+
91
+ // src/services/gateway.ts
92
+ class GatewayManager {
93
+ upstreamMap = new Map;
94
+ apiKeyMap = new Map;
95
+ getUpstream(profile) {
96
+ const p = profile || "default";
97
+ const cached = this.upstreamMap.get(p);
98
+ if (cached)
99
+ return cached;
100
+ const url = `http://${config.gatewayHost}:8642`;
101
+ this.upstreamMap.set(p, url);
102
+ return url;
103
+ }
104
+ getApiKey(profile) {
105
+ const p = profile || "default";
106
+ return this.apiKeyMap.get(p);
107
+ }
108
+ setUpstream(profile, url) {
109
+ this.upstreamMap.set(profile, url);
110
+ logger.info(`Gateway upstream set for profile ${profile}: ${url}`);
111
+ }
112
+ setApiKey(profile, key) {
113
+ this.apiKeyMap.set(profile, key);
114
+ }
115
+ }
116
+ var instance = null;
117
+ function initGatewayManager() {
118
+ if (!instance) {
119
+ instance = new GatewayManager;
120
+ }
121
+ return instance;
122
+ }
123
+ function getGatewayManagerInstance() {
124
+ if (!instance) {
125
+ throw new Error("GatewayManager not initialized");
126
+ }
127
+ return instance;
128
+ }
129
+
130
+ // src/db/index.ts
131
+ import { Database } from "bun:sqlite";
132
+ var dbInstance = null;
133
+ function getDb() {
134
+ if (!dbInstance) {
135
+ dbInstance = new Database(config.dbPath, { create: true });
136
+ dbInstance.exec("PRAGMA journal_mode = WAL");
137
+ logger.info(`SQLite opened at ${config.dbPath}`);
138
+ }
139
+ return dbInstance;
140
+ }
141
+ function closeDb() {
142
+ if (dbInstance) {
143
+ dbInstance.close();
144
+ dbInstance = null;
145
+ logger.info("SQLite closed");
146
+ }
147
+ }
148
+
149
+ // src/db/schemas.ts
150
+ function initAllHermesTables() {
151
+ const db = getDb();
152
+ db.exec(`
153
+ CREATE TABLE IF NOT EXISTS sessions (
154
+ id TEXT PRIMARY KEY,
155
+ title TEXT NOT NULL,
156
+ source TEXT,
157
+ created_at INTEGER NOT NULL,
158
+ updated_at INTEGER NOT NULL,
159
+ model TEXT,
160
+ provider TEXT,
161
+ message_count INTEGER DEFAULT 0,
162
+ input_tokens INTEGER DEFAULT 0,
163
+ output_tokens INTEGER DEFAULT 0,
164
+ ended_at INTEGER,
165
+ last_active_at INTEGER,
166
+ workspace TEXT
167
+ )
168
+ `);
169
+ db.exec(`
170
+ CREATE TABLE IF NOT EXISTS messages (
171
+ id TEXT PRIMARY KEY,
172
+ session_id TEXT NOT NULL,
173
+ role TEXT NOT NULL,
174
+ content TEXT NOT NULL,
175
+ timestamp INTEGER NOT NULL,
176
+ tool_name TEXT,
177
+ tool_call_id TEXT,
178
+ tool_args TEXT,
179
+ tool_result TEXT,
180
+ tool_status TEXT,
181
+ tool_duration REAL,
182
+ is_streaming INTEGER DEFAULT 0,
183
+ reasoning TEXT,
184
+ reasoning_started_at INTEGER,
185
+ reasoning_ended_at INTEGER,
186
+ tool_calls TEXT,
187
+ queued INTEGER DEFAULT 0
188
+ )
189
+ `);
190
+ db.exec(`
191
+ CREATE TABLE IF NOT EXISTS usage (
192
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
193
+ session_id TEXT NOT NULL,
194
+ input_tokens INTEGER DEFAULT 0,
195
+ output_tokens INTEGER DEFAULT 0,
196
+ cache_read_tokens INTEGER DEFAULT 0,
197
+ cache_write_tokens INTEGER DEFAULT 0,
198
+ reasoning_tokens INTEGER DEFAULT 0,
199
+ model TEXT,
200
+ profile TEXT,
201
+ timestamp INTEGER NOT NULL
202
+ )
203
+ `);
204
+ db.exec(`
205
+ CREATE TABLE IF NOT EXISTS jobs (
206
+ id TEXT PRIMARY KEY,
207
+ name TEXT NOT NULL,
208
+ cron TEXT NOT NULL,
209
+ command TEXT NOT NULL,
210
+ enabled INTEGER DEFAULT 1,
211
+ profile TEXT NOT NULL,
212
+ created_at INTEGER NOT NULL,
213
+ updated_at INTEGER NOT NULL
214
+ )
215
+ `);
216
+ db.exec(`
217
+ CREATE TABLE IF NOT EXISTS job_runs (
218
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
219
+ job_id TEXT NOT NULL,
220
+ status TEXT NOT NULL,
221
+ output TEXT,
222
+ error TEXT,
223
+ started_at INTEGER NOT NULL,
224
+ ended_at INTEGER
225
+ )
226
+ `);
227
+ db.exec(`
228
+ CREATE TABLE IF NOT EXISTS gc_rooms (
229
+ id TEXT PRIMARY KEY,
230
+ name TEXT NOT NULL,
231
+ invite_code TEXT,
232
+ created_at INTEGER NOT NULL,
233
+ updated_at INTEGER NOT NULL
234
+ )
235
+ `);
236
+ db.exec(`
237
+ CREATE TABLE IF NOT EXISTS gc_messages (
238
+ id TEXT PRIMARY KEY,
239
+ room_id TEXT NOT NULL,
240
+ sender_id TEXT NOT NULL,
241
+ sender_name TEXT NOT NULL,
242
+ content TEXT NOT NULL,
243
+ timestamp INTEGER NOT NULL
244
+ )
245
+ `);
246
+ db.exec(`
247
+ CREATE TABLE IF NOT EXISTS gc_room_agents (
248
+ id TEXT PRIMARY KEY,
249
+ room_id TEXT NOT NULL,
250
+ agent_id TEXT NOT NULL,
251
+ profile TEXT NOT NULL,
252
+ name TEXT NOT NULL,
253
+ description TEXT,
254
+ invited INTEGER DEFAULT 0
255
+ )
256
+ `);
257
+ db.exec(`
258
+ CREATE TABLE IF NOT EXISTS gc_room_members (
259
+ id TEXT PRIMARY KEY,
260
+ room_id TEXT NOT NULL,
261
+ user_id TEXT NOT NULL,
262
+ name TEXT,
263
+ joined_at INTEGER NOT NULL,
264
+ online INTEGER DEFAULT 0,
265
+ socket_id TEXT
266
+ )
267
+ `);
268
+ try {
269
+ db.exec("ALTER TABLE messages ADD COLUMN reasoning_started_at INTEGER");
270
+ } catch {}
271
+ try {
272
+ db.exec("ALTER TABLE messages ADD COLUMN reasoning_ended_at INTEGER");
273
+ } catch {}
274
+ try {
275
+ db.exec("ALTER TABLE messages ADD COLUMN tool_calls TEXT");
276
+ } catch {}
277
+ try {
278
+ db.exec("ALTER TABLE messages ADD COLUMN attachments TEXT");
279
+ } catch {}
280
+ db.exec("CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id, timestamp)");
281
+ db.exec("CREATE INDEX IF NOT EXISTS idx_usage_session ON usage(session_id, timestamp)");
282
+ db.exec("CREATE INDEX IF NOT EXISTS idx_gc_messages_room ON gc_messages(room_id, timestamp)");
283
+ db.exec("CREATE INDEX IF NOT EXISTS idx_gc_room_agents_room ON gc_room_agents(room_id)");
284
+ }
285
+
286
+ // src/db/init.ts
287
+ function initAllStores() {
288
+ initAllHermesTables();
289
+ }
290
+
291
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/compose.js
292
+ var compose = (middleware, onError, onNotFound) => {
293
+ return (context, next) => {
294
+ let index = -1;
295
+ return dispatch(0);
296
+ async function dispatch(i) {
297
+ if (i <= index) {
298
+ throw new Error("next() called multiple times");
299
+ }
300
+ index = i;
301
+ let res;
302
+ let isError = false;
303
+ let handler;
304
+ if (middleware[i]) {
305
+ handler = middleware[i][0][0];
306
+ context.req.routeIndex = i;
307
+ } else {
308
+ handler = i === middleware.length && next || undefined;
309
+ }
310
+ if (handler) {
311
+ try {
312
+ res = await handler(context, () => dispatch(i + 1));
313
+ } catch (err) {
314
+ if (err instanceof Error && onError) {
315
+ context.error = err;
316
+ res = await onError(err, context);
317
+ isError = true;
318
+ } else {
319
+ throw err;
320
+ }
321
+ }
322
+ } else {
323
+ if (context.finalized === false && onNotFound) {
324
+ res = await onNotFound(context);
325
+ }
326
+ }
327
+ if (res && (context.finalized === false || isError)) {
328
+ context.res = res;
329
+ }
330
+ return context;
331
+ }
332
+ };
333
+ };
334
+
335
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/http-exception.js
336
+ var HTTPException = class extends Error {
337
+ res;
338
+ status;
339
+ constructor(status = 500, options) {
340
+ super(options?.message, { cause: options?.cause });
341
+ this.res = options?.res;
342
+ this.status = status;
343
+ }
344
+ getResponse() {
345
+ if (this.res) {
346
+ const newResponse = new Response(this.res.body, {
347
+ status: this.status,
348
+ headers: this.res.headers
349
+ });
350
+ return newResponse;
351
+ }
352
+ return new Response(this.message, {
353
+ status: this.status
354
+ });
355
+ }
356
+ };
357
+
358
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/request/constants.js
359
+ var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
360
+
361
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/utils/body.js
362
+ var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
363
+ const { all = false, dot = false } = options;
364
+ const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;
365
+ const contentType = headers.get("Content-Type");
366
+ if (contentType?.startsWith("multipart/form-data") || contentType?.startsWith("application/x-www-form-urlencoded")) {
367
+ return parseFormData(request, { all, dot });
368
+ }
369
+ return {};
370
+ };
371
+ async function parseFormData(request, options) {
372
+ const formData = await request.formData();
373
+ if (formData) {
374
+ return convertFormDataToBodyData(formData, options);
375
+ }
376
+ return {};
377
+ }
378
+ function convertFormDataToBodyData(formData, options) {
379
+ const form = /* @__PURE__ */ Object.create(null);
380
+ formData.forEach((value, key) => {
381
+ const shouldParseAllValues = options.all || key.endsWith("[]");
382
+ if (!shouldParseAllValues) {
383
+ form[key] = value;
384
+ } else {
385
+ handleParsingAllValues(form, key, value);
386
+ }
387
+ });
388
+ if (options.dot) {
389
+ Object.entries(form).forEach(([key, value]) => {
390
+ const shouldParseDotValues = key.includes(".");
391
+ if (shouldParseDotValues) {
392
+ handleParsingNestedValues(form, key, value);
393
+ delete form[key];
394
+ }
395
+ });
396
+ }
397
+ return form;
398
+ }
399
+ var handleParsingAllValues = (form, key, value) => {
400
+ if (form[key] !== undefined) {
401
+ if (Array.isArray(form[key])) {
402
+ form[key].push(value);
403
+ } else {
404
+ form[key] = [form[key], value];
405
+ }
406
+ } else {
407
+ if (!key.endsWith("[]")) {
408
+ form[key] = value;
409
+ } else {
410
+ form[key] = [value];
411
+ }
412
+ }
413
+ };
414
+ var handleParsingNestedValues = (form, key, value) => {
415
+ if (/(?:^|\.)__proto__\./.test(key)) {
416
+ return;
417
+ }
418
+ let nestedForm = form;
419
+ const keys = key.split(".");
420
+ keys.forEach((key2, index) => {
421
+ if (index === keys.length - 1) {
422
+ nestedForm[key2] = value;
423
+ } else {
424
+ if (!nestedForm[key2] || typeof nestedForm[key2] !== "object" || Array.isArray(nestedForm[key2]) || nestedForm[key2] instanceof File) {
425
+ nestedForm[key2] = /* @__PURE__ */ Object.create(null);
426
+ }
427
+ nestedForm = nestedForm[key2];
428
+ }
429
+ });
430
+ };
431
+
432
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/utils/url.js
433
+ var splitPath = (path) => {
434
+ const paths = path.split("/");
435
+ if (paths[0] === "") {
436
+ paths.shift();
437
+ }
438
+ return paths;
439
+ };
440
+ var splitRoutingPath = (routePath) => {
441
+ const { groups, path } = extractGroupsFromPath(routePath);
442
+ const paths = splitPath(path);
443
+ return replaceGroupMarks(paths, groups);
444
+ };
445
+ var extractGroupsFromPath = (path) => {
446
+ const groups = [];
447
+ path = path.replace(/\{[^}]+\}/g, (match, index) => {
448
+ const mark = `@${index}`;
449
+ groups.push([mark, match]);
450
+ return mark;
451
+ });
452
+ return { groups, path };
453
+ };
454
+ var replaceGroupMarks = (paths, groups) => {
455
+ for (let i = groups.length - 1;i >= 0; i--) {
456
+ const [mark] = groups[i];
457
+ for (let j = paths.length - 1;j >= 0; j--) {
458
+ if (paths[j].includes(mark)) {
459
+ paths[j] = paths[j].replace(mark, groups[i][1]);
460
+ break;
461
+ }
462
+ }
463
+ }
464
+ return paths;
465
+ };
466
+ var patternCache = {};
467
+ var getPattern = (label, next) => {
468
+ if (label === "*") {
469
+ return "*";
470
+ }
471
+ const match = label.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
472
+ if (match) {
473
+ const cacheKey = `${label}#${next}`;
474
+ if (!patternCache[cacheKey]) {
475
+ if (match[2]) {
476
+ patternCache[cacheKey] = next && next[0] !== ":" && next[0] !== "*" ? [cacheKey, match[1], new RegExp(`^${match[2]}(?=/${next})`)] : [label, match[1], new RegExp(`^${match[2]}$`)];
477
+ } else {
478
+ patternCache[cacheKey] = [label, match[1], true];
479
+ }
480
+ }
481
+ return patternCache[cacheKey];
482
+ }
483
+ return null;
484
+ };
485
+ var tryDecode = (str, decoder) => {
486
+ try {
487
+ return decoder(str);
488
+ } catch {
489
+ return str.replace(/(?:%[0-9A-Fa-f]{2})+/g, (match) => {
490
+ try {
491
+ return decoder(match);
492
+ } catch {
493
+ return match;
494
+ }
495
+ });
496
+ }
497
+ };
498
+ var tryDecodeURI = (str) => tryDecode(str, decodeURI);
499
+ var getPath = (request) => {
500
+ const url = request.url;
501
+ const start = url.indexOf("/", url.indexOf(":") + 4);
502
+ let i = start;
503
+ for (;i < url.length; i++) {
504
+ const charCode = url.charCodeAt(i);
505
+ if (charCode === 37) {
506
+ const queryIndex = url.indexOf("?", i);
507
+ const hashIndex = url.indexOf("#", i);
508
+ const end = queryIndex === -1 ? hashIndex === -1 ? undefined : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex);
509
+ const path = url.slice(start, end);
510
+ return tryDecodeURI(path.includes("%25") ? path.replace(/%25/g, "%2525") : path);
511
+ } else if (charCode === 63 || charCode === 35) {
512
+ break;
513
+ }
514
+ }
515
+ return url.slice(start, i);
516
+ };
517
+ var getPathNoStrict = (request) => {
518
+ const result = getPath(request);
519
+ return result.length > 1 && result.at(-1) === "/" ? result.slice(0, -1) : result;
520
+ };
521
+ var mergePath = (base, sub, ...rest) => {
522
+ if (rest.length) {
523
+ sub = mergePath(sub, ...rest);
524
+ }
525
+ return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`;
526
+ };
527
+ var checkOptionalParameter = (path) => {
528
+ if (path.charCodeAt(path.length - 1) !== 63 || !path.includes(":")) {
529
+ return null;
530
+ }
531
+ const segments = path.split("/");
532
+ const results = [];
533
+ let basePath = "";
534
+ segments.forEach((segment) => {
535
+ if (segment !== "" && !/\:/.test(segment)) {
536
+ basePath += "/" + segment;
537
+ } else if (/\:/.test(segment)) {
538
+ if (/\?/.test(segment)) {
539
+ if (results.length === 0 && basePath === "") {
540
+ results.push("/");
541
+ } else {
542
+ results.push(basePath);
543
+ }
544
+ const optionalSegment = segment.replace("?", "");
545
+ basePath += "/" + optionalSegment;
546
+ results.push(basePath);
547
+ } else {
548
+ basePath += "/" + segment;
549
+ }
550
+ }
551
+ });
552
+ return results.filter((v, i, a) => a.indexOf(v) === i);
553
+ };
554
+ var _decodeURI = (value) => {
555
+ if (!/[%+]/.test(value)) {
556
+ return value;
557
+ }
558
+ if (value.indexOf("+") !== -1) {
559
+ value = value.replace(/\+/g, " ");
560
+ }
561
+ return value.indexOf("%") !== -1 ? tryDecode(value, decodeURIComponent_) : value;
562
+ };
563
+ var _getQueryParam = (url, key, multiple) => {
564
+ let encoded;
565
+ if (!multiple && key && !/[%+]/.test(key)) {
566
+ let keyIndex2 = url.indexOf("?", 8);
567
+ if (keyIndex2 === -1) {
568
+ return;
569
+ }
570
+ if (!url.startsWith(key, keyIndex2 + 1)) {
571
+ keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1);
572
+ }
573
+ while (keyIndex2 !== -1) {
574
+ const trailingKeyCode = url.charCodeAt(keyIndex2 + key.length + 1);
575
+ if (trailingKeyCode === 61) {
576
+ const valueIndex = keyIndex2 + key.length + 2;
577
+ const endIndex = url.indexOf("&", valueIndex);
578
+ return _decodeURI(url.slice(valueIndex, endIndex === -1 ? undefined : endIndex));
579
+ } else if (trailingKeyCode == 38 || isNaN(trailingKeyCode)) {
580
+ return "";
581
+ }
582
+ keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1);
583
+ }
584
+ encoded = /[%+]/.test(url);
585
+ if (!encoded) {
586
+ return;
587
+ }
588
+ }
589
+ const results = {};
590
+ encoded ??= /[%+]/.test(url);
591
+ let keyIndex = url.indexOf("?", 8);
592
+ while (keyIndex !== -1) {
593
+ const nextKeyIndex = url.indexOf("&", keyIndex + 1);
594
+ let valueIndex = url.indexOf("=", keyIndex);
595
+ if (valueIndex > nextKeyIndex && nextKeyIndex !== -1) {
596
+ valueIndex = -1;
597
+ }
598
+ let name = url.slice(keyIndex + 1, valueIndex === -1 ? nextKeyIndex === -1 ? undefined : nextKeyIndex : valueIndex);
599
+ if (encoded) {
600
+ name = _decodeURI(name);
601
+ }
602
+ keyIndex = nextKeyIndex;
603
+ if (name === "") {
604
+ continue;
605
+ }
606
+ let value;
607
+ if (valueIndex === -1) {
608
+ value = "";
609
+ } else {
610
+ value = url.slice(valueIndex + 1, nextKeyIndex === -1 ? undefined : nextKeyIndex);
611
+ if (encoded) {
612
+ value = _decodeURI(value);
613
+ }
614
+ }
615
+ if (multiple) {
616
+ if (!(results[name] && Array.isArray(results[name]))) {
617
+ results[name] = [];
618
+ }
619
+ results[name].push(value);
620
+ } else {
621
+ results[name] ??= value;
622
+ }
623
+ }
624
+ return key ? results[key] : results;
625
+ };
626
+ var getQueryParam = _getQueryParam;
627
+ var getQueryParams = (url, key) => {
628
+ return _getQueryParam(url, key, true);
629
+ };
630
+ var decodeURIComponent_ = decodeURIComponent;
631
+
632
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/request.js
633
+ var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
634
+ var HonoRequest = class {
635
+ raw;
636
+ #validatedData;
637
+ #matchResult;
638
+ routeIndex = 0;
639
+ path;
640
+ bodyCache = {};
641
+ constructor(request, path = "/", matchResult = [[]]) {
642
+ this.raw = request;
643
+ this.path = path;
644
+ this.#matchResult = matchResult;
645
+ this.#validatedData = {};
646
+ }
647
+ param(key) {
648
+ return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
649
+ }
650
+ #getDecodedParam(key) {
651
+ const paramKey = this.#matchResult[0][this.routeIndex][1][key];
652
+ const param = this.#getParamValue(paramKey);
653
+ return param && /\%/.test(param) ? tryDecodeURIComponent(param) : param;
654
+ }
655
+ #getAllDecodedParams() {
656
+ const decoded = {};
657
+ const keys = Object.keys(this.#matchResult[0][this.routeIndex][1]);
658
+ for (const key of keys) {
659
+ const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
660
+ if (value !== undefined) {
661
+ decoded[key] = /\%/.test(value) ? tryDecodeURIComponent(value) : value;
662
+ }
663
+ }
664
+ return decoded;
665
+ }
666
+ #getParamValue(paramKey) {
667
+ return this.#matchResult[1] ? this.#matchResult[1][paramKey] : paramKey;
668
+ }
669
+ query(key) {
670
+ return getQueryParam(this.url, key);
671
+ }
672
+ queries(key) {
673
+ return getQueryParams(this.url, key);
674
+ }
675
+ header(name) {
676
+ if (name) {
677
+ return this.raw.headers.get(name) ?? undefined;
678
+ }
679
+ const headerData = {};
680
+ this.raw.headers.forEach((value, key) => {
681
+ headerData[key] = value;
682
+ });
683
+ return headerData;
684
+ }
685
+ async parseBody(options) {
686
+ return parseBody(this, options);
687
+ }
688
+ #cachedBody = (key) => {
689
+ const { bodyCache, raw } = this;
690
+ const cachedBody = bodyCache[key];
691
+ if (cachedBody) {
692
+ return cachedBody;
693
+ }
694
+ const anyCachedKey = Object.keys(bodyCache)[0];
695
+ if (anyCachedKey) {
696
+ return bodyCache[anyCachedKey].then((body) => {
697
+ if (anyCachedKey === "json") {
698
+ body = JSON.stringify(body);
699
+ }
700
+ return new Response(body)[key]();
701
+ });
702
+ }
703
+ return bodyCache[key] = raw[key]();
704
+ };
705
+ json() {
706
+ return this.#cachedBody("text").then((text) => JSON.parse(text));
707
+ }
708
+ text() {
709
+ return this.#cachedBody("text");
710
+ }
711
+ arrayBuffer() {
712
+ return this.#cachedBody("arrayBuffer");
713
+ }
714
+ bytes() {
715
+ return this.#cachedBody("arrayBuffer").then((buffer) => new Uint8Array(buffer));
716
+ }
717
+ blob() {
718
+ return this.#cachedBody("blob");
719
+ }
720
+ formData() {
721
+ return this.#cachedBody("formData");
722
+ }
723
+ addValidatedData(target, data) {
724
+ this.#validatedData[target] = data;
725
+ }
726
+ valid(target) {
727
+ return this.#validatedData[target];
728
+ }
729
+ get url() {
730
+ return this.raw.url;
731
+ }
732
+ get method() {
733
+ return this.raw.method;
734
+ }
735
+ get [GET_MATCH_RESULT]() {
736
+ return this.#matchResult;
737
+ }
738
+ get matchedRoutes() {
739
+ return this.#matchResult[0].map(([[, route]]) => route);
740
+ }
741
+ get routePath() {
742
+ return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path;
743
+ }
744
+ };
745
+
746
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/utils/html.js
747
+ var HtmlEscapedCallbackPhase = {
748
+ Stringify: 1,
749
+ BeforeStream: 2,
750
+ Stream: 3
751
+ };
752
+ var raw = (value, callbacks) => {
753
+ const escapedString = new String(value);
754
+ escapedString.isEscaped = true;
755
+ escapedString.callbacks = callbacks;
756
+ return escapedString;
757
+ };
758
+ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
759
+ if (typeof str === "object" && !(str instanceof String)) {
760
+ if (!(str instanceof Promise)) {
761
+ str = str.toString();
762
+ }
763
+ if (str instanceof Promise) {
764
+ str = await str;
765
+ }
766
+ }
767
+ const callbacks = str.callbacks;
768
+ if (!callbacks?.length) {
769
+ return Promise.resolve(str);
770
+ }
771
+ if (buffer) {
772
+ buffer[0] += str;
773
+ } else {
774
+ buffer = [str];
775
+ }
776
+ const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context }))).then((res) => Promise.all(res.filter(Boolean).map((str2) => resolveCallback(str2, phase, false, context, buffer))).then(() => buffer[0]));
777
+ if (preserveCallbacks) {
778
+ return raw(await resStr, callbacks);
779
+ } else {
780
+ return resStr;
781
+ }
782
+ };
783
+
784
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/context.js
785
+ var TEXT_PLAIN = "text/plain; charset=UTF-8";
786
+ var setDefaultContentType = (contentType, headers) => {
787
+ return {
788
+ "Content-Type": contentType,
789
+ ...headers
790
+ };
791
+ };
792
+ var createResponseInstance = (body, init) => new Response(body, init);
793
+ var Context = class {
794
+ #rawRequest;
795
+ #req;
796
+ env = {};
797
+ #var;
798
+ finalized = false;
799
+ error;
800
+ #status;
801
+ #executionCtx;
802
+ #res;
803
+ #layout;
804
+ #renderer;
805
+ #notFoundHandler;
806
+ #preparedHeaders;
807
+ #matchResult;
808
+ #path;
809
+ constructor(req, options) {
810
+ this.#rawRequest = req;
811
+ if (options) {
812
+ this.#executionCtx = options.executionCtx;
813
+ this.env = options.env;
814
+ this.#notFoundHandler = options.notFoundHandler;
815
+ this.#path = options.path;
816
+ this.#matchResult = options.matchResult;
817
+ }
818
+ }
819
+ get req() {
820
+ this.#req ??= new HonoRequest(this.#rawRequest, this.#path, this.#matchResult);
821
+ return this.#req;
822
+ }
823
+ get event() {
824
+ if (this.#executionCtx && "respondWith" in this.#executionCtx) {
825
+ return this.#executionCtx;
826
+ } else {
827
+ throw Error("This context has no FetchEvent");
828
+ }
829
+ }
830
+ get executionCtx() {
831
+ if (this.#executionCtx) {
832
+ return this.#executionCtx;
833
+ } else {
834
+ throw Error("This context has no ExecutionContext");
835
+ }
836
+ }
837
+ get res() {
838
+ return this.#res ||= createResponseInstance(null, {
839
+ headers: this.#preparedHeaders ??= new Headers
840
+ });
841
+ }
842
+ set res(_res) {
843
+ if (this.#res && _res) {
844
+ _res = createResponseInstance(_res.body, _res);
845
+ for (const [k, v] of this.#res.headers.entries()) {
846
+ if (k === "content-type") {
847
+ continue;
848
+ }
849
+ if (k === "set-cookie") {
850
+ const cookies = this.#res.headers.getSetCookie();
851
+ _res.headers.delete("set-cookie");
852
+ for (const cookie of cookies) {
853
+ _res.headers.append("set-cookie", cookie);
854
+ }
855
+ } else {
856
+ _res.headers.set(k, v);
857
+ }
858
+ }
859
+ }
860
+ this.#res = _res;
861
+ this.finalized = true;
862
+ }
863
+ render = (...args) => {
864
+ this.#renderer ??= (content) => this.html(content);
865
+ return this.#renderer(...args);
866
+ };
867
+ setLayout = (layout) => this.#layout = layout;
868
+ getLayout = () => this.#layout;
869
+ setRenderer = (renderer) => {
870
+ this.#renderer = renderer;
871
+ };
872
+ header = (name, value, options) => {
873
+ if (this.finalized) {
874
+ this.#res = createResponseInstance(this.#res.body, this.#res);
875
+ }
876
+ const headers = this.#res ? this.#res.headers : this.#preparedHeaders ??= new Headers;
877
+ if (value === undefined) {
878
+ headers.delete(name);
879
+ } else if (options?.append) {
880
+ headers.append(name, value);
881
+ } else {
882
+ headers.set(name, value);
883
+ }
884
+ };
885
+ status = (status) => {
886
+ this.#status = status;
887
+ };
888
+ set = (key, value) => {
889
+ this.#var ??= /* @__PURE__ */ new Map;
890
+ this.#var.set(key, value);
891
+ };
892
+ get = (key) => {
893
+ return this.#var ? this.#var.get(key) : undefined;
894
+ };
895
+ get var() {
896
+ if (!this.#var) {
897
+ return {};
898
+ }
899
+ return Object.fromEntries(this.#var);
900
+ }
901
+ #newResponse(data, arg, headers) {
902
+ const responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders ?? new Headers;
903
+ if (typeof arg === "object" && "headers" in arg) {
904
+ const argHeaders = arg.headers instanceof Headers ? arg.headers : new Headers(arg.headers);
905
+ for (const [key, value] of argHeaders) {
906
+ if (key.toLowerCase() === "set-cookie") {
907
+ responseHeaders.append(key, value);
908
+ } else {
909
+ responseHeaders.set(key, value);
910
+ }
911
+ }
912
+ }
913
+ if (headers) {
914
+ for (const [k, v] of Object.entries(headers)) {
915
+ if (typeof v === "string") {
916
+ responseHeaders.set(k, v);
917
+ } else {
918
+ responseHeaders.delete(k);
919
+ for (const v2 of v) {
920
+ responseHeaders.append(k, v2);
921
+ }
922
+ }
923
+ }
924
+ }
925
+ const status = typeof arg === "number" ? arg : arg?.status ?? this.#status;
926
+ return createResponseInstance(data, { status, headers: responseHeaders });
927
+ }
928
+ newResponse = (...args) => this.#newResponse(...args);
929
+ body = (data, arg, headers) => this.#newResponse(data, arg, headers);
930
+ text = (text, arg, headers) => {
931
+ return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text) : this.#newResponse(text, arg, setDefaultContentType(TEXT_PLAIN, headers));
932
+ };
933
+ json = (object, arg, headers) => {
934
+ return this.#newResponse(JSON.stringify(object), arg, setDefaultContentType("application/json", headers));
935
+ };
936
+ html = (html, arg, headers) => {
937
+ const res = (html2) => this.#newResponse(html2, arg, setDefaultContentType("text/html; charset=UTF-8", headers));
938
+ return typeof html === "object" ? resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res) : res(html);
939
+ };
940
+ redirect = (location, status) => {
941
+ const locationString = String(location);
942
+ this.header("Location", !/[^\x00-\xFF]/.test(locationString) ? locationString : encodeURI(locationString));
943
+ return this.newResponse(null, status ?? 302);
944
+ };
945
+ notFound = () => {
946
+ this.#notFoundHandler ??= () => createResponseInstance();
947
+ return this.#notFoundHandler(this);
948
+ };
949
+ };
950
+
951
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/router.js
952
+ var METHOD_NAME_ALL = "ALL";
953
+ var METHOD_NAME_ALL_LOWERCASE = "all";
954
+ var METHODS = ["get", "post", "put", "delete", "options", "patch"];
955
+ var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built.";
956
+ var UnsupportedPathError = class extends Error {
957
+ };
958
+
959
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/utils/constants.js
960
+ var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
961
+
962
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/hono-base.js
963
+ var notFoundHandler = (c) => {
964
+ return c.text("404 Not Found", 404);
965
+ };
966
+ var errorHandler = (err, c) => {
967
+ if ("getResponse" in err) {
968
+ const res = err.getResponse();
969
+ return c.newResponse(res.body, res);
970
+ }
971
+ console.error(err);
972
+ return c.text("Internal Server Error", 500);
973
+ };
974
+ var Hono = class _Hono {
975
+ get;
976
+ post;
977
+ put;
978
+ delete;
979
+ options;
980
+ patch;
981
+ all;
982
+ on;
983
+ use;
984
+ router;
985
+ getPath;
986
+ _basePath = "/";
987
+ #path = "/";
988
+ routes = [];
989
+ constructor(options = {}) {
990
+ const allMethods = [...METHODS, METHOD_NAME_ALL_LOWERCASE];
991
+ allMethods.forEach((method) => {
992
+ this[method] = (args1, ...args) => {
993
+ if (typeof args1 === "string") {
994
+ this.#path = args1;
995
+ } else {
996
+ this.#addRoute(method, this.#path, args1);
997
+ }
998
+ args.forEach((handler) => {
999
+ this.#addRoute(method, this.#path, handler);
1000
+ });
1001
+ return this;
1002
+ };
1003
+ });
1004
+ this.on = (method, path, ...handlers) => {
1005
+ for (const p of [path].flat()) {
1006
+ this.#path = p;
1007
+ for (const m of [method].flat()) {
1008
+ handlers.map((handler) => {
1009
+ this.#addRoute(m.toUpperCase(), this.#path, handler);
1010
+ });
1011
+ }
1012
+ }
1013
+ return this;
1014
+ };
1015
+ this.use = (arg1, ...handlers) => {
1016
+ if (typeof arg1 === "string") {
1017
+ this.#path = arg1;
1018
+ } else {
1019
+ this.#path = "*";
1020
+ handlers.unshift(arg1);
1021
+ }
1022
+ handlers.forEach((handler) => {
1023
+ this.#addRoute(METHOD_NAME_ALL, this.#path, handler);
1024
+ });
1025
+ return this;
1026
+ };
1027
+ const { strict, ...optionsWithoutStrict } = options;
1028
+ Object.assign(this, optionsWithoutStrict);
1029
+ this.getPath = strict ?? true ? options.getPath ?? getPath : getPathNoStrict;
1030
+ }
1031
+ #clone() {
1032
+ const clone = new _Hono({
1033
+ router: this.router,
1034
+ getPath: this.getPath
1035
+ });
1036
+ clone.errorHandler = this.errorHandler;
1037
+ clone.#notFoundHandler = this.#notFoundHandler;
1038
+ clone.routes = this.routes;
1039
+ return clone;
1040
+ }
1041
+ #notFoundHandler = notFoundHandler;
1042
+ errorHandler = errorHandler;
1043
+ route(path, app) {
1044
+ const subApp = this.basePath(path);
1045
+ app.routes.map((r) => {
1046
+ let handler;
1047
+ if (app.errorHandler === errorHandler) {
1048
+ handler = r.handler;
1049
+ } else {
1050
+ handler = async (c, next) => (await compose([], app.errorHandler)(c, () => r.handler(c, next))).res;
1051
+ handler[COMPOSED_HANDLER] = r.handler;
1052
+ }
1053
+ subApp.#addRoute(r.method, r.path, handler);
1054
+ });
1055
+ return this;
1056
+ }
1057
+ basePath(path) {
1058
+ const subApp = this.#clone();
1059
+ subApp._basePath = mergePath(this._basePath, path);
1060
+ return subApp;
1061
+ }
1062
+ onError = (handler) => {
1063
+ this.errorHandler = handler;
1064
+ return this;
1065
+ };
1066
+ notFound = (handler) => {
1067
+ this.#notFoundHandler = handler;
1068
+ return this;
1069
+ };
1070
+ mount(path, applicationHandler, options) {
1071
+ let replaceRequest;
1072
+ let optionHandler;
1073
+ if (options) {
1074
+ if (typeof options === "function") {
1075
+ optionHandler = options;
1076
+ } else {
1077
+ optionHandler = options.optionHandler;
1078
+ if (options.replaceRequest === false) {
1079
+ replaceRequest = (request) => request;
1080
+ } else {
1081
+ replaceRequest = options.replaceRequest;
1082
+ }
1083
+ }
1084
+ }
1085
+ const getOptions = optionHandler ? (c) => {
1086
+ const options2 = optionHandler(c);
1087
+ return Array.isArray(options2) ? options2 : [options2];
1088
+ } : (c) => {
1089
+ let executionContext = undefined;
1090
+ try {
1091
+ executionContext = c.executionCtx;
1092
+ } catch {}
1093
+ return [c.env, executionContext];
1094
+ };
1095
+ replaceRequest ||= (() => {
1096
+ const mergedPath = mergePath(this._basePath, path);
1097
+ const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
1098
+ return (request) => {
1099
+ const url = new URL(request.url);
1100
+ url.pathname = url.pathname.slice(pathPrefixLength) || "/";
1101
+ return new Request(url, request);
1102
+ };
1103
+ })();
1104
+ const handler = async (c, next) => {
1105
+ const res = await applicationHandler(replaceRequest(c.req.raw), ...getOptions(c));
1106
+ if (res) {
1107
+ return res;
1108
+ }
1109
+ await next();
1110
+ };
1111
+ this.#addRoute(METHOD_NAME_ALL, mergePath(path, "*"), handler);
1112
+ return this;
1113
+ }
1114
+ #addRoute(method, path, handler) {
1115
+ method = method.toUpperCase();
1116
+ path = mergePath(this._basePath, path);
1117
+ const r = { basePath: this._basePath, path, method, handler };
1118
+ this.router.add(method, path, [handler, r]);
1119
+ this.routes.push(r);
1120
+ }
1121
+ #handleError(err, c) {
1122
+ if (err instanceof Error) {
1123
+ return this.errorHandler(err, c);
1124
+ }
1125
+ throw err;
1126
+ }
1127
+ #dispatch(request, executionCtx, env, method) {
1128
+ if (method === "HEAD") {
1129
+ return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))();
1130
+ }
1131
+ const path = this.getPath(request, { env });
1132
+ const matchResult = this.router.match(method, path);
1133
+ const c = new Context(request, {
1134
+ path,
1135
+ matchResult,
1136
+ env,
1137
+ executionCtx,
1138
+ notFoundHandler: this.#notFoundHandler
1139
+ });
1140
+ if (matchResult[0].length === 1) {
1141
+ let res;
1142
+ try {
1143
+ res = matchResult[0][0][0][0](c, async () => {
1144
+ c.res = await this.#notFoundHandler(c);
1145
+ });
1146
+ } catch (err) {
1147
+ return this.#handleError(err, c);
1148
+ }
1149
+ return res instanceof Promise ? res.then((resolved) => resolved || (c.finalized ? c.res : this.#notFoundHandler(c))).catch((err) => this.#handleError(err, c)) : res ?? this.#notFoundHandler(c);
1150
+ }
1151
+ const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler);
1152
+ return (async () => {
1153
+ try {
1154
+ const context = await composed(c);
1155
+ if (!context.finalized) {
1156
+ throw new Error("Context is not finalized. Did you forget to return a Response object or `await next()`?");
1157
+ }
1158
+ return context.res;
1159
+ } catch (err) {
1160
+ return this.#handleError(err, c);
1161
+ }
1162
+ })();
1163
+ }
1164
+ fetch = (request, ...rest) => {
1165
+ return this.#dispatch(request, rest[1], rest[0], request.method);
1166
+ };
1167
+ request = (input, requestInit, Env, executionCtx) => {
1168
+ if (input instanceof Request) {
1169
+ return this.fetch(requestInit ? new Request(input, requestInit) : input, Env, executionCtx);
1170
+ }
1171
+ input = input.toString();
1172
+ return this.fetch(new Request(/^https?:\/\//.test(input) ? input : `http://localhost${mergePath("/", input)}`, requestInit), Env, executionCtx);
1173
+ };
1174
+ fire = () => {
1175
+ addEventListener("fetch", (event) => {
1176
+ event.respondWith(this.#dispatch(event.request, event, undefined, event.request.method));
1177
+ });
1178
+ };
1179
+ };
1180
+
1181
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/router/reg-exp-router/matcher.js
1182
+ var emptyParam = [];
1183
+ function match(method, path) {
1184
+ const matchers = this.buildAllMatchers();
1185
+ const match2 = (method2, path2) => {
1186
+ const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
1187
+ const staticMatch = matcher[2][path2];
1188
+ if (staticMatch) {
1189
+ return staticMatch;
1190
+ }
1191
+ const match3 = path2.match(matcher[0]);
1192
+ if (!match3) {
1193
+ return [[], emptyParam];
1194
+ }
1195
+ const index = match3.indexOf("", 1);
1196
+ return [matcher[1][index], match3];
1197
+ };
1198
+ this.match = match2;
1199
+ return match2(method, path);
1200
+ }
1201
+
1202
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/router/reg-exp-router/node.js
1203
+ var LABEL_REG_EXP_STR = "[^/]+";
1204
+ var ONLY_WILDCARD_REG_EXP_STR = ".*";
1205
+ var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
1206
+ var PATH_ERROR = /* @__PURE__ */ Symbol();
1207
+ var regExpMetaChars = new Set(".\\+*[^]$()");
1208
+ function compareKey(a, b) {
1209
+ if (a.length === 1) {
1210
+ return b.length === 1 ? a < b ? -1 : 1 : -1;
1211
+ }
1212
+ if (b.length === 1) {
1213
+ return 1;
1214
+ }
1215
+ if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {
1216
+ return 1;
1217
+ } else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {
1218
+ return -1;
1219
+ }
1220
+ if (a === LABEL_REG_EXP_STR) {
1221
+ return 1;
1222
+ } else if (b === LABEL_REG_EXP_STR) {
1223
+ return -1;
1224
+ }
1225
+ return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length;
1226
+ }
1227
+ var Node = class _Node {
1228
+ #index;
1229
+ #varIndex;
1230
+ #children = /* @__PURE__ */ Object.create(null);
1231
+ insert(tokens, index, paramMap, context, pathErrorCheckOnly) {
1232
+ if (tokens.length === 0) {
1233
+ if (this.#index !== undefined) {
1234
+ throw PATH_ERROR;
1235
+ }
1236
+ if (pathErrorCheckOnly) {
1237
+ return;
1238
+ }
1239
+ this.#index = index;
1240
+ return;
1241
+ }
1242
+ const [token, ...restTokens] = tokens;
1243
+ const pattern = token === "*" ? restTokens.length === 0 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
1244
+ let node;
1245
+ if (pattern) {
1246
+ const name = pattern[1];
1247
+ let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
1248
+ if (name && pattern[2]) {
1249
+ if (regexpStr === ".*") {
1250
+ throw PATH_ERROR;
1251
+ }
1252
+ regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
1253
+ if (/\((?!\?:)/.test(regexpStr)) {
1254
+ throw PATH_ERROR;
1255
+ }
1256
+ }
1257
+ node = this.#children[regexpStr];
1258
+ if (!node) {
1259
+ if (Object.keys(this.#children).some((k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR)) {
1260
+ throw PATH_ERROR;
1261
+ }
1262
+ if (pathErrorCheckOnly) {
1263
+ return;
1264
+ }
1265
+ node = this.#children[regexpStr] = new _Node;
1266
+ if (name !== "") {
1267
+ node.#varIndex = context.varIndex++;
1268
+ }
1269
+ }
1270
+ if (!pathErrorCheckOnly && name !== "") {
1271
+ paramMap.push([name, node.#varIndex]);
1272
+ }
1273
+ } else {
1274
+ node = this.#children[token];
1275
+ if (!node) {
1276
+ if (Object.keys(this.#children).some((k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR)) {
1277
+ throw PATH_ERROR;
1278
+ }
1279
+ if (pathErrorCheckOnly) {
1280
+ return;
1281
+ }
1282
+ node = this.#children[token] = new _Node;
1283
+ }
1284
+ }
1285
+ node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly);
1286
+ }
1287
+ buildRegExpStr() {
1288
+ const childKeys = Object.keys(this.#children).sort(compareKey);
1289
+ const strList = childKeys.map((k) => {
1290
+ const c = this.#children[k];
1291
+ return (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + c.buildRegExpStr();
1292
+ });
1293
+ if (typeof this.#index === "number") {
1294
+ strList.unshift(`#${this.#index}`);
1295
+ }
1296
+ if (strList.length === 0) {
1297
+ return "";
1298
+ }
1299
+ if (strList.length === 1) {
1300
+ return strList[0];
1301
+ }
1302
+ return "(?:" + strList.join("|") + ")";
1303
+ }
1304
+ };
1305
+
1306
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/router/reg-exp-router/trie.js
1307
+ var Trie = class {
1308
+ #context = { varIndex: 0 };
1309
+ #root = new Node;
1310
+ insert(path, index, pathErrorCheckOnly) {
1311
+ const paramAssoc = [];
1312
+ const groups = [];
1313
+ for (let i = 0;; ) {
1314
+ let replaced = false;
1315
+ path = path.replace(/\{[^}]+\}/g, (m) => {
1316
+ const mark = `@\\${i}`;
1317
+ groups[i] = [mark, m];
1318
+ i++;
1319
+ replaced = true;
1320
+ return mark;
1321
+ });
1322
+ if (!replaced) {
1323
+ break;
1324
+ }
1325
+ }
1326
+ const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
1327
+ for (let i = groups.length - 1;i >= 0; i--) {
1328
+ const [mark] = groups[i];
1329
+ for (let j = tokens.length - 1;j >= 0; j--) {
1330
+ if (tokens[j].indexOf(mark) !== -1) {
1331
+ tokens[j] = tokens[j].replace(mark, groups[i][1]);
1332
+ break;
1333
+ }
1334
+ }
1335
+ }
1336
+ this.#root.insert(tokens, index, paramAssoc, this.#context, pathErrorCheckOnly);
1337
+ return paramAssoc;
1338
+ }
1339
+ buildRegExp() {
1340
+ let regexp = this.#root.buildRegExpStr();
1341
+ if (regexp === "") {
1342
+ return [/^$/, [], []];
1343
+ }
1344
+ let captureIndex = 0;
1345
+ const indexReplacementMap = [];
1346
+ const paramReplacementMap = [];
1347
+ regexp = regexp.replace(/#(\d+)|@(\d+)|\.\*\$/g, (_, handlerIndex, paramIndex) => {
1348
+ if (handlerIndex !== undefined) {
1349
+ indexReplacementMap[++captureIndex] = Number(handlerIndex);
1350
+ return "$()";
1351
+ }
1352
+ if (paramIndex !== undefined) {
1353
+ paramReplacementMap[Number(paramIndex)] = ++captureIndex;
1354
+ return "";
1355
+ }
1356
+ return "";
1357
+ });
1358
+ return [new RegExp(`^${regexp}`), indexReplacementMap, paramReplacementMap];
1359
+ }
1360
+ };
1361
+
1362
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/router/reg-exp-router/router.js
1363
+ var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
1364
+ var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
1365
+ function buildWildcardRegExp(path) {
1366
+ return wildcardRegExpCache[path] ??= new RegExp(path === "*" ? "" : `^${path.replace(/\/\*$|([.\\+*[^\]$()])/g, (_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)")}$`);
1367
+ }
1368
+ function clearWildcardRegExpCache() {
1369
+ wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
1370
+ }
1371
+ function buildMatcherFromPreprocessedRoutes(routes) {
1372
+ const trie = new Trie;
1373
+ const handlerData = [];
1374
+ if (routes.length === 0) {
1375
+ return nullMatcher;
1376
+ }
1377
+ const routesWithStaticPathFlag = routes.map((route) => [!/\*|\/:/.test(route[0]), ...route]).sort(([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length);
1378
+ const staticMap = /* @__PURE__ */ Object.create(null);
1379
+ for (let i = 0, j = -1, len = routesWithStaticPathFlag.length;i < len; i++) {
1380
+ const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];
1381
+ if (pathErrorCheckOnly) {
1382
+ staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
1383
+ } else {
1384
+ j++;
1385
+ }
1386
+ let paramAssoc;
1387
+ try {
1388
+ paramAssoc = trie.insert(path, j, pathErrorCheckOnly);
1389
+ } catch (e) {
1390
+ throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
1391
+ }
1392
+ if (pathErrorCheckOnly) {
1393
+ continue;
1394
+ }
1395
+ handlerData[j] = handlers.map(([h, paramCount]) => {
1396
+ const paramIndexMap = /* @__PURE__ */ Object.create(null);
1397
+ paramCount -= 1;
1398
+ for (;paramCount >= 0; paramCount--) {
1399
+ const [key, value] = paramAssoc[paramCount];
1400
+ paramIndexMap[key] = value;
1401
+ }
1402
+ return [h, paramIndexMap];
1403
+ });
1404
+ }
1405
+ const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
1406
+ for (let i = 0, len = handlerData.length;i < len; i++) {
1407
+ for (let j = 0, len2 = handlerData[i].length;j < len2; j++) {
1408
+ const map = handlerData[i][j]?.[1];
1409
+ if (!map) {
1410
+ continue;
1411
+ }
1412
+ const keys = Object.keys(map);
1413
+ for (let k = 0, len3 = keys.length;k < len3; k++) {
1414
+ map[keys[k]] = paramReplacementMap[map[keys[k]]];
1415
+ }
1416
+ }
1417
+ }
1418
+ const handlerMap = [];
1419
+ for (const i in indexReplacementMap) {
1420
+ handlerMap[i] = handlerData[indexReplacementMap[i]];
1421
+ }
1422
+ return [regexp, handlerMap, staticMap];
1423
+ }
1424
+ function findMiddleware(middleware, path) {
1425
+ if (!middleware) {
1426
+ return;
1427
+ }
1428
+ for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {
1429
+ if (buildWildcardRegExp(k).test(path)) {
1430
+ return [...middleware[k]];
1431
+ }
1432
+ }
1433
+ return;
1434
+ }
1435
+ var RegExpRouter = class {
1436
+ name = "RegExpRouter";
1437
+ #middleware;
1438
+ #routes;
1439
+ constructor() {
1440
+ this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
1441
+ this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
1442
+ }
1443
+ add(method, path, handler) {
1444
+ const middleware = this.#middleware;
1445
+ const routes = this.#routes;
1446
+ if (!middleware || !routes) {
1447
+ throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
1448
+ }
1449
+ if (!middleware[method]) {
1450
+ [middleware, routes].forEach((handlerMap) => {
1451
+ handlerMap[method] = /* @__PURE__ */ Object.create(null);
1452
+ Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => {
1453
+ handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];
1454
+ });
1455
+ });
1456
+ }
1457
+ if (path === "/*") {
1458
+ path = "*";
1459
+ }
1460
+ const paramCount = (path.match(/\/:/g) || []).length;
1461
+ if (/\*$/.test(path)) {
1462
+ const re = buildWildcardRegExp(path);
1463
+ if (method === METHOD_NAME_ALL) {
1464
+ Object.keys(middleware).forEach((m) => {
1465
+ middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
1466
+ });
1467
+ } else {
1468
+ middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
1469
+ }
1470
+ Object.keys(middleware).forEach((m) => {
1471
+ if (method === METHOD_NAME_ALL || method === m) {
1472
+ Object.keys(middleware[m]).forEach((p) => {
1473
+ re.test(p) && middleware[m][p].push([handler, paramCount]);
1474
+ });
1475
+ }
1476
+ });
1477
+ Object.keys(routes).forEach((m) => {
1478
+ if (method === METHOD_NAME_ALL || method === m) {
1479
+ Object.keys(routes[m]).forEach((p) => re.test(p) && routes[m][p].push([handler, paramCount]));
1480
+ }
1481
+ });
1482
+ return;
1483
+ }
1484
+ const paths = checkOptionalParameter(path) || [path];
1485
+ for (let i = 0, len = paths.length;i < len; i++) {
1486
+ const path2 = paths[i];
1487
+ Object.keys(routes).forEach((m) => {
1488
+ if (method === METHOD_NAME_ALL || method === m) {
1489
+ routes[m][path2] ||= [
1490
+ ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
1491
+ ];
1492
+ routes[m][path2].push([handler, paramCount - len + i + 1]);
1493
+ }
1494
+ });
1495
+ }
1496
+ }
1497
+ match = match;
1498
+ buildAllMatchers() {
1499
+ const matchers = /* @__PURE__ */ Object.create(null);
1500
+ Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => {
1501
+ matchers[method] ||= this.#buildMatcher(method);
1502
+ });
1503
+ this.#middleware = this.#routes = undefined;
1504
+ clearWildcardRegExpCache();
1505
+ return matchers;
1506
+ }
1507
+ #buildMatcher(method) {
1508
+ const routes = [];
1509
+ let hasOwnRoute = method === METHOD_NAME_ALL;
1510
+ [this.#middleware, this.#routes].forEach((r) => {
1511
+ const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : [];
1512
+ if (ownRoute.length !== 0) {
1513
+ hasOwnRoute ||= true;
1514
+ routes.push(...ownRoute);
1515
+ } else if (method !== METHOD_NAME_ALL) {
1516
+ routes.push(...Object.keys(r[METHOD_NAME_ALL]).map((path) => [path, r[METHOD_NAME_ALL][path]]));
1517
+ }
1518
+ });
1519
+ if (!hasOwnRoute) {
1520
+ return null;
1521
+ } else {
1522
+ return buildMatcherFromPreprocessedRoutes(routes);
1523
+ }
1524
+ }
1525
+ };
1526
+
1527
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/router/reg-exp-router/prepared-router.js
1528
+ var PreparedRegExpRouter = class {
1529
+ name = "PreparedRegExpRouter";
1530
+ #matchers;
1531
+ #relocateMap;
1532
+ constructor(matchers, relocateMap) {
1533
+ this.#matchers = matchers;
1534
+ this.#relocateMap = relocateMap;
1535
+ }
1536
+ #addWildcard(method, handlerData) {
1537
+ const matcher = this.#matchers[method];
1538
+ matcher[1].forEach((list) => list && list.push(handlerData));
1539
+ Object.values(matcher[2]).forEach((list) => list[0].push(handlerData));
1540
+ }
1541
+ #addPath(method, path, handler, indexes, map) {
1542
+ const matcher = this.#matchers[method];
1543
+ if (!map) {
1544
+ matcher[2][path][0].push([handler, {}]);
1545
+ } else {
1546
+ indexes.forEach((index) => {
1547
+ if (typeof index === "number") {
1548
+ matcher[1][index].push([handler, map]);
1549
+ } else {
1550
+ matcher[2][index || path][0].push([handler, map]);
1551
+ }
1552
+ });
1553
+ }
1554
+ }
1555
+ add(method, path, handler) {
1556
+ if (!this.#matchers[method]) {
1557
+ const all = this.#matchers[METHOD_NAME_ALL];
1558
+ const staticMap = {};
1559
+ for (const key in all[2]) {
1560
+ staticMap[key] = [all[2][key][0].slice(), emptyParam];
1561
+ }
1562
+ this.#matchers[method] = [
1563
+ all[0],
1564
+ all[1].map((list) => Array.isArray(list) ? list.slice() : 0),
1565
+ staticMap
1566
+ ];
1567
+ }
1568
+ if (path === "/*" || path === "*") {
1569
+ const handlerData = [handler, {}];
1570
+ if (method === METHOD_NAME_ALL) {
1571
+ for (const m in this.#matchers) {
1572
+ this.#addWildcard(m, handlerData);
1573
+ }
1574
+ } else {
1575
+ this.#addWildcard(method, handlerData);
1576
+ }
1577
+ return;
1578
+ }
1579
+ const data = this.#relocateMap[path];
1580
+ if (!data) {
1581
+ throw new Error(`Path ${path} is not registered`);
1582
+ }
1583
+ for (const [indexes, map] of data) {
1584
+ if (method === METHOD_NAME_ALL) {
1585
+ for (const m in this.#matchers) {
1586
+ this.#addPath(m, path, handler, indexes, map);
1587
+ }
1588
+ } else {
1589
+ this.#addPath(method, path, handler, indexes, map);
1590
+ }
1591
+ }
1592
+ }
1593
+ buildAllMatchers() {
1594
+ return this.#matchers;
1595
+ }
1596
+ match = match;
1597
+ };
1598
+
1599
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/router/smart-router/router.js
1600
+ var SmartRouter = class {
1601
+ name = "SmartRouter";
1602
+ #routers = [];
1603
+ #routes = [];
1604
+ constructor(init) {
1605
+ this.#routers = init.routers;
1606
+ }
1607
+ add(method, path, handler) {
1608
+ if (!this.#routes) {
1609
+ throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
1610
+ }
1611
+ this.#routes.push([method, path, handler]);
1612
+ }
1613
+ match(method, path) {
1614
+ if (!this.#routes) {
1615
+ throw new Error("Fatal error");
1616
+ }
1617
+ const routers = this.#routers;
1618
+ const routes = this.#routes;
1619
+ const len = routers.length;
1620
+ let i = 0;
1621
+ let res;
1622
+ for (;i < len; i++) {
1623
+ const router = routers[i];
1624
+ try {
1625
+ for (let i2 = 0, len2 = routes.length;i2 < len2; i2++) {
1626
+ router.add(...routes[i2]);
1627
+ }
1628
+ res = router.match(method, path);
1629
+ } catch (e) {
1630
+ if (e instanceof UnsupportedPathError) {
1631
+ continue;
1632
+ }
1633
+ throw e;
1634
+ }
1635
+ this.match = router.match.bind(router);
1636
+ this.#routers = [router];
1637
+ this.#routes = undefined;
1638
+ break;
1639
+ }
1640
+ if (i === len) {
1641
+ throw new Error("Fatal error");
1642
+ }
1643
+ this.name = `SmartRouter + ${this.activeRouter.name}`;
1644
+ return res;
1645
+ }
1646
+ get activeRouter() {
1647
+ if (this.#routes || this.#routers.length !== 1) {
1648
+ throw new Error("No active router has been determined yet.");
1649
+ }
1650
+ return this.#routers[0];
1651
+ }
1652
+ };
1653
+
1654
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/router/trie-router/node.js
1655
+ var emptyParams = /* @__PURE__ */ Object.create(null);
1656
+ var hasChildren = (children) => {
1657
+ for (const _ in children) {
1658
+ return true;
1659
+ }
1660
+ return false;
1661
+ };
1662
+ var Node2 = class _Node2 {
1663
+ #methods;
1664
+ #children;
1665
+ #patterns;
1666
+ #order = 0;
1667
+ #params = emptyParams;
1668
+ constructor(method, handler, children) {
1669
+ this.#children = children || /* @__PURE__ */ Object.create(null);
1670
+ this.#methods = [];
1671
+ if (method && handler) {
1672
+ const m = /* @__PURE__ */ Object.create(null);
1673
+ m[method] = { handler, possibleKeys: [], score: 0 };
1674
+ this.#methods = [m];
1675
+ }
1676
+ this.#patterns = [];
1677
+ }
1678
+ insert(method, path, handler) {
1679
+ this.#order = ++this.#order;
1680
+ let curNode = this;
1681
+ const parts = splitRoutingPath(path);
1682
+ const possibleKeys = [];
1683
+ for (let i = 0, len = parts.length;i < len; i++) {
1684
+ const p = parts[i];
1685
+ const nextP = parts[i + 1];
1686
+ const pattern = getPattern(p, nextP);
1687
+ const key = Array.isArray(pattern) ? pattern[0] : p;
1688
+ if (key in curNode.#children) {
1689
+ curNode = curNode.#children[key];
1690
+ if (pattern) {
1691
+ possibleKeys.push(pattern[1]);
1692
+ }
1693
+ continue;
1694
+ }
1695
+ curNode.#children[key] = new _Node2;
1696
+ if (pattern) {
1697
+ curNode.#patterns.push(pattern);
1698
+ possibleKeys.push(pattern[1]);
1699
+ }
1700
+ curNode = curNode.#children[key];
1701
+ }
1702
+ curNode.#methods.push({
1703
+ [method]: {
1704
+ handler,
1705
+ possibleKeys: possibleKeys.filter((v, i, a) => a.indexOf(v) === i),
1706
+ score: this.#order
1707
+ }
1708
+ });
1709
+ return curNode;
1710
+ }
1711
+ #pushHandlerSets(handlerSets, node, method, nodeParams, params) {
1712
+ for (let i = 0, len = node.#methods.length;i < len; i++) {
1713
+ const m = node.#methods[i];
1714
+ const handlerSet = m[method] || m[METHOD_NAME_ALL];
1715
+ const processedSet = {};
1716
+ if (handlerSet !== undefined) {
1717
+ handlerSet.params = /* @__PURE__ */ Object.create(null);
1718
+ handlerSets.push(handlerSet);
1719
+ if (nodeParams !== emptyParams || params && params !== emptyParams) {
1720
+ for (let i2 = 0, len2 = handlerSet.possibleKeys.length;i2 < len2; i2++) {
1721
+ const key = handlerSet.possibleKeys[i2];
1722
+ const processed = processedSet[handlerSet.score];
1723
+ handlerSet.params[key] = params?.[key] && !processed ? params[key] : nodeParams[key] ?? params?.[key];
1724
+ processedSet[handlerSet.score] = true;
1725
+ }
1726
+ }
1727
+ }
1728
+ }
1729
+ }
1730
+ search(method, path) {
1731
+ const handlerSets = [];
1732
+ this.#params = emptyParams;
1733
+ const curNode = this;
1734
+ let curNodes = [curNode];
1735
+ const parts = splitPath(path);
1736
+ const curNodesQueue = [];
1737
+ const len = parts.length;
1738
+ let partOffsets = null;
1739
+ for (let i = 0;i < len; i++) {
1740
+ const part = parts[i];
1741
+ const isLast = i === len - 1;
1742
+ const tempNodes = [];
1743
+ for (let j = 0, len2 = curNodes.length;j < len2; j++) {
1744
+ const node = curNodes[j];
1745
+ const nextNode = node.#children[part];
1746
+ if (nextNode) {
1747
+ nextNode.#params = node.#params;
1748
+ if (isLast) {
1749
+ if (nextNode.#children["*"]) {
1750
+ this.#pushHandlerSets(handlerSets, nextNode.#children["*"], method, node.#params);
1751
+ }
1752
+ this.#pushHandlerSets(handlerSets, nextNode, method, node.#params);
1753
+ } else {
1754
+ tempNodes.push(nextNode);
1755
+ }
1756
+ }
1757
+ for (let k = 0, len3 = node.#patterns.length;k < len3; k++) {
1758
+ const pattern = node.#patterns[k];
1759
+ const params = node.#params === emptyParams ? {} : { ...node.#params };
1760
+ if (pattern === "*") {
1761
+ const astNode = node.#children["*"];
1762
+ if (astNode) {
1763
+ this.#pushHandlerSets(handlerSets, astNode, method, node.#params);
1764
+ astNode.#params = params;
1765
+ tempNodes.push(astNode);
1766
+ }
1767
+ continue;
1768
+ }
1769
+ const [key, name, matcher] = pattern;
1770
+ if (!part && !(matcher instanceof RegExp)) {
1771
+ continue;
1772
+ }
1773
+ const child = node.#children[key];
1774
+ if (matcher instanceof RegExp) {
1775
+ if (partOffsets === null) {
1776
+ partOffsets = new Array(len);
1777
+ let offset = path[0] === "/" ? 1 : 0;
1778
+ for (let p = 0;p < len; p++) {
1779
+ partOffsets[p] = offset;
1780
+ offset += parts[p].length + 1;
1781
+ }
1782
+ }
1783
+ const restPathString = path.substring(partOffsets[i]);
1784
+ const m = matcher.exec(restPathString);
1785
+ if (m) {
1786
+ params[name] = m[0];
1787
+ this.#pushHandlerSets(handlerSets, child, method, node.#params, params);
1788
+ if (hasChildren(child.#children)) {
1789
+ child.#params = params;
1790
+ const componentCount = m[0].match(/\//)?.length ?? 0;
1791
+ const targetCurNodes = curNodesQueue[componentCount] ||= [];
1792
+ targetCurNodes.push(child);
1793
+ }
1794
+ continue;
1795
+ }
1796
+ }
1797
+ if (matcher === true || matcher.test(part)) {
1798
+ params[name] = part;
1799
+ if (isLast) {
1800
+ this.#pushHandlerSets(handlerSets, child, method, params, node.#params);
1801
+ if (child.#children["*"]) {
1802
+ this.#pushHandlerSets(handlerSets, child.#children["*"], method, params, node.#params);
1803
+ }
1804
+ } else {
1805
+ child.#params = params;
1806
+ tempNodes.push(child);
1807
+ }
1808
+ }
1809
+ }
1810
+ }
1811
+ const shifted = curNodesQueue.shift();
1812
+ curNodes = shifted ? tempNodes.concat(shifted) : tempNodes;
1813
+ }
1814
+ if (handlerSets.length > 1) {
1815
+ handlerSets.sort((a, b) => {
1816
+ return a.score - b.score;
1817
+ });
1818
+ }
1819
+ return [handlerSets.map(({ handler, params }) => [handler, params])];
1820
+ }
1821
+ };
1822
+
1823
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/router/trie-router/router.js
1824
+ var TrieRouter = class {
1825
+ name = "TrieRouter";
1826
+ #node;
1827
+ constructor() {
1828
+ this.#node = new Node2;
1829
+ }
1830
+ add(method, path, handler) {
1831
+ const results = checkOptionalParameter(path);
1832
+ if (results) {
1833
+ for (let i = 0, len = results.length;i < len; i++) {
1834
+ this.#node.insert(method, results[i], handler);
1835
+ }
1836
+ return;
1837
+ }
1838
+ this.#node.insert(method, path, handler);
1839
+ }
1840
+ match(method, path) {
1841
+ return this.#node.search(method, path);
1842
+ }
1843
+ };
1844
+
1845
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/hono.js
1846
+ var Hono2 = class extends Hono {
1847
+ constructor(options = {}) {
1848
+ super(options);
1849
+ this.router = options.router ?? new SmartRouter({
1850
+ routers: [new RegExpRouter, new TrieRouter]
1851
+ });
1852
+ }
1853
+ };
1854
+
1855
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/middleware/cors/index.js
1856
+ var cors = (options) => {
1857
+ const opts = {
1858
+ origin: "*",
1859
+ allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"],
1860
+ allowHeaders: [],
1861
+ exposeHeaders: [],
1862
+ ...options
1863
+ };
1864
+ const findAllowOrigin = ((optsOrigin) => {
1865
+ if (typeof optsOrigin === "string") {
1866
+ if (optsOrigin === "*") {
1867
+ if (opts.credentials) {
1868
+ return (origin) => origin || null;
1869
+ }
1870
+ return () => optsOrigin;
1871
+ } else {
1872
+ return (origin) => optsOrigin === origin ? origin : null;
1873
+ }
1874
+ } else if (typeof optsOrigin === "function") {
1875
+ return optsOrigin;
1876
+ } else {
1877
+ return (origin) => optsOrigin.includes(origin) ? origin : null;
1878
+ }
1879
+ })(opts.origin);
1880
+ const findAllowMethods = ((optsAllowMethods) => {
1881
+ if (typeof optsAllowMethods === "function") {
1882
+ return optsAllowMethods;
1883
+ } else if (Array.isArray(optsAllowMethods)) {
1884
+ return () => optsAllowMethods;
1885
+ } else {
1886
+ return () => [];
1887
+ }
1888
+ })(opts.allowMethods);
1889
+ return async function cors2(c, next) {
1890
+ function set(key, value) {
1891
+ c.res.headers.set(key, value);
1892
+ }
1893
+ const allowOrigin = await findAllowOrigin(c.req.header("origin") || "", c);
1894
+ if (allowOrigin) {
1895
+ set("Access-Control-Allow-Origin", allowOrigin);
1896
+ }
1897
+ if (opts.credentials) {
1898
+ set("Access-Control-Allow-Credentials", "true");
1899
+ }
1900
+ if (opts.exposeHeaders?.length) {
1901
+ set("Access-Control-Expose-Headers", opts.exposeHeaders.join(","));
1902
+ }
1903
+ if (c.req.method === "OPTIONS") {
1904
+ if (opts.origin !== "*" || opts.credentials) {
1905
+ set("Vary", "Origin");
1906
+ }
1907
+ if (opts.maxAge != null) {
1908
+ set("Access-Control-Max-Age", opts.maxAge.toString());
1909
+ }
1910
+ const allowMethods = await findAllowMethods(c.req.header("origin") || "", c);
1911
+ if (allowMethods.length) {
1912
+ set("Access-Control-Allow-Methods", allowMethods.join(","));
1913
+ }
1914
+ let headers = opts.allowHeaders;
1915
+ if (!headers?.length) {
1916
+ const requestHeaders = c.req.header("Access-Control-Request-Headers");
1917
+ if (requestHeaders) {
1918
+ headers = requestHeaders.split(/\s*,\s*/);
1919
+ }
1920
+ }
1921
+ if (headers?.length) {
1922
+ set("Access-Control-Allow-Headers", headers.join(","));
1923
+ c.res.headers.append("Vary", "Access-Control-Request-Headers");
1924
+ }
1925
+ c.res.headers.delete("Content-Length");
1926
+ c.res.headers.delete("Content-Type");
1927
+ return new Response(null, {
1928
+ headers: c.res.headers,
1929
+ status: 204,
1930
+ statusText: "No Content"
1931
+ });
1932
+ }
1933
+ await next();
1934
+ if (opts.origin !== "*" || opts.credentials) {
1935
+ c.header("Vary", "Origin", { append: true });
1936
+ }
1937
+ };
1938
+ };
1939
+
1940
+ // src/middleware/cors.ts
1941
+ var corsMiddleware = cors({
1942
+ origin: config.corsOrigins === "*" ? "*" : config.corsOrigins.split(","),
1943
+ allowMethods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
1944
+ allowHeaders: ["Content-Type", "Authorization", "X-Hermes-Profile"],
1945
+ credentials: true
1946
+ });
1947
+
1948
+ // ../../node_modules/.bun/hono@4.12.19/node_modules/hono/dist/helper/factory/index.js
1949
+ var Factory = class {
1950
+ initApp;
1951
+ #defaultAppOptions;
1952
+ constructor(init) {
1953
+ this.initApp = init?.initApp;
1954
+ this.#defaultAppOptions = init?.defaultAppOptions;
1955
+ }
1956
+ createApp = (options) => {
1957
+ const app = new Hono2(options && this.#defaultAppOptions ? { ...this.#defaultAppOptions, ...options } : options ?? this.#defaultAppOptions);
1958
+ if (this.initApp) {
1959
+ this.initApp(app);
1960
+ }
1961
+ return app;
1962
+ };
1963
+ createMiddleware = (middleware) => middleware;
1964
+ createHandlers = (...handlers) => {
1965
+ return handlers.filter((handler) => handler !== undefined);
1966
+ };
1967
+ };
1968
+ var createMiddleware = (middleware) => middleware;
1969
+
1970
+ // src/middleware/logger.ts
1971
+ var requestLogger = createMiddleware(async (c, next) => {
1972
+ const start = Date.now();
1973
+ await next();
1974
+ const duration = Date.now() - start;
1975
+ logger.info(`${c.req.method} ${c.req.path} \u2014 ${c.res.status} (${duration}ms)`, {
1976
+ method: c.req.method,
1977
+ path: c.req.path,
1978
+ status: c.res.status,
1979
+ duration
1980
+ });
1981
+ });
1982
+
1983
+ // src/middleware/auth.ts
1984
+ var authMiddleware = createMiddleware(async (c, next) => {
1985
+ if (config.authDisabled) {
1986
+ return next();
1987
+ }
1988
+ const authHeader = c.req.header("authorization") || "";
1989
+ const headerToken = authHeader.replace(/^Bearer\s+/i, "");
1990
+ const queryToken = c.req.query("token") || "";
1991
+ const token = headerToken || queryToken;
1992
+ const serverToken = await getToken();
1993
+ if (serverToken && token !== serverToken) {
1994
+ throw new HTTPException(401, { message: "Unauthorized" });
1995
+ }
1996
+ return next();
1997
+ });
1998
+ var optionalAuth = createMiddleware(async (c, next) => {
1999
+ if (config.authDisabled) {
2000
+ return next();
2001
+ }
2002
+ return next();
2003
+ });
2004
+
2005
+ // src/routes/health.ts
2006
+ var app = new Hono2;
2007
+ app.get("/health", (c) => {
2008
+ return c.json({ status: "ok", version: process.env.APP_VERSION || "dev" });
2009
+ });
2010
+
2011
+ // src/routes/auth.ts
2012
+ var publicApp = new Hono2;
2013
+ publicApp.post("/api/auth/token", async (c) => {
2014
+ const token = await ensureToken();
2015
+ return c.json({ token });
2016
+ });
2017
+ publicApp.get("/api/auth/status", async (c) => {
2018
+ const token = await getToken();
2019
+ return c.json({ enabled: !!token });
2020
+ });
2021
+ var protectedApp = new Hono2;
2022
+ protectedApp.post("/api/auth/logout", async (c) => {
2023
+ return c.json({ ok: true });
2024
+ });
2025
+
2026
+ // src/lib/utils.ts
2027
+ function uid() {
2028
+ return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
2029
+ }
2030
+
2031
+ // src/controllers/hermes/sessions.ts
2032
+ function listSessions(c) {
2033
+ const db = getDb();
2034
+ const rows = db.query("SELECT * FROM sessions ORDER BY updated_at DESC").all();
2035
+ const sessions = rows.map((r) => ({
2036
+ id: String(r.id),
2037
+ title: String(r.title),
2038
+ source: r.source ? String(r.source) : undefined,
2039
+ createdAt: Number(r.created_at),
2040
+ updatedAt: Number(r.updated_at),
2041
+ model: r.model ? String(r.model) : undefined,
2042
+ provider: r.provider ? String(r.provider) : undefined,
2043
+ messageCount: Number(r.message_count || 0),
2044
+ inputTokens: Number(r.input_tokens || 0),
2045
+ outputTokens: Number(r.output_tokens || 0),
2046
+ endedAt: r.ended_at ? Number(r.ended_at) : null,
2047
+ lastActiveAt: r.last_active_at ? Number(r.last_active_at) : undefined,
2048
+ workspace: r.workspace ? String(r.workspace) : null
2049
+ }));
2050
+ return c.json({ sessions });
2051
+ }
2052
+ function getSession(c) {
2053
+ const id = c.req.param("id") ?? "";
2054
+ const db = getDb();
2055
+ const row = db.query("SELECT * FROM sessions WHERE id = ?").get(id);
2056
+ if (!row)
2057
+ return c.json({ error: "Session not found" }, 404);
2058
+ const messages = db.query("SELECT * FROM messages WHERE session_id = ? ORDER BY timestamp").all(id);
2059
+ return c.json({
2060
+ session: {
2061
+ id: String(row.id),
2062
+ title: String(row.title),
2063
+ source: row.source ? String(row.source) : undefined,
2064
+ createdAt: Number(row.created_at),
2065
+ updatedAt: Number(row.updated_at),
2066
+ model: row.model ? String(row.model) : undefined,
2067
+ provider: row.provider ? String(row.provider) : undefined,
2068
+ messageCount: Number(row.message_count || 0),
2069
+ inputTokens: Number(row.input_tokens || 0),
2070
+ outputTokens: Number(row.output_tokens || 0),
2071
+ endedAt: row.ended_at ? Number(row.ended_at) : null,
2072
+ lastActiveAt: row.last_active_at ? Number(row.last_active_at) : undefined,
2073
+ workspace: row.workspace ? String(row.workspace) : null
2074
+ },
2075
+ messages: messages.map((m) => {
2076
+ let toolCalls;
2077
+ if (m.tool_calls) {
2078
+ try {
2079
+ toolCalls = JSON.parse(String(m.tool_calls));
2080
+ } catch {}
2081
+ }
2082
+ return {
2083
+ id: String(m.id),
2084
+ role: String(m.role),
2085
+ content: String(m.content),
2086
+ timestamp: Number(m.timestamp),
2087
+ attachments: m.attachments ? JSON.parse(String(m.attachments)) : undefined,
2088
+ toolName: m.tool_name ? String(m.tool_name) : undefined,
2089
+ toolCallId: m.tool_call_id ? String(m.tool_call_id) : undefined,
2090
+ toolArgs: m.tool_args ? String(m.tool_args) : undefined,
2091
+ toolResult: m.tool_result ? String(m.tool_result) : undefined,
2092
+ toolStatus: m.tool_status ? String(m.tool_status) : undefined,
2093
+ toolDuration: m.tool_duration ? Number(m.tool_duration) : undefined,
2094
+ isStreaming: Boolean(m.is_streaming),
2095
+ reasoning: m.reasoning ? String(m.reasoning) : undefined,
2096
+ reasoningStartedAt: m.reasoning_started_at ? Number(m.reasoning_started_at) : undefined,
2097
+ reasoningEndedAt: m.reasoning_ended_at ? Number(m.reasoning_ended_at) : undefined,
2098
+ toolCalls,
2099
+ queued: Boolean(m.queued)
2100
+ };
2101
+ })
2102
+ });
2103
+ }
2104
+ async function createSession(c) {
2105
+ const body = await c.req.json();
2106
+ const db = getDb();
2107
+ const id = body.id || uid();
2108
+ const now = Date.now();
2109
+ const title = body.title ?? "";
2110
+ db.run("INSERT INTO sessions (id, title, source, created_at, updated_at, model) VALUES (?, ?, ?, ?, ?, ?)", [id, title, body.source ?? null, now, now, body.model ?? null]);
2111
+ return c.json({ id, title, createdAt: now, updatedAt: now });
2112
+ }
2113
+ async function updateSession(c) {
2114
+ const id = c.req.param("id") ?? "";
2115
+ const body = await c.req.json();
2116
+ const db = getDb();
2117
+ const fields = [];
2118
+ const values = [];
2119
+ if (body.title !== undefined) {
2120
+ fields.push("title = ?");
2121
+ values.push(body.title);
2122
+ }
2123
+ if (body.model !== undefined) {
2124
+ fields.push("model = ?");
2125
+ values.push(body.model);
2126
+ }
2127
+ if (body.updatedAt !== undefined) {
2128
+ fields.push("updated_at = ?");
2129
+ values.push(body.updatedAt);
2130
+ }
2131
+ if (fields.length === 0)
2132
+ return c.json({ error: "No fields to update" }, 400);
2133
+ values.push(id);
2134
+ db.run(`UPDATE sessions SET ${fields.join(", ")} WHERE id = ?`, values);
2135
+ return c.json({ ok: true });
2136
+ }
2137
+ function deleteSession(c) {
2138
+ const id = c.req.param("id") ?? "";
2139
+ const db = getDb();
2140
+ db.run("DELETE FROM messages WHERE session_id = ?", [id]);
2141
+ db.run("DELETE FROM sessions WHERE id = ?", [id]);
2142
+ return c.json({ ok: true });
2143
+ }
2144
+ async function renameSession(c) {
2145
+ const id = c.req.param("id") ?? "";
2146
+ const body = await c.req.json();
2147
+ const db = getDb();
2148
+ const now = Date.now();
2149
+ db.run("UPDATE sessions SET title = ?, updated_at = ? WHERE id = ?", [body.title, now, id]);
2150
+ return c.json({ ok: true });
2151
+ }
2152
+ async function addMessage(c) {
2153
+ const sessionId = c.req.param("id") ?? "";
2154
+ const body = await c.req.json();
2155
+ const db = getDb();
2156
+ db.run(`INSERT OR REPLACE INTO messages (
2157
+ id, session_id, role, content, timestamp,
2158
+ tool_name, tool_call_id, tool_args, tool_result, tool_status, tool_duration,
2159
+ is_streaming, reasoning, reasoning_started_at, reasoning_ended_at, tool_calls, queued, attachments
2160
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
2161
+ body.id,
2162
+ sessionId,
2163
+ body.role,
2164
+ body.content,
2165
+ body.timestamp,
2166
+ body.toolName ?? null,
2167
+ body.toolCallId ?? null,
2168
+ body.toolArgs ?? null,
2169
+ body.toolResult ?? null,
2170
+ body.toolStatus ?? null,
2171
+ body.toolDuration ?? null,
2172
+ body.isStreaming ? 1 : 0,
2173
+ body.reasoning ?? null,
2174
+ body.reasoningStartedAt ?? null,
2175
+ body.reasoningEndedAt ?? null,
2176
+ body.toolCalls ? JSON.stringify(body.toolCalls) : null,
2177
+ body.queued ? 1 : 0,
2178
+ body.attachments ? JSON.stringify(body.attachments) : null
2179
+ ]);
2180
+ db.run(`UPDATE sessions SET
2181
+ message_count = (SELECT COUNT(*) FROM messages WHERE session_id = ?),
2182
+ updated_at = ?,
2183
+ last_active_at = ?
2184
+ WHERE id = ?`, [sessionId, Date.now(), Date.now(), sessionId]);
2185
+ return c.json({ ok: true });
2186
+ }
2187
+
2188
+ // src/routes/hermes/sessions.ts
2189
+ var app2 = new Hono2;
2190
+ app2.get("/api/hermes/sessions", listSessions);
2191
+ app2.get("/api/hermes/sessions/:id", getSession);
2192
+ app2.post("/api/hermes/sessions", createSession);
2193
+ app2.patch("/api/hermes/sessions/:id", updateSession);
2194
+ app2.delete("/api/hermes/sessions/:id", deleteSession);
2195
+ app2.post("/api/hermes/sessions/:id/rename", renameSession);
2196
+ app2.post("/api/hermes/sessions/:id/messages", addMessage);
2197
+
2198
+ // src/services/chat-gateway.ts
2199
+ var runSessionMap = new Map;
2200
+ function setRunSession(runId, sessionId, model, profile) {
2201
+ runSessionMap.set(runId, { sessionId, model, profile });
2202
+ setTimeout(() => runSessionMap.delete(runId), 30 * 60 * 1000);
2203
+ }
2204
+ function getSessionForRun(runId) {
2205
+ return runSessionMap.get(runId);
2206
+ }
2207
+ function getUpstream(profile) {
2208
+ const mgr = getGatewayManagerInstance();
2209
+ return mgr.getUpstream(profile);
2210
+ }
2211
+ function getApiKey(profile) {
2212
+ const mgr = getGatewayManagerInstance();
2213
+ return mgr.getApiKey(profile);
2214
+ }
2215
+ function buildProxyHeaders(upstream, profile) {
2216
+ const headers = {
2217
+ "Content-Type": "application/json",
2218
+ Accept: "text/event-stream, application/json"
2219
+ };
2220
+ const apiKey = getApiKey(profile);
2221
+ if (apiKey) {
2222
+ headers["Authorization"] = `Bearer ${apiKey}`;
2223
+ }
2224
+ headers["host"] = new URL(upstream).host;
2225
+ return headers;
2226
+ }
2227
+ function loadSessionMessages(sessionId) {
2228
+ try {
2229
+ const db = getDb();
2230
+ const rows = db.query("SELECT role, content FROM messages WHERE session_id = ? ORDER BY timestamp").all(sessionId);
2231
+ return rows.filter((r) => r.role === "user" || r.role === "assistant").map((r) => ({
2232
+ role: r.role,
2233
+ content: r.content || ""
2234
+ })).filter((r) => r.content.trim().length > 0);
2235
+ } catch (err) {
2236
+ logger.warn("[gateway-chat] failed to load session messages", { session_id: sessionId, error: err.message });
2237
+ return [];
2238
+ }
2239
+ }
2240
+ async function createGatewayRun(payload, profile) {
2241
+ const upstream = getUpstream(profile);
2242
+ const url = `${upstream}/v1/runs`;
2243
+ const headers = buildProxyHeaders(upstream, profile);
2244
+ const bodyJson = {
2245
+ input: payload.input,
2246
+ model: payload.model,
2247
+ instructions: payload.instructions,
2248
+ session_id: payload.session_id
2249
+ };
2250
+ if (payload.session_id) {
2251
+ const history = loadSessionMessages(payload.session_id);
2252
+ if (history.length > 0) {
2253
+ bodyJson.conversation_history = history;
2254
+ }
2255
+ }
2256
+ const body = JSON.stringify(bodyJson);
2257
+ logger.info("[gateway-chat] POST /v1/runs", { session_id: payload.session_id, model: payload.model, profile });
2258
+ const res = await fetch(url, { method: "POST", headers, body });
2259
+ const responseBody = await res.json().catch(() => ({}));
2260
+ if (!res.ok) {
2261
+ throw new Error(`Gateway error ${res.status}: ${JSON.stringify(responseBody)}`);
2262
+ }
2263
+ const runId = responseBody.run_id;
2264
+ if (!runId) {
2265
+ throw new Error("Gateway returned no run_id");
2266
+ }
2267
+ if (payload.session_id) {
2268
+ setRunSession(runId, payload.session_id, payload.model, profile);
2269
+ }
2270
+ logger.info("[gateway-chat] run created", { run_id: runId });
2271
+ return { runId, responseBody };
2272
+ }
2273
+ function transformGatewayEvent(gatewayEvent, gatewayData, runId) {
2274
+ switch (gatewayEvent) {
2275
+ case "response.created": {
2276
+ const response = gatewayData.response || gatewayData;
2277
+ return {
2278
+ event: "run.started",
2279
+ data: {
2280
+ event: "run.started",
2281
+ run_id: runId,
2282
+ response_id: response.id || runId,
2283
+ status: response.status || "in_progress"
2284
+ }
2285
+ };
2286
+ }
2287
+ case "response.output_text.delta": {
2288
+ const delta = gatewayData.delta || gatewayData.text || "";
2289
+ if (!delta)
2290
+ return null;
2291
+ return {
2292
+ event: "message.delta",
2293
+ data: {
2294
+ event: "message.delta",
2295
+ run_id: runId,
2296
+ response_id: runId,
2297
+ delta
2298
+ }
2299
+ };
2300
+ }
2301
+ case "response.output_text.done":
2302
+ return null;
2303
+ case "response.reasoning_text.delta":
2304
+ case "reasoning.delta": {
2305
+ const delta = gatewayData.delta || gatewayData.text || "";
2306
+ if (!delta)
2307
+ return null;
2308
+ return {
2309
+ event: "reasoning.delta",
2310
+ data: {
2311
+ event: "reasoning.delta",
2312
+ run_id: runId,
2313
+ response_id: runId,
2314
+ delta
2315
+ }
2316
+ };
2317
+ }
2318
+ case "response.reasoning_item.done":
2319
+ case "reasoning.available":
2320
+ return {
2321
+ event: "reasoning.available",
2322
+ data: { event: "reasoning.available", run_id: runId, ...gatewayData }
2323
+ };
2324
+ case "response.function_call_arguments.delta":
2325
+ case "tool.started":
2326
+ return {
2327
+ event: "tool.started",
2328
+ data: { event: "tool.started", run_id: runId, ...gatewayData }
2329
+ };
2330
+ case "response.function_call_arguments.done":
2331
+ case "tool.completed":
2332
+ return {
2333
+ event: "tool.completed",
2334
+ data: { event: "tool.completed", run_id: runId, ...gatewayData }
2335
+ };
2336
+ case "run.completed": {
2337
+ const usage = gatewayData.usage || {};
2338
+ return {
2339
+ event: "run.completed",
2340
+ data: {
2341
+ event: "run.completed",
2342
+ run_id: runId,
2343
+ response_id: runId,
2344
+ status: "completed",
2345
+ output: gatewayData.output,
2346
+ usage: {
2347
+ input_tokens: usage.input_tokens || 0,
2348
+ output_tokens: usage.output_tokens || 0,
2349
+ cache_read_tokens: usage.cache_read_tokens,
2350
+ cache_write_tokens: usage.cache_write_tokens,
2351
+ reasoning_tokens: usage.reasoning_tokens
2352
+ }
2353
+ }
2354
+ };
2355
+ }
2356
+ case "error":
2357
+ case "run.failed":
2358
+ return {
2359
+ event: "run.error",
2360
+ data: {
2361
+ event: "run.error",
2362
+ run_id: runId,
2363
+ error: gatewayData.error?.message || String(gatewayData.error || "Unknown error")
2364
+ }
2365
+ };
2366
+ default:
2367
+ return {
2368
+ event: gatewayEvent,
2369
+ data: { event: gatewayEvent, run_id: runId, ...gatewayData }
2370
+ };
2371
+ }
2372
+ }
2373
+ async function* streamGatewayRun(runId, profile) {
2374
+ const upstream = getUpstream(profile);
2375
+ const url = `${upstream}/v1/runs/${runId}/events`;
2376
+ const headers = buildProxyHeaders(upstream, profile);
2377
+ logger.info("[gateway-chat] GET /v1/runs/{id}/events", { run_id: runId, url });
2378
+ const res = await fetch(url, { method: "GET", headers });
2379
+ if (!res.ok) {
2380
+ const body = await res.text().catch(() => "");
2381
+ throw new Error(`Gateway stream error ${res.status}: ${body}`);
2382
+ }
2383
+ if (!res.body) {
2384
+ throw new Error("Gateway stream has no body");
2385
+ }
2386
+ const reader = res.body.getReader();
2387
+ const decoder = new TextDecoder;
2388
+ let buffer = "";
2389
+ try {
2390
+ while (true) {
2391
+ const { done, value } = await reader.read();
2392
+ if (done)
2393
+ break;
2394
+ buffer += decoder.decode(value, { stream: true });
2395
+ let idx;
2396
+ while ((idx = buffer.indexOf(`
2397
+
2398
+ `)) !== -1) {
2399
+ const block = buffer.slice(0, idx);
2400
+ buffer = buffer.slice(idx + 2);
2401
+ const lines = block.split(`
2402
+ `);
2403
+ let eventName = "message";
2404
+ let dataJson = "";
2405
+ for (const line of lines) {
2406
+ if (line.startsWith("event: ")) {
2407
+ eventName = line.slice(7);
2408
+ } else if (line.startsWith("data: ")) {
2409
+ dataJson += line.slice(6);
2410
+ }
2411
+ }
2412
+ if (!dataJson)
2413
+ continue;
2414
+ try {
2415
+ const parsed = JSON.parse(dataJson);
2416
+ const transformed = transformGatewayEvent(eventName, parsed, runId);
2417
+ if (transformed) {
2418
+ yield {
2419
+ event: transformed.event,
2420
+ data: JSON.stringify(transformed.data)
2421
+ };
2422
+ }
2423
+ } catch {}
2424
+ }
2425
+ }
2426
+ if (buffer.trim()) {
2427
+ const lines = buffer.split(`
2428
+ `);
2429
+ let eventName = "message";
2430
+ let dataJson = "";
2431
+ for (const line of lines) {
2432
+ if (line.startsWith("event: "))
2433
+ eventName = line.slice(7);
2434
+ else if (line.startsWith("data: "))
2435
+ dataJson += line.slice(6);
2436
+ }
2437
+ if (dataJson) {
2438
+ try {
2439
+ const parsed = JSON.parse(dataJson);
2440
+ const transformed = transformGatewayEvent(eventName, parsed, runId);
2441
+ if (transformed) {
2442
+ yield {
2443
+ event: transformed.event,
2444
+ data: JSON.stringify(transformed.data)
2445
+ };
2446
+ }
2447
+ } catch {}
2448
+ }
2449
+ }
2450
+ } finally {
2451
+ reader.releaseLock();
2452
+ }
2453
+ }
2454
+
2455
+ // src/controllers/hermes/chat.ts
2456
+ var runEventBuffers = new Map;
2457
+ var MAX_BUFFER_AGE_MS = 5 * 60 * 1000;
2458
+ var MAX_BUFFER_SIZE = 500;
2459
+ function bufferEvent(runId, event, data) {
2460
+ let buffer = runEventBuffers.get(runId);
2461
+ if (!buffer) {
2462
+ buffer = [];
2463
+ runEventBuffers.set(runId, buffer);
2464
+ }
2465
+ const id = `${runId}-${buffer.length}`;
2466
+ buffer.push({ id, event, data, ts: Date.now() });
2467
+ if (buffer.length > MAX_BUFFER_SIZE) {
2468
+ buffer.splice(0, buffer.length - MAX_BUFFER_SIZE);
2469
+ }
2470
+ const now = Date.now();
2471
+ for (const [rid, buf] of runEventBuffers.entries()) {
2472
+ if (rid === runId)
2473
+ continue;
2474
+ if (buf.length > 0 && now - buf[buf.length - 1].ts > MAX_BUFFER_AGE_MS) {
2475
+ runEventBuffers.delete(rid);
2476
+ }
2477
+ }
2478
+ return id;
2479
+ }
2480
+ function getBufferedEvents(runId, lastEventId) {
2481
+ const buffer = runEventBuffers.get(runId);
2482
+ if (!buffer)
2483
+ return [];
2484
+ if (!lastEventId)
2485
+ return [...buffer];
2486
+ const idx = buffer.findIndex((e) => e.id === lastEventId);
2487
+ if (idx === -1)
2488
+ return [...buffer];
2489
+ return buffer.slice(idx + 1);
2490
+ }
2491
+ async function runChat(c) {
2492
+ const body = await c.req.json();
2493
+ const profile = c.req.header("x-hermes-profile") || "default";
2494
+ const payload = {
2495
+ ...body,
2496
+ input: body.input
2497
+ };
2498
+ try {
2499
+ const { runId } = await createGatewayRun(payload, profile);
2500
+ return c.json({ run_id: runId, status: "queued" });
2501
+ } catch (err) {
2502
+ const msg = err.message || String(err);
2503
+ if (msg.includes("ConnectionRefused") || msg.includes("ECONNREFUSED") || msg.includes("Unable to connect")) {
2504
+ return c.json({ error: { message: "Hermes Gateway is not running. Start it with: hermes gateway run" } }, 503);
2505
+ }
2506
+ return c.json({ error: { message: msg } }, 502);
2507
+ }
2508
+ }
2509
+ async function abortChat(c) {
2510
+ const body = await c.req.json();
2511
+ return c.json({ ok: false });
2512
+ }
2513
+ function streamChat(c) {
2514
+ const runId = c.req.query("run_id");
2515
+ if (!runId) {
2516
+ return c.json({ error: { message: "Missing run_id" } }, 400);
2517
+ }
2518
+ const profile = c.req.header("x-hermes-profile") || "default";
2519
+ const lastEventId = c.req.header("last-event-id") || c.req.query("last_event_id") || undefined;
2520
+ const stream = new ReadableStream({
2521
+ async start(ctrl) {
2522
+ const encoder = new TextEncoder;
2523
+ let closed = false;
2524
+ const sendRaw = (event, data, id) => {
2525
+ if (closed)
2526
+ return;
2527
+ let payload = "";
2528
+ if (id)
2529
+ payload += `id: ${id}
2530
+ `;
2531
+ payload += `event: ${event}
2532
+ data: ${JSON.stringify(data)}
2533
+
2534
+ `;
2535
+ ctrl.enqueue(encoder.encode(payload));
2536
+ };
2537
+ const send = (event, data) => {
2538
+ const id = bufferEvent(runId, event, data);
2539
+ sendRaw(event, data, id);
2540
+ if (event === "run.completed") {
2541
+ try {
2542
+ const usage = data.usage || {};
2543
+ const sessionInfo = getSessionForRun(runId);
2544
+ if (sessionInfo && usage.input_tokens !== undefined) {
2545
+ const db = getDb();
2546
+ db.run(`INSERT INTO usage (session_id, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, reasoning_tokens, model, profile, timestamp)
2547
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
2548
+ sessionInfo.sessionId,
2549
+ usage.input_tokens || 0,
2550
+ usage.output_tokens || 0,
2551
+ usage.cache_read_tokens || 0,
2552
+ usage.cache_write_tokens || 0,
2553
+ usage.reasoning_tokens || 0,
2554
+ sessionInfo.model || null,
2555
+ sessionInfo.profile || "default",
2556
+ Date.now()
2557
+ ]);
2558
+ logger.info("[usage] recorded", {
2559
+ run_id: runId,
2560
+ session_id: sessionInfo.sessionId,
2561
+ input: usage.input_tokens,
2562
+ output: usage.output_tokens
2563
+ });
2564
+ }
2565
+ } catch (err) {
2566
+ logger.error("[usage] failed to record", { run_id: runId, error: err.message });
2567
+ }
2568
+ }
2569
+ };
2570
+ const buffered = getBufferedEvents(runId, lastEventId);
2571
+ if (buffered.length > 0) {
2572
+ logger.info("[gateway-chat] Replaying buffered events", { run_id: runId, count: buffered.length });
2573
+ for (const be of buffered) {
2574
+ sendRaw(be.event, be.data, be.id);
2575
+ }
2576
+ }
2577
+ const heartbeat = setInterval(() => {
2578
+ if (closed)
2579
+ return;
2580
+ sendRaw("ping", { event: "ping", run_id: runId });
2581
+ }, 15000);
2582
+ try {
2583
+ for await (const chunk of streamGatewayRun(runId, profile)) {
2584
+ if (closed)
2585
+ break;
2586
+ send(chunk.event, JSON.parse(chunk.data));
2587
+ }
2588
+ } catch (err) {
2589
+ const msg = err.message || String(err);
2590
+ logger.error("[gateway-chat] Stream error", { run_id: runId, error: msg });
2591
+ if (msg.includes("ConnectionRefused") || msg.includes("ECONNREFUSED") || msg.includes("Unable to connect")) {
2592
+ send("run.error", {
2593
+ event: "run.error",
2594
+ run_id: runId,
2595
+ error: "Hermes Gateway is not running. Start it with: hermes gateway run"
2596
+ });
2597
+ } else {
2598
+ send("run.error", {
2599
+ event: "run.error",
2600
+ run_id: runId,
2601
+ error: msg
2602
+ });
2603
+ }
2604
+ } finally {
2605
+ closed = true;
2606
+ clearInterval(heartbeat);
2607
+ ctrl.close();
2608
+ }
2609
+ }
2610
+ });
2611
+ return c.body(stream, {
2612
+ headers: {
2613
+ "Content-Type": "text/event-stream",
2614
+ "Cache-Control": "no-cache",
2615
+ Connection: "keep-alive",
2616
+ "X-Accel-Buffering": "no"
2617
+ }
2618
+ });
2619
+ }
2620
+
2621
+ // src/routes/hermes/chat.ts
2622
+ var app3 = new Hono2;
2623
+ app3.post("/api/hermes/chat/run", runChat);
2624
+ app3.post("/api/hermes/chat/abort", abortChat);
2625
+ app3.get("/api/hermes/chat/stream", streamChat);
2626
+
2627
+ // src/routes/hermes/files.ts
2628
+ import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
2629
+ import { join as join3 } from "path";
2630
+ var app4 = new Hono2;
2631
+ app4.get("/api/hermes/files/*", (c) => c.json({ files: [] }));
2632
+ app4.post("/api/hermes/files/upload", async (c) => {
2633
+ try {
2634
+ const formData = await c.req.formData();
2635
+ const files = formData.getAll("file");
2636
+ const targetDir = c.req.query("path") || "uploads";
2637
+ const uploadDir = join3(config.uploadDir, targetDir);
2638
+ await mkdir2(uploadDir, { recursive: true });
2639
+ const uploaded = [];
2640
+ for (const file of files) {
2641
+ const bytes = await file.arrayBuffer();
2642
+ const filePath = join3(uploadDir, file.name);
2643
+ await writeFile2(filePath, Buffer.from(bytes));
2644
+ const relativePath = join3(targetDir, file.name);
2645
+ uploaded.push({ name: file.name, path: relativePath });
2646
+ }
2647
+ return c.json({ files: uploaded });
2648
+ } catch (err) {
2649
+ return c.json({ error: err.message }, 500);
2650
+ }
2651
+ });
2652
+ app4.delete("/api/hermes/files/*", (c) => c.json({ ok: true }));
2653
+
2654
+ // src/routes/hermes/download.ts
2655
+ import { readFile as readFile2 } from "fs/promises";
2656
+ import { join as join4, basename } from "path";
2657
+ import { existsSync } from "fs";
2658
+ var app5 = new Hono2;
2659
+ app5.get("/api/hermes/download", async (c) => {
2660
+ const path = c.req.query("path");
2661
+ const name = c.req.query("name");
2662
+ if (!path) {
2663
+ return c.json({ error: "Missing path" }, 400);
2664
+ }
2665
+ const filePath = join4(config.uploadDir, path);
2666
+ if (!existsSync(filePath)) {
2667
+ return c.json({ error: "File not found" }, 404);
2668
+ }
2669
+ try {
2670
+ const buffer = await readFile2(filePath);
2671
+ const fileName = name || basename(path);
2672
+ c.header("Content-Disposition", `inline; filename="${encodeURIComponent(fileName)}"`);
2673
+ return c.body(buffer);
2674
+ } catch (err) {
2675
+ return c.json({ error: err.message }, 500);
2676
+ }
2677
+ });
2678
+
2679
+ // src/routes/hermes/models.ts
2680
+ var app6 = new Hono2;
2681
+ app6.get("/api/hermes/models", (c) => c.json({ models: [] }));
2682
+ app6.get("/api/hermes/models/discover", (c) => c.json({ models: [] }));
2683
+
2684
+ // src/routes/hermes/providers.ts
2685
+ var app7 = new Hono2;
2686
+ app7.get("/api/hermes/providers", (c) => c.json({ providers: [] }));
2687
+ app7.post("/api/hermes/providers", (c) => c.json({ ok: true }));
2688
+ app7.patch("/api/hermes/providers/:id", (c) => c.json({ ok: true }));
2689
+ app7.delete("/api/hermes/providers/:id", (c) => c.json({ ok: true }));
2690
+
2691
+ // src/routes/hermes/profiles.ts
2692
+ var app8 = new Hono2;
2693
+ app8.get("/api/hermes/profiles", (c) => c.json({ profiles: [], active: "default" }));
2694
+ app8.post("/api/hermes/profiles", (c) => c.json({ ok: true }));
2695
+ app8.post("/api/hermes/profiles/:name/activate", (c) => c.json({ ok: true }));
2696
+ app8.delete("/api/hermes/profiles/:name", (c) => c.json({ ok: true }));
2697
+
2698
+ // src/routes/hermes/gateways.ts
2699
+ var app9 = new Hono2;
2700
+ app9.get("/api/hermes/gateways", (c) => c.json({ gateways: [] }));
2701
+ app9.post("/api/hermes/gateways/:profile/start", (c) => c.json({ ok: true }));
2702
+ app9.post("/api/hermes/gateways/:profile/stop", (c) => c.json({ ok: true }));
2703
+
2704
+ // src/routes/hermes/jobs.ts
2705
+ var app10 = new Hono2;
2706
+ app10.get("/api/hermes/jobs", (c) => c.json({ jobs: [] }));
2707
+ app10.post("/api/hermes/jobs", (c) => c.json({ ok: true }));
2708
+ app10.patch("/api/hermes/jobs/:id", (c) => c.json({ ok: true }));
2709
+ app10.delete("/api/hermes/jobs/:id", (c) => c.json({ ok: true }));
2710
+ app10.post("/api/hermes/jobs/:id/run", (c) => c.json({ ok: true }));
2711
+
2712
+ // src/routes/hermes/logs.ts
2713
+ var app11 = new Hono2;
2714
+ app11.get("/api/hermes/logs", (c) => c.json({ logs: [] }));
2715
+
2716
+ // src/routes/hermes/config.ts
2717
+ var app12 = new Hono2;
2718
+ app12.get("/api/hermes/config", (c) => c.json({ config: {} }));
2719
+ app12.put("/api/hermes/config", (c) => c.json({ ok: true }));
2720
+
2721
+ // src/controllers/hermes/proxy.ts
2722
+ function resolveProfile(c) {
2723
+ return c.req.header("x-hermes-profile") || c.req.query("profile") || "default";
2724
+ }
2725
+ function buildProxyHeaders2(c, upstream) {
2726
+ const headers = {};
2727
+ const skip = new Set(["host", "origin", "referer", "connection", "authorization"]);
2728
+ c.req.raw.headers.forEach((value, key) => {
2729
+ const lower = key.toLowerCase();
2730
+ if (lower === "host") {
2731
+ headers["host"] = new URL(upstream).host;
2732
+ } else if (!skip.has(lower)) {
2733
+ headers[key] = value;
2734
+ }
2735
+ });
2736
+ const mgr = getGatewayManagerInstance();
2737
+ const apiKey = mgr.getApiKey(resolveProfile(c));
2738
+ if (apiKey) {
2739
+ headers["authorization"] = `Bearer ${apiKey}`;
2740
+ }
2741
+ return headers;
2742
+ }
2743
+ async function proxyToGateway(c, next) {
2744
+ const profile = resolveProfile(c);
2745
+ const mgr = getGatewayManagerInstance();
2746
+ const upstream = mgr.getUpstream(profile);
2747
+ const originalPath = c.req.path;
2748
+ const upstreamPath = originalPath.replace(/^\/api\/hermes\/v1/, "/v1").replace(/^\/api\/hermes/, "/api");
2749
+ const url = new URL(upstreamPath, upstream);
2750
+ const originalQuery = new URL(c.req.url).searchParams;
2751
+ originalQuery.forEach((value, key) => {
2752
+ if (key !== "token") {
2753
+ url.searchParams.append(key, value);
2754
+ }
2755
+ });
2756
+ const headers = buildProxyHeaders2(c, upstream);
2757
+ const method = c.req.method;
2758
+ let body;
2759
+ if (method !== "GET" && method !== "HEAD") {
2760
+ const cloned = c.req.raw.clone();
2761
+ if (cloned.body) {
2762
+ body = cloned.body;
2763
+ }
2764
+ }
2765
+ try {
2766
+ logger.info(`Proxy ${method} ${originalPath} \u2192 ${url.toString()}`);
2767
+ const res = await fetch(url.toString(), { method, headers, body });
2768
+ c.status(res.status);
2769
+ res.headers.forEach((value, key) => {
2770
+ const lower = key.toLowerCase();
2771
+ if (lower !== "transfer-encoding" && lower !== "connection") {
2772
+ c.header(key, value);
2773
+ }
2774
+ });
2775
+ if (!res.body) {
2776
+ return c.body(null);
2777
+ }
2778
+ return c.body(res.body);
2779
+ } catch (err) {
2780
+ logger.error("Proxy error", { error: err.message, path: originalPath });
2781
+ return c.json({ error: { message: `Proxy error: ${err.message}` } }, 502);
2782
+ }
2783
+ }
2784
+
2785
+ // src/routes/hermes/proxy.ts
2786
+ var app13 = new Hono2;
2787
+ app13.all("/api/hermes/v1/*", proxyToGateway);
2788
+
2789
+ // src/controllers/hermes/memory.ts
2790
+ import { readFile as readFile3, stat, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
2791
+ import { join as join5, resolve as resolve2 } from "path";
2792
+ import { homedir as homedir2 } from "os";
2793
+ function getHermesDir() {
2794
+ if (process.env.HERMES_HOME) {
2795
+ return resolve2(process.env.HERMES_HOME);
2796
+ }
2797
+ return resolve2(homedir2(), ".hermes");
2798
+ }
2799
+ var hermesDir = getHermesDir();
2800
+ var memoriesDir = join5(hermesDir, "memories");
2801
+ var soulPath = join5(hermesDir, "SOUL.md");
2802
+ async function safeReadFile(path) {
2803
+ try {
2804
+ return await readFile3(path, "utf-8");
2805
+ } catch {
2806
+ return null;
2807
+ }
2808
+ }
2809
+ async function safeStat(path) {
2810
+ try {
2811
+ return await stat(path);
2812
+ } catch {
2813
+ return null;
2814
+ }
2815
+ }
2816
+ async function getMemory(c) {
2817
+ await mkdir3(memoriesDir, { recursive: true });
2818
+ const memoryPath = join5(memoriesDir, "MEMORY.md");
2819
+ const userPath = join5(memoriesDir, "USER.md");
2820
+ const [memory, user, soul, memoryStat, userStat, soulStat] = await Promise.all([
2821
+ safeReadFile(memoryPath),
2822
+ safeReadFile(userPath),
2823
+ safeReadFile(soulPath),
2824
+ safeStat(memoryPath),
2825
+ safeStat(userPath),
2826
+ safeStat(soulPath)
2827
+ ]);
2828
+ return c.json({
2829
+ memory: memory || "",
2830
+ user: user || "",
2831
+ soul: soul || "",
2832
+ memory_mtime: memoryStat?.mtime ? memoryStat.mtime.getTime() : null,
2833
+ user_mtime: userStat?.mtime ? userStat.mtime.getTime() : null,
2834
+ soul_mtime: soulStat?.mtime ? soulStat.mtime.getTime() : null
2835
+ });
2836
+ }
2837
+ async function saveMemory(c) {
2838
+ const body = await c.req.json();
2839
+ if (!body.section || body.content === undefined) {
2840
+ return c.json({ error: "Missing section or content" }, 400);
2841
+ }
2842
+ if (body.section !== "memory" && body.section !== "user" && body.section !== "soul") {
2843
+ return c.json({ error: 'Section must be "memory", "user", or "soul"' }, 400);
2844
+ }
2845
+ let filePath;
2846
+ if (body.section === "soul") {
2847
+ filePath = soulPath;
2848
+ } else {
2849
+ await mkdir3(memoriesDir, { recursive: true });
2850
+ const fileName = body.section === "memory" ? "MEMORY.md" : "USER.md";
2851
+ filePath = join5(memoriesDir, fileName);
2852
+ }
2853
+ try {
2854
+ await writeFile3(filePath, body.content, "utf-8");
2855
+ return c.json({ success: true });
2856
+ } catch (err) {
2857
+ return c.json({ error: err.message }, 500);
2858
+ }
2859
+ }
2860
+
2861
+ // src/routes/hermes/memory.ts
2862
+ var memoryRoutes = new Hono2;
2863
+ memoryRoutes.get("/api/hermes/memory", getMemory);
2864
+ memoryRoutes.post("/api/hermes/memory", saveMemory);
2865
+
2866
+ // src/controllers/hermes/usage-stats.ts
2867
+ import { Database as Database2 } from "bun:sqlite";
2868
+ import { resolve as resolve3, join as join6 } from "path";
2869
+ import { homedir as homedir3 } from "os";
2870
+ import { existsSync as existsSync2, readFileSync } from "fs";
2871
+ function getHermesStateDbPath() {
2872
+ const hermesHome = process.env.HERMES_HOME ? resolve3(process.env.HERMES_HOME) : resolve3(homedir3(), ".hermes");
2873
+ const activeProfileFile = join6(hermesHome, "active_profile");
2874
+ let profileDir = hermesHome;
2875
+ try {
2876
+ const name = readFileSync(activeProfileFile, "utf-8").trim();
2877
+ if (name && name !== "default") {
2878
+ const dir = join6(hermesHome, "profiles", name);
2879
+ if (existsSync2(dir))
2880
+ profileDir = dir;
2881
+ }
2882
+ } catch {}
2883
+ const stateDb = join6(profileDir, "state.db");
2884
+ return existsSync2(stateDb) ? stateDb : null;
2885
+ }
2886
+ function queryWebUiDb(cutoff) {
2887
+ const empty = {
2888
+ input_tokens: 0,
2889
+ output_tokens: 0,
2890
+ cache_read_tokens: 0,
2891
+ cache_write_tokens: 0,
2892
+ sessions: 0,
2893
+ by_model: new Map,
2894
+ by_day: new Map
2895
+ };
2896
+ try {
2897
+ const db = getDb();
2898
+ const totals = db.query(`
2899
+ SELECT
2900
+ COALESCE(SUM(input_tokens), 0) as input_tokens,
2901
+ COALESCE(SUM(output_tokens), 0) as output_tokens,
2902
+ COALESCE(SUM(cache_read_tokens), 0) as cache_read_tokens,
2903
+ COALESCE(SUM(cache_write_tokens), 0) as cache_write_tokens,
2904
+ COUNT(DISTINCT session_id) as sessions
2905
+ FROM usage
2906
+ WHERE timestamp > ?
2907
+ `).get(cutoff);
2908
+ const byModel = db.query(`
2909
+ SELECT
2910
+ COALESCE(model, 'unknown') as model,
2911
+ COALESCE(SUM(input_tokens), 0) as input_tokens,
2912
+ COALESCE(SUM(output_tokens), 0) as output_tokens,
2913
+ COALESCE(SUM(cache_read_tokens), 0) as cache_read_tokens,
2914
+ COALESCE(SUM(cache_write_tokens), 0) as cache_write_tokens,
2915
+ COUNT(DISTINCT session_id) as sessions
2916
+ FROM usage
2917
+ WHERE timestamp > ?
2918
+ GROUP BY model
2919
+ `).all(cutoff);
2920
+ const byDay = db.query(`
2921
+ SELECT
2922
+ DATE(timestamp / 1000, 'unixepoch') as date,
2923
+ COALESCE(SUM(input_tokens), 0) as input_tokens,
2924
+ COALESCE(SUM(output_tokens), 0) as output_tokens,
2925
+ COALESCE(SUM(cache_read_tokens), 0) as cache_read_tokens,
2926
+ COUNT(DISTINCT session_id) as sessions
2927
+ FROM usage
2928
+ WHERE timestamp > ?
2929
+ GROUP BY date
2930
+ `).all(cutoff);
2931
+ const result = {
2932
+ input_tokens: totals.input_tokens,
2933
+ output_tokens: totals.output_tokens,
2934
+ cache_read_tokens: totals.cache_read_tokens,
2935
+ cache_write_tokens: totals.cache_write_tokens,
2936
+ sessions: totals.sessions,
2937
+ by_model: new Map,
2938
+ by_day: new Map
2939
+ };
2940
+ for (const m of byModel) {
2941
+ result.by_model.set(m.model, { ...m });
2942
+ }
2943
+ for (const d of byDay) {
2944
+ result.by_day.set(d.date, {
2945
+ date: d.date,
2946
+ input_tokens: d.input_tokens,
2947
+ output_tokens: d.output_tokens,
2948
+ cache_read_tokens: d.cache_read_tokens,
2949
+ sessions: d.sessions,
2950
+ errors: 0,
2951
+ cost: 0
2952
+ });
2953
+ }
2954
+ return result;
2955
+ } catch (err) {
2956
+ logger.warn("[usage-stats] web-ui db query failed:", err.message);
2957
+ return empty;
2958
+ }
2959
+ }
2960
+ function queryHermesStateDb(dbPath, sinceSeconds) {
2961
+ const empty = {
2962
+ input_tokens: 0,
2963
+ output_tokens: 0,
2964
+ cache_read_tokens: 0,
2965
+ cache_write_tokens: 0,
2966
+ sessions: 0,
2967
+ by_model: new Map,
2968
+ by_day: new Map
2969
+ };
2970
+ try {
2971
+ const db = new Database2(dbPath, { readonly: true });
2972
+ const tableCheck = db.query(`SELECT name FROM sqlite_master WHERE type='table' AND name='sessions'`).get();
2973
+ if (!tableCheck) {
2974
+ db.close();
2975
+ return empty;
2976
+ }
2977
+ const totals = db.query(`
2978
+ SELECT
2979
+ COALESCE(SUM(input_tokens), 0) as input_tokens,
2980
+ COALESCE(SUM(output_tokens), 0) as output_tokens,
2981
+ COALESCE(SUM(cache_read_tokens), 0) as cache_read_tokens,
2982
+ COALESCE(SUM(cache_write_tokens), 0) as cache_write_tokens,
2983
+ COUNT(*) as sessions
2984
+ FROM sessions
2985
+ WHERE started_at > ?
2986
+ `).get(sinceSeconds);
2987
+ const byModel = db.query(`
2988
+ SELECT
2989
+ COALESCE(model, '') as model,
2990
+ COALESCE(SUM(input_tokens), 0) as input_tokens,
2991
+ COALESCE(SUM(output_tokens), 0) as output_tokens,
2992
+ COALESCE(SUM(cache_read_tokens), 0) as cache_read_tokens,
2993
+ COALESCE(SUM(cache_write_tokens), 0) as cache_write_tokens,
2994
+ COUNT(*) as sessions
2995
+ FROM sessions
2996
+ WHERE started_at > ? AND model IS NOT NULL
2997
+ GROUP BY model
2998
+ `).all(sinceSeconds);
2999
+ const byDay = db.query(`
3000
+ SELECT
3001
+ date(started_at, 'unixepoch') as date,
3002
+ COALESCE(SUM(input_tokens), 0) as input_tokens,
3003
+ COALESCE(SUM(output_tokens), 0) as output_tokens,
3004
+ COALESCE(SUM(cache_read_tokens), 0) as cache_read_tokens,
3005
+ COUNT(*) as sessions
3006
+ FROM sessions
3007
+ WHERE started_at > ?
3008
+ GROUP BY date
3009
+ `).all(sinceSeconds);
3010
+ db.close();
3011
+ const result = {
3012
+ input_tokens: totals.input_tokens,
3013
+ output_tokens: totals.output_tokens,
3014
+ cache_read_tokens: totals.cache_read_tokens,
3015
+ cache_write_tokens: totals.cache_write_tokens,
3016
+ sessions: totals.sessions,
3017
+ by_model: new Map,
3018
+ by_day: new Map
3019
+ };
3020
+ for (const m of byModel) {
3021
+ result.by_model.set(m.model || "unknown", { ...m, model: m.model || "unknown" });
3022
+ }
3023
+ for (const d of byDay) {
3024
+ result.by_day.set(d.date, {
3025
+ date: d.date,
3026
+ input_tokens: d.input_tokens,
3027
+ output_tokens: d.output_tokens,
3028
+ cache_read_tokens: d.cache_read_tokens,
3029
+ sessions: d.sessions,
3030
+ errors: 0,
3031
+ cost: 0
3032
+ });
3033
+ }
3034
+ return result;
3035
+ } catch (err) {
3036
+ logger.warn("[usage-stats] hermes state.db query failed:", err.message);
3037
+ return empty;
3038
+ }
3039
+ }
3040
+ function mergeStats(a, b) {
3041
+ const merged = {
3042
+ input_tokens: a.input_tokens + b.input_tokens,
3043
+ output_tokens: a.output_tokens + b.output_tokens,
3044
+ cache_read_tokens: a.cache_read_tokens + b.cache_read_tokens,
3045
+ cache_write_tokens: a.cache_write_tokens + b.cache_write_tokens,
3046
+ sessions: a.sessions + b.sessions,
3047
+ by_model: new Map,
3048
+ by_day: new Map
3049
+ };
3050
+ for (const [model, stats] of a.by_model) {
3051
+ merged.by_model.set(model, { ...stats });
3052
+ }
3053
+ for (const [model, stats] of b.by_model) {
3054
+ const existing = merged.by_model.get(model);
3055
+ if (existing) {
3056
+ existing.input_tokens += stats.input_tokens;
3057
+ existing.output_tokens += stats.output_tokens;
3058
+ existing.cache_read_tokens += stats.cache_read_tokens;
3059
+ existing.cache_write_tokens += stats.cache_write_tokens;
3060
+ existing.sessions += stats.sessions;
3061
+ } else {
3062
+ merged.by_model.set(model, { ...stats });
3063
+ }
3064
+ }
3065
+ for (const [date, stats] of a.by_day) {
3066
+ merged.by_day.set(date, { ...stats });
3067
+ }
3068
+ for (const [date, stats] of b.by_day) {
3069
+ const existing = merged.by_day.get(date);
3070
+ if (existing) {
3071
+ existing.input_tokens += stats.input_tokens;
3072
+ existing.output_tokens += stats.output_tokens;
3073
+ existing.cache_read_tokens += stats.cache_read_tokens;
3074
+ existing.sessions += stats.sessions;
3075
+ } else {
3076
+ merged.by_day.set(date, { ...stats });
3077
+ }
3078
+ }
3079
+ return merged;
3080
+ }
3081
+ async function getUsageStats(c) {
3082
+ const rawDays = parseInt(c.req.query("days") || "30", 10);
3083
+ const days = Number.isFinite(rawDays) && rawDays > 0 ? Math.min(rawDays, 365) : 30;
3084
+ const cutoffMs = Date.now() - days * 24 * 60 * 60 * 1000;
3085
+ const cutoffSeconds = Math.floor(cutoffMs / 1000);
3086
+ const webUiStats = queryWebUiDb(cutoffMs);
3087
+ const hermesDbPath = getHermesStateDbPath();
3088
+ const hermesStats = hermesDbPath ? queryHermesStateDb(hermesDbPath, cutoffSeconds) : null;
3089
+ const merged = hermesStats ? mergeStats(webUiStats, hermesStats) : webUiStats;
3090
+ const dayMap = new Map;
3091
+ const now = new Date;
3092
+ for (let i = days - 1;i >= 0; i--) {
3093
+ const d = new Date(now);
3094
+ d.setDate(d.getDate() - i);
3095
+ const key = d.toISOString().slice(0, 10);
3096
+ dayMap.set(key, { date: key, input_tokens: 0, output_tokens: 0, cache_read_tokens: 0, sessions: 0, errors: 0, cost: 0 });
3097
+ }
3098
+ for (const d of merged.by_day.values()) {
3099
+ const existing = dayMap.get(d.date);
3100
+ if (existing) {
3101
+ existing.input_tokens = d.input_tokens;
3102
+ existing.output_tokens = d.output_tokens;
3103
+ existing.cache_read_tokens = d.cache_read_tokens;
3104
+ existing.sessions = d.sessions;
3105
+ }
3106
+ }
3107
+ const modelUsage = [...merged.by_model.values()].sort((a, b) => b.input_tokens + b.output_tokens - (a.input_tokens + a.output_tokens));
3108
+ return c.json({
3109
+ total_input_tokens: merged.input_tokens,
3110
+ total_output_tokens: merged.output_tokens,
3111
+ total_cache_read_tokens: merged.cache_read_tokens,
3112
+ total_cache_write_tokens: merged.cache_write_tokens,
3113
+ total_sessions: merged.sessions,
3114
+ period_days: days,
3115
+ model_usage: modelUsage,
3116
+ daily_usage: [...dayMap.values()]
3117
+ });
3118
+ }
3119
+
3120
+ // src/routes/hermes/usage.ts
3121
+ var usageRoutes = new Hono2;
3122
+ usageRoutes.get("/api/hermes/usage/stats", getUsageStats);
3123
+
3124
+ // src/controllers/hermes/skills.ts
3125
+ import { readdir, readFile as readFile4, stat as stat2 } from "fs/promises";
3126
+ import { join as join7, resolve as resolve4 } from "path";
3127
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
3128
+ import { homedir as homedir4 } from "os";
3129
+ function getHermesDir2() {
3130
+ if (process.env.HERMES_HOME)
3131
+ return resolve4(process.env.HERMES_HOME);
3132
+ return resolve4(homedir4(), ".hermes");
3133
+ }
3134
+ async function safeReadFile2(path) {
3135
+ try {
3136
+ const s = await stat2(path);
3137
+ if (!s.isFile())
3138
+ return null;
3139
+ return await readFile4(path, "utf-8");
3140
+ } catch {
3141
+ return null;
3142
+ }
3143
+ }
3144
+ function extractDescription(md) {
3145
+ const lines = md.split(`
3146
+ `);
3147
+ for (const line of lines) {
3148
+ const trimmed = line.trim();
3149
+ if (!trimmed)
3150
+ continue;
3151
+ if (trimmed.startsWith("#")) {
3152
+ const title = trimmed.replace(/^#+\s*/, "").trim();
3153
+ if (title)
3154
+ return title;
3155
+ }
3156
+ if (trimmed.length > 5)
3157
+ return trimmed.slice(0, 120);
3158
+ }
3159
+ return "";
3160
+ }
3161
+ async function listFilesRecursive(dir, prefix) {
3162
+ const results = [];
3163
+ try {
3164
+ const entries = await readdir(dir, { withFileTypes: true });
3165
+ for (const entry of entries) {
3166
+ if (entry.name.startsWith("."))
3167
+ continue;
3168
+ const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
3169
+ if (entry.isDirectory()) {
3170
+ results.push({ path: relPath, name: entry.name, isDir: true });
3171
+ results.push(...await listFilesRecursive(join7(dir, entry.name), relPath));
3172
+ } else {
3173
+ results.push({ path: relPath, name: entry.name, isDir: false });
3174
+ }
3175
+ }
3176
+ } catch {}
3177
+ return results;
3178
+ }
3179
+ function getSkillSource(dirName, bundled, hub) {
3180
+ if (bundled.has(dirName))
3181
+ return "builtin";
3182
+ if (hub.has(dirName))
3183
+ return "hub";
3184
+ return "local";
3185
+ }
3186
+ function readBundledManifest(content) {
3187
+ const set = new Set;
3188
+ if (!content)
3189
+ return set;
3190
+ for (const line of content.split(`
3191
+ `)) {
3192
+ const trimmed = line.trim();
3193
+ if (!trimmed)
3194
+ continue;
3195
+ const idx = trimmed.indexOf(":");
3196
+ if (idx === -1)
3197
+ continue;
3198
+ const name = trimmed.slice(0, idx).trim();
3199
+ if (name)
3200
+ set.add(name);
3201
+ }
3202
+ return set;
3203
+ }
3204
+ function readHubInstalled(content) {
3205
+ if (!content)
3206
+ return new Set;
3207
+ try {
3208
+ const data = JSON.parse(content);
3209
+ if (data?.installed && typeof data.installed === "object") {
3210
+ return new Set(Object.keys(data.installed));
3211
+ }
3212
+ } catch {}
3213
+ return new Set;
3214
+ }
3215
+ function readUsageStats(content) {
3216
+ const map = new Map;
3217
+ if (!content)
3218
+ return map;
3219
+ try {
3220
+ const data = JSON.parse(content);
3221
+ for (const [name, stats] of Object.entries(data)) {
3222
+ const s = stats;
3223
+ map.set(name, { patch_count: s.patch_count ?? 0, use_count: s.use_count ?? 0, view_count: s.view_count ?? 0, pinned: !!s.pinned });
3224
+ }
3225
+ } catch {}
3226
+ return map;
3227
+ }
3228
+ function readConfigDisabled(skillsDir) {
3229
+ try {
3230
+ const configPath = join7(skillsDir, "..", "config.yaml");
3231
+ const content = readFileSync2(configPath, "utf-8");
3232
+ const match2 = content.match(/disabled:\s*\n((\s+-\s+[^\n]+\n)+)/);
3233
+ if (match2) {
3234
+ return match2[1].split(`
3235
+ `).filter((l) => l.trim().startsWith("-")).map((l) => l.trim().slice(1).trim());
3236
+ }
3237
+ } catch {}
3238
+ return [];
3239
+ }
3240
+ async function listSkills(c) {
3241
+ const hermesDir2 = getHermesDir2();
3242
+ const skillsDir = join7(hermesDir2, "skills");
3243
+ try {
3244
+ const disabledList = readConfigDisabled(skillsDir);
3245
+ const bundledManifest = readBundledManifest(await safeReadFile2(join7(skillsDir, ".bundled_manifest")));
3246
+ const hubNames = readHubInstalled(await safeReadFile2(join7(skillsDir, ".hub", "lock.json")));
3247
+ const usageStats = readUsageStats(await safeReadFile2(join7(skillsDir, ".usage.json")));
3248
+ const allEntries = await readdir(skillsDir, { withFileTypes: true }).catch(() => []);
3249
+ const dirNames = allEntries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
3250
+ const categories = [];
3251
+ const flatSkills = [];
3252
+ for (const dirName of dirNames) {
3253
+ const catDir = join7(skillsDir, dirName);
3254
+ const hasDesc = await safeReadFile2(join7(catDir, "DESCRIPTION.md"));
3255
+ const hasSkillMd = await safeReadFile2(join7(catDir, "SKILL.md"));
3256
+ const subEntries = await readdir(catDir, { withFileTypes: true }).catch(() => []);
3257
+ const subDirs = subEntries.filter((se) => se.isDirectory() && !se.name.startsWith("."));
3258
+ if (hasSkillMd) {
3259
+ flatSkills.push({ name: dirName, skillMd: hasSkillMd, source: getSkillSource(dirName, bundledManifest, hubNames) });
3260
+ } else if (hasDesc || subDirs.length > 0) {
3261
+ const desc = hasDesc ? hasDesc.trim().split(`
3262
+ `)[0].replace(/^#+\s*/, "").slice(0, 100) : "";
3263
+ const skills = [];
3264
+ for (const se of subDirs) {
3265
+ const skillMd = await safeReadFile2(join7(catDir, se.name, "SKILL.md"));
3266
+ if (skillMd) {
3267
+ const source = getSkillSource(se.name, bundledManifest, hubNames);
3268
+ const usage = usageStats.get(se.name);
3269
+ skills.push({
3270
+ name: se.name,
3271
+ description: extractDescription(skillMd),
3272
+ enabled: !disabledList.includes(se.name),
3273
+ source,
3274
+ patchCount: usage?.patch_count,
3275
+ useCount: usage?.use_count,
3276
+ viewCount: usage?.view_count,
3277
+ pinned: usage?.pinned || undefined
3278
+ });
3279
+ }
3280
+ }
3281
+ if (skills.length > 0) {
3282
+ categories.push({ name: dirName, description: desc, skills: skills.sort((a, b) => a.name.localeCompare(b.name)) });
3283
+ }
3284
+ }
3285
+ }
3286
+ if (flatSkills.length > 0) {
3287
+ const miscSkills = flatSkills.map((fs) => {
3288
+ const usage = usageStats.get(fs.name);
3289
+ return {
3290
+ name: fs.name,
3291
+ description: extractDescription(fs.skillMd),
3292
+ enabled: !disabledList.includes(fs.name),
3293
+ source: fs.source,
3294
+ patchCount: usage?.patch_count,
3295
+ useCount: usage?.use_count,
3296
+ viewCount: usage?.view_count,
3297
+ pinned: usage?.pinned || undefined
3298
+ };
3299
+ }).sort((a, b) => a.name.localeCompare(b.name));
3300
+ categories.push({ name: "misc", description: "", skills: miscSkills });
3301
+ }
3302
+ categories.sort((a, b) => a.name.localeCompare(b.name));
3303
+ const archived = [];
3304
+ const archiveDir = join7(skillsDir, ".archive");
3305
+ const archiveEntries = await readdir(archiveDir, { withFileTypes: true }).catch(() => []);
3306
+ for (const entry of archiveEntries) {
3307
+ if (!entry.isDirectory())
3308
+ continue;
3309
+ const skillMd = await safeReadFile2(join7(archiveDir, entry.name, "SKILL.md"));
3310
+ if (skillMd) {
3311
+ const usage = usageStats.get(entry.name);
3312
+ archived.push({
3313
+ name: entry.name,
3314
+ description: extractDescription(skillMd),
3315
+ source: getSkillSource(entry.name, bundledManifest, hubNames),
3316
+ patchCount: usage?.patch_count,
3317
+ useCount: usage?.use_count,
3318
+ viewCount: usage?.view_count,
3319
+ pinned: usage?.pinned || undefined
3320
+ });
3321
+ }
3322
+ }
3323
+ archived.sort((a, b) => a.name.localeCompare(b.name));
3324
+ return c.json({ categories, archived });
3325
+ } catch (err) {
3326
+ return c.json({ error: err.message }, 500);
3327
+ }
3328
+ }
3329
+ async function readSkillFile(c) {
3330
+ const hermesDir2 = getHermesDir2();
3331
+ const urlPath = c.req.path;
3332
+ const prefix = "/api/hermes/skills/";
3333
+ const filePath = urlPath.startsWith(prefix) ? urlPath.slice(prefix.length) : "";
3334
+ logger.info("[skills] readSkillFile request", { filePath, urlPath, hermesDir: hermesDir2 });
3335
+ let realPath = filePath;
3336
+ if (realPath.startsWith("misc/"))
3337
+ realPath = realPath.slice(5);
3338
+ const fullPath = resolve4(join7(hermesDir2, "skills", realPath));
3339
+ const skillsBase = join7(hermesDir2, "skills");
3340
+ logger.info("[skills] resolved path", { fullPath, skillsBase });
3341
+ if (!fullPath.startsWith(skillsBase)) {
3342
+ return c.json({ error: "Access denied", path: fullPath }, 403);
3343
+ }
3344
+ const fileExists = existsSync3(fullPath);
3345
+ if (!fileExists) {
3346
+ return c.json({ error: "File not found", path: fullPath }, 404);
3347
+ }
3348
+ const content = await safeReadFile2(fullPath);
3349
+ if (content === null) {
3350
+ return c.json({ error: "File read failed", path: fullPath }, 500);
3351
+ }
3352
+ return c.json({ content });
3353
+ }
3354
+ async function listSkillFiles(c) {
3355
+ const hermesDir2 = getHermesDir2();
3356
+ const category = c.req.param("category") ?? "";
3357
+ const skill = c.req.param("skill") ?? "";
3358
+ const realDir = category === "misc" ? skill : join7(category, skill);
3359
+ const skillDir = join7(hermesDir2, "skills", realDir);
3360
+ try {
3361
+ const allFiles = await listFilesRecursive(skillDir, "");
3362
+ const files = allFiles.filter((f) => f.path !== "SKILL.md");
3363
+ return c.json({ files });
3364
+ } catch (err) {
3365
+ return c.json({ error: err.message }, 500);
3366
+ }
3367
+ }
3368
+ async function toggleSkill(c) {
3369
+ const body = await c.req.json();
3370
+ if (!body.name || typeof body.enabled !== "boolean") {
3371
+ return c.json({ error: "Missing name or enabled flag" }, 400);
3372
+ }
3373
+ return c.json({ success: true });
3374
+ }
3375
+ async function pinSkill(c) {
3376
+ const body = await c.req.json();
3377
+ if (!body.name || typeof body.pinned !== "boolean") {
3378
+ return c.json({ error: "Missing name or pinned flag" }, 400);
3379
+ }
3380
+ return c.json({ success: true });
3381
+ }
3382
+
3383
+ // src/routes/hermes/skills.ts
3384
+ var skillRoutes = new Hono2;
3385
+ skillRoutes.get("/api/hermes/skills", listSkills);
3386
+ skillRoutes.get("/api/hermes/skills/:category/:skill/files", listSkillFiles);
3387
+ skillRoutes.get("/api/hermes/skills/*", readSkillFile);
3388
+ skillRoutes.put("/api/hermes/skills/toggle", toggleSkill);
3389
+ skillRoutes.put("/api/hermes/skills/pin", pinSkill);
3390
+
3391
+ // src/routes/static.ts
3392
+ import { readFile as readFile5 } from "fs/promises";
3393
+ import { join as join8, resolve as resolve5 } from "path";
3394
+ import { existsSync as existsSync4 } from "fs";
3395
+ function createStaticRoutes(staticDir) {
3396
+ const app14 = new Hono2;
3397
+ app14.get("*", async (c) => {
3398
+ let filePath = c.req.path;
3399
+ if (filePath === "/")
3400
+ filePath = "/index.html";
3401
+ const fullPath = join8(staticDir, filePath);
3402
+ const resolved = resolve5(fullPath);
3403
+ if (!resolved.startsWith(resolve5(staticDir))) {
3404
+ return c.notFound();
3405
+ }
3406
+ if (!existsSync4(fullPath)) {
3407
+ const indexPath = join8(staticDir, "index.html");
3408
+ if (existsSync4(indexPath)) {
3409
+ const content2 = await readFile5(indexPath, "utf-8");
3410
+ return c.html(content2);
3411
+ }
3412
+ return c.notFound();
3413
+ }
3414
+ const content = await readFile5(fullPath);
3415
+ const ext = filePath.split(".").pop()?.toLowerCase() || "";
3416
+ const mimeTypes = {
3417
+ html: "text/html",
3418
+ js: "text/javascript",
3419
+ mjs: "text/javascript",
3420
+ css: "text/css",
3421
+ json: "application/json",
3422
+ png: "image/png",
3423
+ jpg: "image/jpeg",
3424
+ jpeg: "image/jpeg",
3425
+ gif: "image/gif",
3426
+ svg: "image/svg+xml",
3427
+ ico: "image/x-icon",
3428
+ woff2: "font/woff2",
3429
+ woff: "font/woff",
3430
+ ttf: "font/ttf",
3431
+ otf: "font/otf",
3432
+ webp: "image/webp"
3433
+ };
3434
+ const mime = mimeTypes[ext] || "application/octet-stream";
3435
+ c.header("Content-Type", mime);
3436
+ return c.body(content);
3437
+ });
3438
+ return app14;
3439
+ }
3440
+
3441
+ // src/routes/index.ts
3442
+ function createApp(staticDir) {
3443
+ const app14 = new Hono2;
3444
+ app14.use(corsMiddleware);
3445
+ app14.use(requestLogger);
3446
+ app14.route("/", app);
3447
+ app14.route("/", publicApp);
3448
+ app14.use(authMiddleware);
3449
+ app14.route("/", protectedApp);
3450
+ app14.route("/", app2);
3451
+ app14.route("/", app3);
3452
+ app14.route("/", app4);
3453
+ app14.route("/", app5);
3454
+ app14.route("/", app6);
3455
+ app14.route("/", app7);
3456
+ app14.route("/", app8);
3457
+ app14.route("/", app9);
3458
+ app14.route("/", app10);
3459
+ app14.route("/", app11);
3460
+ app14.route("/", app12);
3461
+ app14.route("/", memoryRoutes);
3462
+ app14.route("/", usageRoutes);
3463
+ app14.route("/", skillRoutes);
3464
+ if (staticDir) {
3465
+ app14.route("/", createStaticRoutes(staticDir));
3466
+ }
3467
+ app14.route("/", app13);
3468
+ app14.notFound((c) => c.json({ error: { message: `Not Found: ${c.req.method} ${c.req.path}` } }, 404));
3469
+ return app14;
3470
+ }
3471
+
3472
+ // src/index.ts
3473
+ var APP_VERSION = process.env.APP_VERSION || "dev";
3474
+ async function bootstrap() {
3475
+ console.log(`hermium-api v${APP_VERSION} starting...`);
3476
+ logger.info(`hermium-api v${APP_VERSION} starting...`);
3477
+ await mkdir4(config.uploadDir, { recursive: true });
3478
+ await mkdir4(config.dataDir, { recursive: true });
3479
+ const token = await ensureToken();
3480
+ if (token) {
3481
+ console.log(`Auth enabled \u2014 token: ${token}`);
3482
+ logger.info("Auth enabled");
3483
+ }
3484
+ initGatewayManager();
3485
+ logger.info("Gateway manager initialized");
3486
+ initAllStores();
3487
+ logger.info("Database initialized");
3488
+ const staticDir = process.env.WEB_STATIC_DIR;
3489
+ const app14 = createApp(staticDir);
3490
+ const server = Bun.serve({
3491
+ hostname: config.host,
3492
+ port: config.port,
3493
+ fetch: app14.fetch
3494
+ });
3495
+ const localUrl = `http://localhost:${config.port}`;
3496
+ console.log(`Server running at ${localUrl}`);
3497
+ logger.info(`Server running at ${localUrl}`);
3498
+ const shutdown = async (signal) => {
3499
+ console.log(`
3500
+ Received ${signal}, shutting down...`);
3501
+ logger.info(`Received ${signal}, shutting down...`);
3502
+ server.stop();
3503
+ closeDb();
3504
+ process.exit(0);
3505
+ };
3506
+ process.on("SIGINT", () => shutdown("SIGINT"));
3507
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
3508
+ }
3509
+ bootstrap().catch((err) => {
3510
+ console.error("FATAL: Failed to start API server", err);
3511
+ logger.fatal("Failed to start API server", { error: err.message });
3512
+ process.exit(1);
3513
+ });