hermes-web-ui 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +117 -408
  2. package/bin/hermes-web-ui.mjs +111 -97
  3. package/dist/assets/{ChatView-WnNeYrS7.js → ChatView-BBqtEbUW.js} +20 -20
  4. package/dist/assets/ChatView-DC6_7Uwg.css +1 -0
  5. package/dist/assets/JobsView-sQ8sqrxF.js +356 -0
  6. package/dist/assets/LogsView-BN_TkDPi.css +1 -0
  7. package/dist/assets/LogsView-DukKyFJt.js +1 -0
  8. package/dist/assets/Modal-oIDM0xXN.js +232 -0
  9. package/dist/assets/Spin-zWt--szS.js +476 -0
  10. package/dist/assets/Tooltip-BGvPCNBt.js +1 -0
  11. package/dist/assets/Warning-B9_T2nKK.js +1 -0
  12. package/dist/assets/_plugin-vue_export-helper-V8xgnEJh.js +231 -0
  13. package/dist/assets/chat-CGF6ipPC.js +5 -0
  14. package/dist/assets/fade-in-scale-up.cssr-DQYNrBys.js +45 -0
  15. package/dist/assets/index-BsLYVWlc.js +307 -0
  16. package/dist/assets/index-DDAe8BhJ.css +1 -0
  17. package/dist/assets/{jobs-Cuol6Mqb.js → jobs-QHXENDTQ.js} +1 -1
  18. package/dist/assets/use-message-42wmA96q.js +117 -0
  19. package/dist/index.html +8 -6
  20. package/dist/server/config.d.ts +7 -0
  21. package/dist/server/config.js +12 -0
  22. package/dist/server/index.d.ts +1 -0
  23. package/dist/server/index.js +154 -0
  24. package/dist/server/routes/logs.d.ts +2 -0
  25. package/dist/server/routes/logs.js +87 -0
  26. package/dist/server/routes/proxy-handler.d.ts +2 -0
  27. package/dist/server/routes/proxy-handler.js +82 -0
  28. package/dist/server/routes/proxy.d.ts +2 -0
  29. package/dist/server/routes/proxy.js +12 -0
  30. package/dist/server/routes/sessions.d.ts +2 -0
  31. package/dist/server/routes/sessions.js +69 -0
  32. package/dist/server/routes/upload.d.ts +2 -0
  33. package/dist/server/routes/upload.js +50 -0
  34. package/dist/server/routes/webhook.d.ts +2 -0
  35. package/dist/server/routes/webhook.js +33 -0
  36. package/dist/server/services/hermes-cli.d.ts +50 -0
  37. package/dist/server/services/hermes-cli.js +185 -0
  38. package/dist/server/services/hermes.d.ts +40 -0
  39. package/dist/server/services/hermes.js +118 -0
  40. package/package.json +19 -4
  41. package/dist/assets/ChatView-DrzZz5ex.css +0 -1
  42. package/dist/assets/JobsView-BF9wWdwU.js +0 -831
  43. package/dist/assets/Modal-CQVLL_Oc.js +0 -276
  44. package/dist/assets/_plugin-vue_export-helper-D5N3Hfca.js +0 -231
  45. package/dist/assets/chat-640V6r6N.js +0 -1
  46. package/dist/assets/index-4iFlrAYJ.js +0 -307
  47. package/dist/assets/index-CMJKLUKk.css +0 -1
  48. package/dist/assets/use-message-B8ClBznx.js +0 -117
  49. /package/dist/assets/{client-BxIiwrqC.js → client-ZYGCrm9m.js} +0 -0
@@ -1,125 +1,139 @@
1
1
  #!/usr/bin/env node
2
- import { createServer as createViteServer } from 'http'
2
+ import { spawn } from 'child_process'
3
3
  import { resolve, dirname, join } from 'path'
4
4
  import { fileURLToPath } from 'url'
5
- import { readFile, stat, readdir } from 'fs/promises'
5
+ import { readFileSync, writeFileSync, unlinkSync, mkdirSync, openSync } from 'fs'
6
6
 
7
7
  const __dirname = dirname(fileURLToPath(import.meta.url))
8
- const distDir = resolve(__dirname, '..', 'dist')
9
- const API_TARGET = 'http://127.0.0.1:8642'
8
+ const serverEntry = resolve(__dirname, '..', 'dist', 'server', 'index.js')
9
+ const PID_DIR = resolve(__dirname, '..', '.hermes-web-ui')
10
+ const PID_FILE = join(PID_DIR, 'server.pid')
11
+ const LOG_FILE = join(PID_DIR, 'server.log')
10
12
  const DEFAULT_PORT = 8648
11
13
 
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',
14
+ function getPort() {
15
+ if (process.argv[3] && !isNaN(process.argv[3])) return parseInt(process.argv[3])
16
+ if (process.argv.includes('--port')) return parseInt(process.argv[process.argv.indexOf('--port') + 1])
17
+ return DEFAULT_PORT
22
18
  }
23
19
 
