hermes-web-ui 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/hermes-web-ui.mjs +120 -19
- package/dist/assets/ChatView-DrzZz5ex.css +1 -0
- package/dist/assets/ChatView-WnNeYrS7.js +38 -0
- package/dist/assets/JobsView-BF9wWdwU.js +831 -0
- package/dist/assets/JobsView-BhwwXuLt.css +1 -0
- package/dist/assets/Modal-CQVLL_Oc.js +276 -0
- package/dist/assets/_plugin-vue_export-helper-D5N3Hfca.js +231 -0
- package/dist/assets/chat-640V6r6N.js +1 -0
- package/dist/assets/client-BxIiwrqC.js +1 -0
- package/dist/assets/index-4iFlrAYJ.js +307 -0
- package/dist/assets/index-CMJKLUKk.css +1 -0
- package/dist/assets/jobs-Cuol6Mqb.js +1 -0
- package/dist/assets/use-message-B8ClBznx.js +117 -0
- package/dist/favicon.ico +0 -0
- package/dist/index.html +21 -0
- package/package.json +2 -10
- package/index.html +0 -13
- package/src/App.vue +0 -54
- package/src/api/chat.ts +0 -87
- package/src/api/client.ts +0 -44
- package/src/api/jobs.ts +0 -100
- package/src/api/system.ts +0 -25
- package/src/assets/hero.png +0 -0
- package/src/assets/vite.svg +0 -1
- package/src/components/chat/ChatInput.vue +0 -123
- package/src/components/chat/ChatPanel.vue +0 -289
- package/src/components/chat/MarkdownRenderer.vue +0 -187
- package/src/components/chat/MessageItem.vue +0 -189
- package/src/components/chat/MessageList.vue +0 -94
- package/src/components/jobs/JobCard.vue +0 -244
- package/src/components/jobs/JobFormModal.vue +0 -188
- package/src/components/jobs/JobsPanel.vue +0 -58
- package/src/components/layout/AppSidebar.vue +0 -169
- package/src/composables/useKeyboard.ts +0 -39
- package/src/env.d.ts +0 -7
- package/src/main.ts +0 -10
- package/src/router/index.ts +0 -24
- package/src/stores/app.ts +0 -66
- package/src/stores/chat.ts +0 -344
- package/src/stores/jobs.ts +0 -72
- package/src/styles/global.scss +0 -60
- package/src/styles/theme.ts +0 -71
- package/src/styles/variables.scss +0 -56
- package/src/views/ChatView.vue +0 -25
- package/src/views/JobsView.vue +0 -93
- package/src/views/SettingsView.vue +0 -257
- package/tsconfig.app.json +0 -17
- package/tsconfig.json +0 -7
- package/tsconfig.node.json +0 -24
- package/vite.config.ts +0 -39
- /package/{assets/logo.png → dist/assets/logo-BAarh-tH.png} +0 -0
- /package/{public → dist}/favicon.svg +0 -0
- /package/{public → dist}/icons.svg +0 -0
package/bin/hermes-web-ui.mjs
CHANGED
|
@@ -1,24 +1,125 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { resolve, dirname } from 'path'
|
|
2
|
+
import { createServer as createViteServer } from 'http'
|
|
3
|
+
import { resolve, dirname, join } from 'path'
|
|
4
4
|
import { fileURLToPath } from 'url'
|
|
5
|
+
import { readFile, stat, readdir } from 'fs/promises'
|
|
5
6
|
|
|
6
7
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
8
|
+
const distDir = resolve(__dirname, '..', 'dist')
|
|
9
|
+
const API_TARGET = 'http://127.0.0.1:8642'
|
|
10
|
+
const DEFAULT_PORT = 8648
|
|
11
|
+
|
|
12
|
+
const MIME_TYPES = {
|
|
13
|
+
'.html': 'text/html',
|
|
14
|
+
'.js': 'application/javascript',
|
|
15
|
+
'.css': 'text/css',
|
|
16
|
+
'.json': 'application/json',
|
|
17
|
+
'.png': 'image/png',
|
|
18
|
+
'.svg': 'image/svg+xml',
|
|
19
|
+
'.ico': 'image/x-icon',
|
|
20
|
+
'.woff': 'font/woff',
|
|
21
|
+
'.woff2': 'font/woff2',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getMimeType(filePath) {
|
|
25
|
+
const ext = filePath.substring(filePath.lastIndexOf('.'))
|
|
26
|
+
return MIME_TYPES[ext] || 'application/octet-stream'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function serveStatic(reqPath, res) {
|
|
30
|
+
let filePath = join(distDir, reqPath)
|
|
31
|
+
try {
|
|
32
|
+
const s = await stat(filePath)
|
|
33
|
+
if (s.isDirectory()) filePath = join(filePath, 'index.html')
|
|
34
|
+
const data = await readFile(filePath)
|
|
35
|
+
res.writeHead(200, {
|
|
36
|
+
'Content-Type': getMimeType(filePath),
|
|
37
|
+
'Cache-Control': 'public, max-age=3600',
|
|
38
|
+
})
|
|
39
|
+
res.end(data)
|
|
40
|
+
} catch {
|
|
41
|
+
// SPA fallback
|
|
42
|
+
try {
|
|
43
|
+
const data = await readFile(join(distDir, 'index.html'))
|
|
44
|
+
res.writeHead(200, { 'Content-Type': 'text/html' })
|
|
45
|
+
res.end(data)
|
|
46
|
+
} catch {
|
|
47
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' })
|
|
48
|
+
res.end('Not Found')
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function proxyRequest(req, res, reqPath) {
|
|
54
|
+
const url = `${API_TARGET}${reqPath}`
|
|
55
|
+
const headers = { ...req.headers, host: '127.0.0.1:8642' }
|
|
56
|
+
delete headers['origin']
|
|
57
|
+
delete headers['referer']
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const hasBody = req.method !== 'GET' && req.method !== 'HEAD'
|
|
61
|
+
const bodyChunks = hasBody ? [] : null
|
|
62
|
+
if (hasBody) {
|
|
63
|
+
for await (const chunk of req) bodyChunks.push(chunk)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const apiRes = await fetch(url, {
|
|
67
|
+
method: req.method,
|
|
68
|
+
headers,
|
|
69
|
+
body: bodyChunks ? Buffer.concat(bodyChunks) : undefined,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const resHeaders = {}
|
|
73
|
+
apiRes.headers.forEach((v, k) => {
|
|
74
|
+
if (k !== 'transfer-encoding' && k !== 'connection') {
|
|
75
|
+
resHeaders[k] = v
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
resHeaders['x-accel-buffering'] = 'no'
|
|
79
|
+
resHeaders['cache-control'] = 'no-cache'
|
|
80
|
+
|
|
81
|
+
res.writeHead(apiRes.status, resHeaders)
|
|
82
|
+
|
|
83
|
+
if (apiRes.body) {
|
|
84
|
+
const reader = apiRes.body.getReader()
|
|
85
|
+
const pump = async () => {
|
|
86
|
+
while (true) {
|
|
87
|
+
const { done, value } = await reader.read()
|
|
88
|
+
if (done) break
|
|
89
|
+
res.write(value)
|
|
90
|
+
}
|
|
91
|
+
res.end()
|
|
92
|
+
}
|
|
93
|
+
await pump()
|
|
94
|
+
} else {
|
|
95
|
+
res.end()
|
|
96
|
+
}
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if (!res.headersSent) {
|
|
99
|
+
res.writeHead(502, { 'Content-Type': 'application/json' })
|
|
100
|
+
}
|
|
101
|
+
res.end(JSON.stringify({ error: { message: `API proxy error: ${err.message}` } }))
|
|
102
|
+
}
|
|
24
103
|
}
|
|
104
|
+
|
|
105
|
+
const command = process.argv[2]
|
|
106
|
+
|
|
107
|
+
if (command === 'build') {
|
|
108
|
+
console.log('Build is done during npm install. Use "npm run build" in the source repo.')
|
|
109
|
+
process.exit(1)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// start (default)
|
|
113
|
+
const port = parseInt(process.argv[2] && !isNaN(process.argv[2]) ? process.argv[2] : process.argv.includes('--port') ? process.argv[process.argv.indexOf('--port') + 1] : '') || DEFAULT_PORT
|
|
114
|
+
|
|
115
|
+
createViteServer(async (req, res) => {
|
|
116
|
+
const reqPath = req.url.split('?')[0]
|
|
117
|
+
|
|
118
|
+
if (reqPath.startsWith('/api/') || reqPath.startsWith('/v1/') || reqPath === '/health' || reqPath.startsWith('/health')) {
|
|
119
|
+
await proxyRequest(req, res, reqPath)
|
|
120
|
+
} else {
|
|
121
|
+
await serveStatic(reqPath, res)
|
|
122
|
+
}
|
|
123
|
+
}).listen(port, '0.0.0.0', () => {
|
|
124
|
+
console.log(` ➜ Hermes Web UI: http://localhost:${port}`)
|
|
125
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.markdown-body{font-size:14px;line-height:1.65}.markdown-body p{margin:0 0 8px}.markdown-body p:last-child{margin-bottom:0}.markdown-body ul,.markdown-body ol{margin:4px 0 8px;padding-left:20px}.markdown-body li{margin:2px 0}.markdown-body strong{color:#1a1a1a;font-weight:600}.markdown-body em{color:#666}.markdown-body a{color:#333;text-underline-offset:2px;text-decoration:underline}.markdown-body a:hover{color:#1a1a1a}.markdown-body blockquote{color:#666;border-left:3px solid #e0e0e0;margin:8px 0;padding:4px 12px}.markdown-body code:not(.hljs){color:#333;background:#f4f4f4;border-radius:4px;padding:2px 6px;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:13px}.markdown-body table{border-collapse:collapse;width:100%;margin:8px 0}.markdown-body table th,.markdown-body table td{text-align:left;border:1px solid #e0e0e0;padding:6px 12px;font-size:13px}.markdown-body table th{color:#1a1a1a;background:#33333314;font-weight:600}.markdown-body table td{color:#666}.markdown-body hr{border:none;border-top:1px solid #e0e0e0;margin:12px 0}.hljs-code-block{background:#f4f4f4;border:1px solid #e0e0e0;border-radius:6px;margin:8px 0;overflow:hidden}.hljs-code-block .code-header{background:#00000008;border-bottom:1px solid #e0e0e0;justify-content:space-between;align-items:center;padding:6px 12px;display:flex}.hljs-code-block .code-header .code-lang{color:#999;text-transform:uppercase;font-size:11px}.hljs-code-block .code-header .copy-btn{color:#999;cursor:pointer;background:0 0;border:none;border-radius:3px;padding:2px 6px;font-size:11px;transition:all .15s}.hljs-code-block .code-header .copy-btn:hover{color:#1a1a1a;background:#0000000d}.hljs-code-block code.hljs{padding:12px;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:13px;line-height:1.5;display:block;overflow-x:auto}.hljs{color:#2a2a2a;background:0 0}.hljs-keyword,.hljs-selector-tag{color:#1a1a1a;font-weight:600}.hljs-string,.hljs-attr{color:#555}.hljs-number{color:#333}.hljs-comment{color:#999;font-style:italic}.hljs-built_in{color:#444}.hljs-type{color:#3a3a3a}.hljs-variable,.hljs-title,.hljs-title\.function_{color:#1a1a1a}.hljs-params{color:#2a2a2a}.hljs-meta{color:#999}.message[data-v-865df6f5]{flex-direction:column;display:flex}.message.user[data-v-865df6f5]{align-items:flex-end}.message.user .msg-body[data-v-865df6f5]{max-width:75%}.message.user .msg-content.user[data-v-865df6f5]{align-items:flex-end}.message.user .message-bubble[data-v-865df6f5]{background-color:#e8e8e8;border-radius:10px 10px 4px}.message.assistant[data-v-865df6f5]{flex-direction:row;align-items:flex-start;gap:8px}.message.assistant .msg-body[data-v-865df6f5]{max-width:80%}.message.assistant .msg-avatar[data-v-865df6f5]{border-radius:4px;flex-shrink:0;width:28px;height:28px;margin-top:2px}.message.assistant .message-bubble[data-v-865df6f5]{background-color:#f5f5f5;border-radius:10px 10px 10px 4px}.message.tool[data-v-865df6f5],.message.system[data-v-865df6f5]{align-items:flex-start}.message.system .message-bubble.system[data-v-865df6f5]{background-color:#f57f170f;border-left:3px solid #f57f17;border-radius:6px;max-width:80%}.msg-body[data-v-865df6f5]{align-items:flex-start;gap:8px;max-width:85%;display:flex}.msg-content[data-v-865df6f5]{flex-direction:column;min-width:0;display:flex}.message-bubble[data-v-865df6f5]{word-break:break-word;padding:10px 14px;font-size:14px;line-height:1.65}.message-time[data-v-865df6f5]{color:#999;margin-top:4px;padding:0 4px;font-size:11px}.tool-line[data-v-865df6f5]{color:#999;align-items:center;gap:6px;padding:0 4px;font-size:11px;display:flex}.tool-line .tool-name[data-v-865df6f5]{font-family:JetBrains Mono,Fira Code,Consolas,monospace}.tool-line .tool-preview[data-v-865df6f5]{text-overflow:ellipsis;white-space:nowrap;max-width:400px;overflow:hidden}.streaming-cursor[data-v-865df6f5]{vertical-align:text-bottom;background-color:#999;width:2px;height:1em;margin-left:2px;animation:.8s infinite blink-865df6f5;display:inline-block}.streaming-dots[data-v-865df6f5]{gap:4px;padding:4px 0;display:flex}.streaming-dots span[data-v-865df6f5]{background-color:#999;border-radius:50%;width:6px;height:6px;animation:1.4s ease-in-out infinite pulse-865df6f5}.streaming-dots span[data-v-865df6f5]:nth-child(2){animation-delay:.2s}.streaming-dots span[data-v-865df6f5]:nth-child(3){animation-delay:.4s}@keyframes blink-865df6f5{0%,50%{opacity:1}51%,to{opacity:0}}@keyframes pulse-865df6f5{0%,80%,to{opacity:.3;transform:scale(.8)}40%{opacity:1;transform:scale(1)}}.message-list[data-v-c48f8f02]{flex-direction:column;flex:1;gap:16px;padding:20px;display:flex;overflow-y:auto}.empty-state[data-v-c48f8f02]{color:#999;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:12px;display:flex}.empty-state .empty-logo[data-v-c48f8f02]{opacity:.25;width:48px;height:48px}.empty-state p[data-v-c48f8f02]{font-size:14px}.streaming-indicator[data-v-c48f8f02]{color:#999;align-items:center;gap:4px;padding:4px;display:flex}.streaming-indicator span[data-v-c48f8f02]{background-color:#999;border-radius:50%;width:5px;height:5px;animation:1.4s ease-in-out infinite stream-pulse-c48f8f02}.streaming-indicator span[data-v-c48f8f02]:nth-child(2){animation-delay:.2s}.streaming-indicator span[data-v-c48f8f02]:nth-child(3){animation-delay:.4s}@keyframes stream-pulse-c48f8f02{0%,80%,to{opacity:.2;transform:scale(.8)}40%{opacity:1;transform:scale(1)}}.chat-input-area[data-v-7668b7e3]{border-top:1px solid #e0e0e0;flex-shrink:0;padding:12px 20px 16px}.input-wrapper[data-v-7668b7e3]{background-color:#fff;border:1px solid #e0e0e0;border-radius:10px;align-items:center;gap:10px;padding:10px 12px;transition:border-color .15s;display:flex}.input-wrapper[data-v-7668b7e3]:focus-within{border-color:#333}.input-textarea[data-v-7668b7e3]{color:#1a1a1a;resize:none;background:0 0;border:none;outline:none;flex:1;min-height:20px;max-height:100px;font-family:Inter,system-ui,-apple-system,sans-serif;font-size:14px;line-height:1.5;overflow-y:auto}.input-textarea[data-v-7668b7e3]::placeholder{color:#999}.input-actions[data-v-7668b7e3]{flex-shrink:0;align-items:center;gap:6px;display:flex}.chat-panel[data-v-24185ee8]{height:100%;display:flex}.session-list[data-v-24185ee8]{border-right:1px solid #e0e0e0;flex-direction:column;flex-shrink:0;width:220px;transition:width .25s,opacity .25s;display:flex;overflow:hidden}.session-list.collapsed[data-v-24185ee8]{opacity:0;pointer-events:none;border-right:none;width:0}.session-list-header[data-v-24185ee8]{flex-shrink:0;justify-content:space-between;align-items:center;padding:12px;display:flex}.session-list-title[data-v-24185ee8]{color:#999;text-transform:uppercase;letter-spacing:.5px;font-size:12px;font-weight:600}.session-items[data-v-24185ee8]{flex:1;padding:0 6px 12px;overflow-y:auto}.session-item[data-v-24185ee8]{cursor:pointer;text-align:left;color:#666;background:0 0;border:none;border-radius:6px;justify-content:space-between;align-items:center;width:100%;margin-bottom:2px;padding:8px 10px;transition:all .15s;display:flex}.session-item[data-v-24185ee8]:hover{color:#1a1a1a;background:#3333330f}.session-item:hover .session-item-delete[data-v-24185ee8]{opacity:1}.session-item.active[data-v-24185ee8]{color:#1a1a1a;background:#3333331a;font-weight:500}.session-item-content[data-v-24185ee8]{flex:1;overflow:hidden}.session-item-title[data-v-24185ee8]{white-space:nowrap;text-overflow:ellipsis;font-size:13px;display:block;overflow:hidden}.session-item-time[data-v-24185ee8]{color:#999;margin-top:2px;font-size:11px;display:block}.session-item-delete[data-v-24185ee8]{opacity:0;color:#999;cursor:pointer;background:0 0;border:none;border-radius:3px;flex-shrink:0;padding:2px;transition:all .15s}.session-item-delete[data-v-24185ee8]:hover{color:#c62828;background:#c628281a}.chat-main[data-v-24185ee8]{flex-direction:column;flex:1;min-width:0;display:flex;overflow:hidden}.chat-header[data-v-24185ee8]{border-bottom:1px solid #e0e0e0;flex-shrink:0;justify-content:space-between;align-items:center;padding:12px 16px;display:flex}.header-left[data-v-24185ee8]{align-items:center;gap:8px;display:flex;overflow:hidden}.header-session-title[data-v-24185ee8]{color:#1a1a1a;white-space:nowrap;text-overflow:ellipsis;font-size:14px;font-weight:500;overflow:hidden}.model-badge[data-v-24185ee8]{color:#999;background:#3333331a;border:1px solid #e0e0e0;border-radius:10px;flex-shrink:0;padding:2px 8px;font-size:11px}.header-actions[data-v-24185ee8]{flex-shrink:0;align-items:center;gap:4px;display:flex}.chat-view[data-v-e67fc0ba]{flex-direction:column;height:100vh;display:flex}
|