24
- function getMimeType(filePath) {
25
- const ext = filePath.substring(filePath.lastIndexOf('.'))
26
- return MIME_TYPES[ext] || 'application/octet-stream'
20
+ function getPid() {
21
+ try {
22
+ return parseInt(readFileSync(PID_FILE, 'utf-8').trim())
23
+ } catch {
24
+ return null
25
+ }
27
26
  }
28
27
 
29
- async function serveStatic(reqPath, res) {
30
- let filePath = join(distDir, reqPath)
28
+ function isRunning(pid) {
31
29
  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)
30
+ process.kill(pid, 0)
31
+ return true
40
32
  } 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
- }
33
+ return false
50
34
  }
51
35
  }
52
36
 
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']
37
+ function writePid(pid) {
38
+ writeFileSync(PID_FILE, String(pid))
39
+ }
58
40
 
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
- }
41
+ function removePid() {
42
+ try { unlinkSync(PID_FILE) } catch {}
43
+ }
65
44
 
66
- const apiRes = await fetch(url, {
67
- method: req.method,
68
- headers,
69
- body: bodyChunks ? Buffer.concat(bodyChunks) : undefined,
70
- })
45
+ function startDaemon(port) {
46
+ const existing = getPid()
47
+ if (existing && isRunning(existing)) {
48
+ console.log(` ✗ hermes-web-ui is already running (PID: ${existing})`)
49
+ console.log(` Use "hermes-web-ui stop" to stop it first`)
50
+ process.exit(1)
51
+ }
52
+ removePid() // stale pid file
53
+ mkdirSync(PID_DIR, { recursive: true })
71
54
 
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()
55
+ const logStream = openSync(LOG_FILE, 'a')
56
+ const child = spawn(process.execPath, [serverEntry], {
57
+ detached: true,
58
+ stdio: ['ignore', logStream, logStream],
59
+ env: { ...process.env, PORT: String(port) },
60
+ })
61
+
62
+ child.unref()
63
+ writePid(child.pid)
64
+
65
+ // Wait a moment and check if the process is still alive
66
+ setTimeout(() => {
67
+ if (isRunning(child.pid)) {
68
+ console.log(` ✓ hermes-web-ui started (PID: ${child.pid}, port: ${port})`)
69
+ console.log(` http://localhost:${port}`)
70
+ console.log(` Log: ${LOG_FILE}`)
94
71
  } else {
95
- res.end()
96
- }
97
- } catch (err) {
98
- if (!res.headersSent) {
99
- res.writeHead(502, { 'Content-Type': 'application/json' })
72
+ console.log(' ✗ Failed to start hermes-web-ui')
73
+ console.log(` Check log: ${LOG_FILE}`)
74
+ removePid()
75
+ process.exit(1)
100
76
  }
101
- res.end(JSON.stringify({ error: { message: `API proxy error: ${err.message}` } }))
102
- }
77
+ }, 500)
103
78
  }
104
79
 
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
- }
80
+ function stopDaemon() {
81
+ const pid = getPid()
82
+ if (!pid) {
83
+ console.log(' hermes-web-ui is not running')
84
+ process.exit(1)
85
+ }
111
86
 
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
87
+ if (!isRunning(pid)) {
88
+ console.log(` ✗ Process ${pid} is not alive (stale PID file)`)
89
+ removePid()
90
+ process.exit(1)
91
+ }
114
92
 
115
- createViteServer(async (req, res) => {
116
- const reqPath = req.url.split('?')[0]
93
+ try {
94
+ process.kill(pid, 'SIGTERM')
95
+ removePid()
96
+ console.log(` ✓ hermes-web-ui stopped (PID: ${pid})`)
97
+ } catch (err) {
98
+ console.log(` ✗ Failed to stop: ${err.message}`)
99
+ process.exit(1)
100
+ }
101
+ }
117
102
 
118
- if (reqPath.startsWith('/api/') || reqPath.startsWith('/v1/') || reqPath === '/health' || reqPath.startsWith('/health')) {
119
- await proxyRequest(req, res, reqPath)
103
+ function showStatus() {
104
+ const pid = getPid()
105
+ if (pid && isRunning(pid)) {
106
+ console.log(` ✓ hermes-web-ui is running (PID: ${pid})`)
120
107
  } else {
121
- await serveStatic(reqPath, res)
108
+ if (pid) removePid() // clean stale
109
+ console.log(' ✗ hermes-web-ui is not running')
122
110
  }
123
- }).listen(port, '0.0.0.0', () => {
124
- console.log(` ➜ Hermes Web UI: http://localhost:${port}`)
125
- })
111
+ }
112
+
113
+ const command = process.argv[2] || 'start'
114
+
115
+ switch (command) {
116
+ case 'start':
117
+ startDaemon(getPort())
118
+ break
119
+ case 'stop':
120
+ stopDaemon()
121
+ break
122
+ case 'restart':
123
+ stopDaemon()
124
+ setTimeout(() => startDaemon(getPort()), 500)
125
+ break
126
+ case 'status':
127
+ showStatus()
128
+ break
129
+ default:
130
+ // Direct run (foreground): hermes-web-ui [port]
131
+ const port = !isNaN(command) ? parseInt(command) : DEFAULT_PORT
132
+ const child = spawn(process.execPath, [serverEntry], {
133
+ stdio: 'inherit',
134
+ env: { ...process.env, PORT: String(port) },
135
+ })
136
+ child.on('exit', (code) => process.exit(code ?? 1))
137
+ process.on('SIGTERM', () => child.kill('SIGTERM'))
138
+ process.on('SIGINT', () => child.kill('SIGINT'))
139
+ }