hermes-web-ui 0.2.5 → 0.2.6

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 (80) hide show
  1. package/README.md +138 -205
  2. package/bin/hermes-web-ui.mjs +14 -2
  3. package/dist/assets/{Button-DBOPt_s1.js → Button-BHvh9zmj.js} +1 -1
  4. package/dist/assets/{ChannelsView-BU9QAub-.css → ChannelsView-C7Wor1FB.css} +1 -1
  5. package/dist/assets/ChannelsView-DZMniRoi.js +1 -0
  6. package/dist/assets/ChatView-Dt1eqp6-.js +127 -0
  7. package/dist/assets/{ChatView-D1SaZAvQ.css → ChatView-gR62J-Xr.css} +1 -1
  8. package/dist/assets/Close-DYPlamoH.js +45 -0
  9. package/dist/assets/{FormItem-De01hN-G.js → FormItem-C75Y2lpf.js} +1 -1
  10. package/dist/assets/Input-CS0p63GR.js +234 -0
  11. package/dist/assets/InputNumber-lHTE1DBf.js +13 -0
  12. package/dist/assets/JobsView-DoUtVQm_.css +1 -0
  13. package/dist/assets/JobsView-kGXDpDGq.js +2 -0
  14. package/dist/assets/LoginView-BXByybMK.css +1 -0
  15. package/dist/assets/LoginView-HQwDR5A7.js +1 -0
  16. package/dist/assets/{LogsView-BgmSycD4.js → LogsView-KPcTBGzh.js} +1 -1
  17. package/dist/assets/{MarkdownRenderer-z5xC7cOw.js → MarkdownRenderer-Bp-uAEtp.js} +1 -1
  18. package/dist/assets/{MemoryView-BUZGXC60.js → MemoryView-CWEya0jB.js} +1 -1
  19. package/dist/assets/{Modal-BLLYorfc.js → Modal-mFCAbKPD.js} +5 -5
  20. package/dist/assets/{ModelsView-ByryHyh7.js → ModelsView-BdPt2Mo_.js} +1 -1
  21. package/dist/assets/{ModelsView-DCR-0I10.css → ModelsView-DbBXgw4g.css} +1 -1
  22. package/dist/assets/Popconfirm-C2OzcGTX.js +16 -0
  23. package/dist/assets/Popover-CRZpNb8Q.js +117 -0
  24. package/dist/assets/Scrollbar-CA0Mq0os.js +77 -0
  25. package/dist/assets/Select-DLECSo9D.js +340 -0
  26. package/dist/assets/SettingRow-CsKwX-jv.js +1 -0
  27. package/dist/assets/{SettingsView-Y2Et1YHr.css → SettingsView-Citzr3ci.css} +1 -1
  28. package/dist/assets/SettingsView-DWXjfdpD.js +352 -0
  29. package/dist/assets/{SkillsView-CREfL6dQ.css → SkillsView-BNEWlriU.css} +1 -1
  30. package/dist/assets/{SkillsView-Blcv_g2_.js → SkillsView-Gb4tyJjH.js} +1 -1
  31. package/dist/assets/{Spin-DiwsqMEC.js → Spin-BbbA3R-G.js} +1 -1
  32. package/dist/assets/Suffix-BMwB7UJK.js +25 -0
  33. package/dist/assets/{Switch-Ce0Tgpcj.js → Switch-Xa0U6jwA.js} +2 -2
  34. package/dist/assets/{Tag-BUJuCRNX.js → Tag-NMAaTGW7.js} +2 -2
  35. package/dist/assets/TerminalView-B7iMIrM8.css +1 -0
  36. package/dist/assets/TerminalView-CvTXTM3W.js +36 -0
  37. package/dist/assets/{Tooltip-C65FWX0W.js → Tooltip-C9RmASqV.js} +1 -1
  38. package/dist/assets/{UsageView-CKMaJ5W0.css → UsageView-DKigFrVY.css} +1 -1
  39. package/dist/assets/UsageView-DiCHVrs8.js +1 -0
  40. package/dist/assets/Warning-CmJYhk3M.js +1 -0
  41. package/dist/assets/{_plugin-vue_export-helper-DkLSTLBi.js → _plugin-vue_export-helper-NLzeiXdV.js} +1 -1
  42. package/dist/assets/{app-DtII96CF.js → app-CQZfHgyy.js} +1 -1
  43. package/dist/assets/app-DeeNNd_a.js +1 -0
  44. package/dist/assets/{browser-oMT2KRlV.js → browser-DNwI2j4X.js} +1 -1
  45. package/dist/assets/{chat-nPd2Ount.js → chat-DnFbBAAK.js} +2 -2
  46. package/dist/assets/context-DLFTSrww.js +1 -0
  47. package/dist/assets/fade-in-scale-up.cssr-BMJXPg-3.js +1 -0
  48. package/dist/assets/index-DlNQjiQP.js +306 -0
  49. package/dist/assets/{index-8v0lZOmS.css → index-TVVcuwR0.css} +1 -1
  50. package/dist/assets/{jobs-fIgcRW5v.js → jobs-B7U8gZia.js} +1 -1
  51. package/dist/assets/{pinia-D41F8A9y.js → pinia-DKSOddVw.js} +1 -1
  52. package/dist/assets/{router-B0PaAFys.js → router-CpYvE976.js} +2 -2
  53. package/dist/assets/{sessions-Dsqr5aLA.js → sessions-DTV2WuAE.js} +1 -1
  54. package/dist/assets/{skills-COmQAkb0.js → skills-DwX9oZGG.js} +1 -1
  55. package/dist/assets/use-compitable-D2-fMbL2.js +1 -0
  56. package/dist/assets/{use-message-CG8KB3t0.js → use-message-17mZlZe3.js} +1 -1
  57. package/dist/index.html +22 -18
  58. package/dist/server/index.js +3 -0
  59. package/dist/server/routes/terminal.d.ts +2 -0
  60. package/dist/server/routes/terminal.js +288 -0
  61. package/package.json +28 -4
  62. package/dist/assets/ChannelsView-CIZXL3vY.js +0 -1
  63. package/dist/assets/ChatView-DvAYfM24.js +0 -142
  64. package/dist/assets/Close-Ck11Jegf.js +0 -45
  65. package/dist/assets/Input-CyvPny5v.js +0 -234
  66. package/dist/assets/InputNumber-v7Tkrv97.js +0 -13
  67. package/dist/assets/JobsView-Cch6Sv2Y.js +0 -2
  68. package/dist/assets/JobsView-DARa8llR.css +0 -1
  69. package/dist/assets/LoginView-Cp-xfwtv.css +0 -1
  70. package/dist/assets/LoginView-DsClZpVT.js +0 -1
  71. package/dist/assets/Popover-ZzeCeChJ.js +0 -117
  72. package/dist/assets/Select-CPQQRbMt.js +0 -340
  73. package/dist/assets/SettingRow-BvAmImkg.js +0 -1
  74. package/dist/assets/SettingsView-B_5pGI0B.js +0 -352
  75. package/dist/assets/Suffix-La8FFPWu.js +0 -101
  76. package/dist/assets/UsageView-MZGY4EKW.js +0 -1
  77. package/dist/assets/app-DVSCPXta.js +0 -1
  78. package/dist/assets/context-DRBiUHq_.js +0 -1
  79. package/dist/assets/index-DNNVWa6r.js +0 -306
  80. /package/dist/assets/{omit-C6t0n7Zy.js → omit-C4dR5R2G.js} +0 -0
package/README.md CHANGED
@@ -1,76 +1,165 @@
1
- # Hermes Web UI
1
+ <p align="center">
2
+ <strong>Hermes Web UI</strong>
3
+ </p>
4
+
5
+ <p align="center">
6
+ A full-featured web dashboard for <a href="https://github.com/NousResearch/hermes-agent">Hermes Agent</a>.<br/>
7
+ Manage AI chat sessions, monitor usage & costs, configure platform channels,<br/>
8
+ schedule cron jobs, browse skills — all from a clean, responsive web interface.
9
+ </p>
10
+
11
+ <p align="center">
12
+ <code>npm install -g hermes-web-ui && hermes-web-ui start</code>
13
+ </p>
14
+
15
+ <p align="center">
16
+ <img src="https://github.com/EKKOLearnAI/hermes-web-ui/blob/main/src/assets/output.gif" alt="Hermes Web UI Demo" width="680"/>
17
+ </p>
18
+
19
+ <p align="center">
20
+ <strong>Mobile</strong>
21
+ </p>
22
+ <p align="center">
23
+ <video src="https://github.com/EKKOLearnAI/hermes-web-ui/blob/main/src/assets/video.mp4?raw=true" width="360" controls></video>
24
+ </p>
25
+
26
+ <p align="center">
27
+ <a href="https://www.npmjs.com/package/hermes-web-ui"><img src="https://img.shields.io/npm/v/hermes-web-ui?style=flat-square&color=blue" alt="npm version"/></a>
28
+ <a href="https://github.com/EKKOLearnAI/hermes-web-ui/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/hermes-web-ui?style=flat-square" alt="license"/></a>
29
+ <a href="https://github.com/EKKOLearnAI/hermes-web-ui/stargazers"><img src="https://img.shields.io/github/stars/EKKOLearnAI/hermes-web-ui?style=flat-square" alt="stars"/></a>
30
+ </p>
2
31
 
3
- Web dashboard for [Hermes Agent](https://github.com/NousResearch/hermes-agent) — chat interaction, session management, scheduled jobs, usage statistics, platform channel configuration, and log viewing.
32
+ ---
4
33
 
5
- ![Hermes Web UI Demo](https://github.com/EKKOLearnAI/hermes-web-ui/blob/main/src/assets/output.gif)
34
+ ## Features
6
35
 
7
- ## Tech Stack
36
+ ### AI Chat
37
+
38
+ - Real-time streaming via SSE with async run support
39
+ - Multi-session management — create, rename, delete, switch between sessions
40
+ - Session grouping by source (Telegram, Discord, Slack, etc.) with collapsible accordion
41
+ - Markdown rendering with syntax highlighting and code copy
42
+ - Tool call detail expansion (arguments / result)
43
+ - File upload support
44
+ - Global model selector — discovers models from `~/.hermes/auth.json` credential pool
45
+ - Per-session model display badge and context token usage
46
+
47
+ ### Platform Channels
48
+
49
+ Unified configuration for **8 platforms** in one page:
50
+
51
+ | Platform | Features |
52
+ |---|---|
53
+ | Telegram | Bot token, mention control, reactions, free-response chats |
54
+ | Discord | Bot token, mention, auto-thread, reactions, channel allow/ignore lists |
55
+ | Slack | Bot token, mention control, bot message handling |
56
+ | WhatsApp | Enable/disable, mention control, mention patterns |
57
+ | Matrix | Access token, homeserver, auto-thread, DM mention threads |
58
+ | Feishu (Lark) | App ID / Secret, mention control |
59
+ | WeChat | QR code login (scan in browser, auto-save credentials) |
60
+ | WeCom | Bot ID / Secret |
61
+
62
+ - Credential management writes to `~/.hermes/.env`
63
+ - Channel behavior settings write to `~/.hermes/config.yaml`
64
+ - Auto gateway restart on config change
65
+ - Per-platform configured/unconfigured status detection
66
+
67
+ ### Usage Analytics
68
+
69
+ - Total token usage breakdown (input / output)
70
+ - Session count with daily average
71
+ - Estimated cost tracking & cache hit rate
72
+ - Model usage distribution chart
73
+ - 30-day daily trend (bar chart + data table)
74
+
75
+ ### Scheduled Jobs
76
+
77
+ - Create, edit, pause, resume, delete cron jobs
78
+ - Trigger immediate execution
79
+ - Cron expression quick presets
80
+
81
+ ### Model Management
82
+
83
+ - Auto-discover models from credential pool (`~/.hermes/auth.json`)
84
+ - Fetch available models from each provider endpoint (`/v1/models`)
85
+ - Add custom OpenAI-compatible providers
86
+ - Provider-level model grouping
8
87
 
9
- - **Vue 3** — Composition API + `<script setup>`
10
- - **TypeScript**
11
- - **Vite** Build tool
12
- - **Naive UI** Component library
13
- - **Pinia** State management
14
- - **Vue Router** — Routing (Hash mode)
15
- - **vue-i18n** — Internationalization (Chinese / English)
16
- - **Koa 2** — BFF server (API proxy, file upload, session management)
17
- - **SCSS** Style preprocessor
18
- - **markdown-it** + **highlight.js** Markdown rendering and code highlighting
88
+ ### Skills & Memory
89
+
90
+ - Browse and search installed skills
91
+ - View skill details and attached files
92
+ - User notes and profile management
93
+
94
+ ### Logs
95
+
96
+ - View agent / gateway / error logs
97
+ - Filter by log level, log file, and keyword
98
+ - Structured log parsing with HTTP access log highlighting
99
+
100
+ ### Settings
101
+
102
+ - Display (streaming, compact mode, reasoning, cost display)
103
+ - Agent (max turns, timeout, tool enforcement)
104
+ - Memory (enable/disable, char limits)
105
+ - Session reset (idle timeout, scheduled reset)
106
+ - Privacy (PII redaction)
107
+ - API server configuration
19
108
 
20
- ## Install and Run
109
+ ---
110
+
111
+ ## Quick Start
21
112
 
22
- ### Quick Install
113
+ ### npm (Recommended)
23
114
 
24
115
  ```bash
25
116
  npm install -g hermes-web-ui
26
117
  hermes-web-ui start
27
118
  ```
28
119
 
29
- Open http://localhost:8648
120
+ Open **http://localhost:8648**
121
+
122
+ ### One-line Setup (Auto-detect OS)
30
123
 
31
- ### WSL (Windows Subsystem for Linux)
124
+ Automatically installs Node.js (if missing) and hermes-web-ui on Debian/Ubuntu/macOS:
32
125
 
33
126
  ```bash
34
- # 1. Auto-setup: install Node.js + hermes-web-ui
35
127
  bash <(curl -fsSL https://cdn.jsdelivr.net/gh/EKKOLearnAI/hermes-web-ui@main/scripts/setup.sh)
36
-
37
- # 2. Start
38
- hermes-web-ui start
39
128
  ```
40
129
 
41
- > WSL will auto-detect and use `hermes gateway run` for background startup (no launchd/systemd).
42
-
43
- ### One-line Setup (Auto-detect OS)
130
+ ### WSL
44
131
 
45
132
  ```bash
46
133
  bash <(curl -fsSL https://cdn.jsdelivr.net/gh/EKKOLearnAI/hermes-web-ui@main/scripts/setup.sh)
134
+ hermes-web-ui start
47
135
  ```
48
136
 
49
- Automatically installs Node.js (if missing) and hermes-web-ui on Debian/Ubuntu/macOS.
137
+ > WSL auto-detects and uses `hermes gateway run` for background startup (no launchd/systemd).
50
138
 
51
139
  ### CLI Commands
52
140
 
53
- | Command | Description |
54
- | --------------------------------- | --------------------------------- |
55
- | `hermes-web-ui start` | Start in background (daemon mode) |
56
- | `hermes-web-ui start --port 9000` | Start on custom port |
57
- | `hermes-web-ui stop` | Stop background process |
58
- | `hermes-web-ui restart` | Restart background process |
59
- | `hermes-web-ui status` | Check if running |
60
- | `hermes-web-ui update` | Update to latest version & restart|
61
- | `hermes-web-ui -v` | Show version number |
62
- | `hermes-web-ui -h` | Show help message |
63
- | `hermes-web-ui` | Run in foreground (for debugging) |
141
+ | Command | Description |
142
+ |---|---|
143
+ | `hermes-web-ui start` | Start in background (daemon mode) |
144
+ | `hermes-web-ui start --port 9000` | Start on custom port |
145
+ | `hermes-web-ui stop` | Stop background process |
146
+ | `hermes-web-ui restart` | Restart background process |
147
+ | `hermes-web-ui status` | Check if running |
148
+ | `hermes-web-ui update` | Update to latest version & restart |
149
+ | `hermes-web-ui -v` | Show version number |
150
+ | `hermes-web-ui -h` | Show help message |
64
151
 
65
152
  ### Auto Configuration
66
153
 
67
- On startup, the BFF server automatically:
154
+ On startup the BFF server automatically:
68
155
 
69
- - Checks `~/.hermes/config.yaml` and ensures `platforms.api_server` has all required fields (`enabled`, `host`, `port`, `key`, `cors_origins`)
70
- - If any field is missing, backs up the original to `config.yaml.bak`, fills in defaults, and restarts the gateway
71
- - Detects if the gateway is running and starts it if needed
72
- - Kills any process occupying the target port before starting
73
- - Opens the browser automatically after successful startup
156
+ - Validates `~/.hermes/config.yaml` and fills missing `api_server` fields
157
+ - Backs up original config to `config.yaml.bak` if modified
158
+ - Detects and starts the gateway if needed
159
+ - Resolves port conflicts (kills stale processes)
160
+ - Opens browser on successful startup
161
+
162
+ ---
74
163
 
75
164
  ## Development
76
165
 
@@ -81,161 +170,13 @@ npm install
81
170
  npm run dev
82
171
  ```
83
172
 
84
- This starts:
85
-
86
173
  - Frontend: http://localhost:5173
87
174
  - BFF Server: http://localhost:8648 (proxies to Hermes on 8642)
88
175
 
89
- ### Build
90
-
91
176
  ```bash
92
- npm run build
93
- ```
94
-
95
- Outputs to `dist/` (frontend + compiled BFF server).
96
-
97
- ## Project Structure
98
-
99
- ```
100
- hermes-web-ui/
101
- ├── bin/
102
- │ └── hermes-web-ui.mjs # CLI entry (start/stop/restart/status/update/version/help)
103
- ├── server/src/
104
- │ ├── index.ts # BFF entry (Koa app bootstrap)
105
- │ ├── config.ts # Configuration (port, upstream, etc.)
106
- │ ├── routes/
107
- │ │ ├── proxy.ts # API proxy to Hermes (/api/*, /v1/*)
108
- │ │ ├── config.ts # Config & credentials management
109
- │ │ ├── weixin.ts # WeChat QR code login proxy
110
- │ │ ├── upload.ts # File upload (POST /upload)
111
- │ │ ├── sessions.ts # Session management via Hermes CLI
112
- │ │ ├── filesystem.ts # Skills, memory, config model management
113
- │ │ ├── webhook.ts # Webhook receiver
114
- │ │ └── logs.ts # Log file listing and reading
115
- │ └── services/
116
- │ └── hermes-cli.ts # Hermes CLI wrapper (sessions, logs, version)
117
- ├── src/
118
- │ ├── i18n/ # Internationalization (en / zh)
119
- │ │ ├── index.ts # i18n instance setup
120
- │ │ └── locales/
121
- │ │ ├── en.ts # English translations
122
- │ │ └── zh.ts # Chinese translations
123
- │ ├── api/ # Frontend API layer
124
- │ ├── stores/ # Pinia state management
125
- │ ├── components/
126
- │ │ ├── layout/
127
- │ │ │ ├── AppSidebar.vue # Sidebar navigation
128
- │ │ │ ├── LanguageSwitch.vue # Language toggle (EN / 中文)
129
- │ │ │ └── ModelSelector.vue # Global model selector
130
- │ │ ├── chat/ # Chat components
131
- │ │ ├── jobs/ # Job components
132
- │ │ ├── models/ # Model/provider components
133
- │ │ ├── settings/ # Settings components
134
- │ │ │ ├── PlatformCard.vue # Platform card with config status
135
- │ │ │ └── PlatformSettings.vue # Platform channel configuration
136
- │ │ ├── usage/ # Usage statistics components
137
- │ │ └── skills/ # Skill components
138
- │ ├── views/
139
- │ │ ├── ChatView.vue # Chat page
140
- │ │ ├── JobsView.vue # Jobs page
141
- │ │ ├── LogsView.vue # Logs page
142
- │ │ ├── ModelsView.vue # Model management page
143
- │ │ ├── ChannelsView.vue # Platform channels page
144
- │ │ ├── SkillsView.vue # Skills page
145
- │ │ ├── MemoryView.vue # Memory page
146
- │ │ ├── UsageView.vue # Usage statistics page
147
- │ │ └── SettingsView.vue # Settings page
148
- │ └── router/index.ts # Router configuration
149
- └── dist/ # Build output (published to npm)
150
- ├── server/index.js # Compiled BFF
151
- ├── index.html # Frontend entry
152
- └── assets/ # Frontend static assets
177
+ npm run build # outputs to dist/
153
178
  ```
154
179
 
155
- ## Features
156
-
157
- ### Chat
158
-
159
- - Async Run + SSE event streaming via BFF proxy
160
- - Session management via Hermes CLI
161
- - Multi-session switching with message history
162
- - Session grouping by source (Telegram, Discord, Slack, etc.) with collapsible accordion
163
- - Session rename and deletion
164
- - Markdown rendering with syntax highlighting and code copy
165
- - Tool call detail expansion (arguments / result)
166
- - File upload support (saved to temp, path passed to API)
167
- - Model selector — automatically discovers available models from `~/.hermes/auth.json` credential pool
168
- - Global model switching (updates `~/.hermes/config.yaml`)
169
- - Per-session model display (badge in chat header and session list)
170
- - Context token usage display (used / total)
171
-
172
- ### Usage Statistics
173
-
174
- - Total token usage breakdown (input / output)
175
- - Session count with daily average
176
- - Estimated cost tracking
177
- - Cache hit rate
178
- - Model usage distribution (horizontal bar chart)
179
- - 30-day daily trend (bar chart + data table)
180
- - Hover tooltips on chart bars
181
-
182
- ### Platform Channels
183
-
184
- - Unified channel configuration page (Telegram, Discord, Slack, WhatsApp, Matrix, Feishu, WeChat, WeCom)
185
- - Credential management — writes to `~/.hermes/.env` (matching `hermes gateway setup` behavior)
186
- - Channel behavior settings — writes to `~/.hermes/config.yaml`
187
- - WeChat QR code login — opens QR in browser, polls scan status, auto-saves credentials
188
- - Auto gateway restart after any channel config change
189
- - Per-platform configured/unconfigured status detection
190
-
191
- ### Model Management
192
-
193
- - Automatically reads credential pool from `~/.hermes/auth.json`
194
- - Fetches available models from each provider endpoint (`/v1/models`)
195
- - Groups models by provider (e.g. zai, subrouter.ai)
196
- - Add custom OpenAI-compatible providers
197
- - Switching model updates `model.provider` in config.yaml to bypass env auto-detection
198
- - Error handling: parallel fetching, per-provider timeout, fallback to config.yaml parsing
199
-
200
- ### Settings
201
-
202
- - Display settings (streaming, compact mode, reasoning, cost, etc.)
203
- - Agent settings (max turns, timeout, tool enforcement)
204
- - Memory settings (enable/disable, char limits)
205
- - Session reset settings (idle timeout, scheduled reset)
206
- - Privacy settings (PII redaction)
207
- - API server settings
208
-
209
- ### Scheduled Jobs
210
-
211
- - Job list view (including paused/disabled jobs)
212
- - Create, edit, pause, resume, and delete jobs
213
- - Trigger immediate job execution
214
- - Cron expression quick presets
215
-
216
- ### Skills & Memory
217
-
218
- - Browse and search installed skills
219
- - View skill details and attached files
220
- - User notes and profile management
221
-
222
- ### Logs
223
-
224
- - View Hermes agent/gateway/error logs
225
- - Filter by log level, log file, and search keyword
226
- - Structured log parsing with HTTP access log highlighting
227
-
228
- ### Other
229
-
230
- - Internationalization — auto-detect browser language, manual toggle between Chinese and English
231
- - Real-time connection status monitoring
232
- - Hermes version display in sidebar
233
- - Auto config check on startup with field-level validation
234
- - Port conflict auto-resolution (kills stale processes)
235
- - Auto browser open on startup
236
- - Minimalist "Pure Ink" theme
237
- - Session group collapse state persisted across navigation
238
-
239
180
  ## Architecture
240
181
 
241
182
  ```
@@ -248,21 +189,13 @@ Browser → BFF (Koa, :8648) → Hermes API (:8642)
248
189
  Tencent iLink API (WeChat QR login)
249
190
  ```
250
191
 
251
- The BFF layer handles:
192
+ The BFF layer handles API proxy, SSE streaming, file upload, session CRUD via CLI, config/credential management, WeChat QR login, model discovery, skills/memory management, log reading, and static file serving.
252
193
 
253
- - API proxy to Hermes (with header forwarding)
254
- - SSE streaming passthrough
255
- - File upload to temp directory
256
- - Session CRUD via Hermes CLI (with cache/cost token passthrough)
257
- - Config & credential management (config.yaml + .env)
258
- - WeChat QR code login flow (fetch QR, poll status, save credentials)
259
- - Auto gateway restart on platform config changes
260
- - Model discovery from `~/.hermes/auth.json` credential pool
261
- - Skills, memory, and custom provider management
262
- - Log file reading and parsing
263
- - Static file serving (SPA fallback)
194
+ ## Tech Stack
264
195
 
265
- ---
196
+ **Frontend:** Vue 3 + TypeScript + Vite + Naive UI + Pinia + Vue Router + vue-i18n + SCSS + markdown-it + highlight.js
197
+
198
+ **Backend:** Koa 2 (BFF server) + node-pty (web terminal)
266
199
 
267
200
  ## License
268
201
 
@@ -2,13 +2,14 @@
2
2
  import { spawn, execSync } from 'child_process'
3
3
  import { resolve, dirname, join } from 'path'
4
4
  import { fileURLToPath } from 'url'
5
- import { readFileSync, writeFileSync, unlinkSync, mkdirSync, openSync } from 'fs'
5
+ import { readFileSync, writeFileSync, unlinkSync, mkdirSync, openSync, chmodSync } from 'fs'
6
6
  import { randomBytes } from 'crypto'
7
7
  import { homedir } from 'os'
8
8
 
9
9
  const __dirname = dirname(fileURLToPath(import.meta.url))
10
10
  const serverEntry = resolve(__dirname, '..', 'dist', 'server', 'index.js')
11
- const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf-8'))
11
+ const pkgDir = resolve(__dirname, '..')
12
+ const pkg = JSON.parse(readFileSync(resolve(pkgDir, 'package.json'), 'utf-8'))
12
13
  const VERSION = pkg.version
13
14
  const PID_DIR = resolve(homedir(), '.hermes-web-ui')
14
15
  const PID_FILE = join(PID_DIR, 'server.pid')
@@ -16,6 +17,15 @@ const LOG_FILE = join(PID_DIR, 'server.log')
16
17
  const TOKEN_FILE = resolve(__dirname, '..', 'dist', 'server', 'data', '.token')
17
18
  const DEFAULT_PORT = 8648
18
19
 
20
+ // ─── Auto-fix node-pty native module ──────────────────────────
21
+ function ensureNativeModules() {
22
+ const prebuildDir = join(pkgDir, 'node_modules', 'node-pty', 'prebuilds', `${process.platform}-${process.arch}`)
23
+ const helper = join(prebuildDir, 'spawn-helper')
24
+ try {
25
+ chmodSync(helper, 0o755)
26
+ } catch {}
27
+ }
28
+
19
29
  function getToken() {
20
30
  try {
21
31
  return readFileSync(TOKEN_FILE, 'utf-8').trim()
@@ -105,6 +115,7 @@ function startDaemon(port) {
105
115
 
106
116
  mkdirSync(PID_DIR, { recursive: true })
107
117
 
118
+ ensureNativeModules()
108
119
  const token = ensureToken()
109
120
 
110
121
  const logStream = openSync(LOG_FILE, 'a')
@@ -260,6 +271,7 @@ switch (command) {
260
271
  doUpdate()
261
272
  break
262
273
  default:
274
+ ensureNativeModules()
263
275
  const port = !isNaN(command) ? parseInt(command) : DEFAULT_PORT
264
276
  const child = spawn(process.execPath, [serverEntry], {
265
277
  stdio: 'inherit',
@@ -1,4 +1,4 @@
1
- import{C as e,F as t,N as n,R as r,V as i,ct as a,d as o,dt as s,f as c,j as l}from"./router-B0PaAFys.js";import{B as u,K as d,Q as f,R as p,U as m,Z as h,a as g,at as _,c as v,ct as y,d as b,dt as x,et as S,l as C,lt as w,n as T,nt as E,o as D,ot as O,q as k,r as A,t as j,tt as M,u as N,ut as P,z as F}from"./browser-oMT2KRlV.js";function I(e){return e.replace(/#|\(|\)|,|\s|\./g,`_`)}var L=l({name:`FadeInExpandTransition`,props:{appear:Boolean,group:Boolean,mode:String,onLeave:Function,onAfterLeave:Function,onAfterEnter:Function,width:Boolean,reverse:Boolean},setup(e,{slots:t}){function r(t){e.width?t.style.maxWidth=`${t.offsetWidth}px`:t.style.maxHeight=`${t.offsetHeight}px`,t.offsetWidth}function i(t){e.width?t.style.maxWidth=`0`:t.style.maxHeight=`0`,t.offsetWidth;let{onLeave:n}=e;n&&n()}function a(t){e.width?t.style.maxWidth=``:t.style.maxHeight=``;let{onAfterLeave:n}=e;n&&n()}function s(t){if(t.style.transition=`none`,e.width){let e=t.offsetWidth;t.style.maxWidth=`0`,t.offsetWidth,t.style.transition=``,t.style.maxWidth=`${e}px`}else if(e.reverse)t.style.maxHeight=`${t.offsetHeight}px`,t.offsetHeight,t.style.transition=``,t.style.maxHeight=`0`;else{let e=t.offsetHeight;t.style.maxHeight=`0`,t.offsetWidth,t.style.transition=``,t.style.maxHeight=`${e}px`}t.offsetWidth}function l(t){var n;e.width?t.style.maxWidth=``:e.reverse||(t.style.maxHeight=``),(n=e.onAfterEnter)==null||n.call(e)}return()=>{let{group:u,width:d,appear:f,mode:p}=e,m=u?c:o,h={name:d?`fade-in-width-expand-transition`:`fade-in-height-expand-transition`,appear:f,onEnter:s,onAfterEnter:l,onBeforeLeave:r,onLeave:i,onAfterLeave:a};return u||(h.mode=p),n(m,h,t)}}}),{cubicBezierEaseInOut:R}=N;function z({duration:e=`.2s`,delay:t=`.1s`}={}){return[_(`&.fade-in-width-expand-transition-leave-from, &.fade-in-width-expand-transition-enter-to`,{opacity:1}),_(`&.fade-in-width-expand-transition-leave-to, &.fade-in-width-expand-transition-enter-from`,`
1
+ import{C as e,F as t,N as n,R as r,V as i,ct as a,d as o,dt as s,f as c,j as l}from"./router-CpYvE976.js";import{B as u,K as d,Q as f,R as p,U as m,Z as h,a as g,at as _,c as v,ct as y,d as b,dt as x,et as S,l as C,lt as w,n as T,nt as E,o as D,ot as O,q as k,r as A,t as j,tt as M,u as N,ut as P,z as F}from"./browser-DNwI2j4X.js";function I(e){return e.replace(/#|\(|\)|,|\s|\./g,`_`)}var L=l({name:`FadeInExpandTransition`,props:{appear:Boolean,group:Boolean,mode:String,onLeave:Function,onAfterLeave:Function,onAfterEnter:Function,width:Boolean,reverse:Boolean},setup(e,{slots:t}){function r(t){e.width?t.style.maxWidth=`${t.offsetWidth}px`:t.style.maxHeight=`${t.offsetHeight}px`,t.offsetWidth}function i(t){e.width?t.style.maxWidth=`0`:t.style.maxHeight=`0`,t.offsetWidth;let{onLeave:n}=e;n&&n()}function a(t){e.width?t.style.maxWidth=``:t.style.maxHeight=``;let{onAfterLeave:n}=e;n&&n()}function s(t){if(t.style.transition=`none`,e.width){let e=t.offsetWidth;t.style.maxWidth=`0`,t.offsetWidth,t.style.transition=``,t.style.maxWidth=`${e}px`}else if(e.reverse)t.style.maxHeight=`${t.offsetHeight}px`,t.offsetHeight,t.style.transition=``,t.style.maxHeight=`0`;else{let e=t.offsetHeight;t.style.maxHeight=`0`,t.offsetWidth,t.style.transition=``,t.style.maxHeight=`${e}px`}t.offsetWidth}function l(t){var n;e.width?t.style.maxWidth=``:e.reverse||(t.style.maxHeight=``),(n=e.onAfterEnter)==null||n.call(e)}return()=>{let{group:u,width:d,appear:f,mode:p}=e,m=u?c:o,h={name:d?`fade-in-width-expand-transition`:`fade-in-height-expand-transition`,appear:f,onEnter:s,onAfterEnter:l,onBeforeLeave:r,onLeave:i,onAfterLeave:a};return u||(h.mode=p),n(m,h,t)}}}),{cubicBezierEaseInOut:R}=N;function z({duration:e=`.2s`,delay:t=`.1s`}={}){return[_(`&.fade-in-width-expand-transition-leave-from, &.fade-in-width-expand-transition-enter-to`,{opacity:1}),_(`&.fade-in-width-expand-transition-leave-to, &.fade-in-width-expand-transition-enter-from`,`
2
2
  opacity: 0!important;
3
3
  margin-left: 0!important;
4
4
  margin-right: 0!important;
@@ -1 +1 @@
1
- .platform-card[data-v-ee5f4168]{background-color:#fff;border:1px solid #e0e0e0;border-radius:10px;margin-bottom:12px;overflow:hidden}.platform-card.configured[data-v-ee5f4168]{border-color:#2e7d3233}.platform-card-header[data-v-ee5f4168]{cursor:pointer;-webkit-user-select:none;user-select:none;justify-content:space-between;align-items:center;padding:12px 16px;display:flex}.platform-card-header[data-v-ee5f4168]:hover{background-color:#1a1a1a08}.platform-info[data-v-ee5f4168]{align-items:center;gap:10px;display:flex}.platform-icon[data-v-ee5f4168]{color:#666;flex-shrink:0;width:18px;height:18px}.platform-name[data-v-ee5f4168]{color:#1a1a1a;font-size:14px;font-weight:500}.expand-icon[data-v-ee5f4168]{color:#999;font-size:12px;transition:transform .2s}.expand-icon.expanded[data-v-ee5f4168]{transform:rotate(0)}.expand-icon[data-v-ee5f4168]:not(.expanded){transform:rotate(-90deg)}.platform-card-body[data-v-ee5f4168]{border-top:1px solid #ebebeb;padding:0 16px 12px}.settings-section[data-v-42f61b1e]{margin-top:16px}.weixin-qr-section[data-v-42f61b1e]{margin-top:12px;margin-bottom:12px}.weixin-qr-loading[data-v-42f61b1e]{color:#999;align-items:center;gap:8px;font-size:13px;display:flex}.weixin-qr-hint[data-v-42f61b1e]{color:#666;font-size:13px}.channels-view[data-v-cddaaa50]{height:calc(100 * var(--vh));flex-direction:column;display:flex}.channels-content[data-v-cddaaa50]{flex:1;padding:20px;overflow-y:auto}
1
+ .platform-card[data-v-ee5f4168]{background-color:#fff;border:1px solid #e0e0e0;border-radius:10px;margin-bottom:12px;overflow:hidden}.platform-card.configured[data-v-ee5f4168]{border-color:#2e7d3233}.platform-card-header[data-v-ee5f4168]{cursor:pointer;-webkit-user-select:none;user-select:none;justify-content:space-between;align-items:center;padding:12px 16px;display:flex}.platform-card-header[data-v-ee5f4168]:hover{background-color:#1a1a1a08}.platform-info[data-v-ee5f4168]{align-items:center;gap:10px;display:flex}.platform-icon[data-v-ee5f4168]{color:#666;flex-shrink:0;width:18px;height:18px}.platform-name[data-v-ee5f4168]{color:#1a1a1a;font-size:14px;font-weight:500}.expand-icon[data-v-ee5f4168]{color:#999;font-size:12px;transition:transform .2s}.expand-icon.expanded[data-v-ee5f4168]{transform:rotate(0)}.expand-icon[data-v-ee5f4168]:not(.expanded){transform:rotate(-90deg)}.platform-card-body[data-v-ee5f4168]{border-top:1px solid #ebebeb;padding:0 16px 12px}.settings-section[data-v-9f4c95ee]{margin-top:16px}.weixin-qr-section[data-v-9f4c95ee]{margin-top:12px;margin-bottom:12px}.weixin-qr-loading[data-v-9f4c95ee]{color:#999;align-items:center;gap:8px;font-size:13px;display:flex}.weixin-qr-hint[data-v-9f4c95ee]{color:#666;font-size:13px}.channels-view[data-v-099b0c1b]{height:calc(100 * var(--vh));flex-direction:column;display:flex}.channels-content[data-v-099b0c1b]{flex:1;padding:20px;position:relative;overflow-y:auto}
@@ -0,0 +1 @@
1
+ import{A as e,C as t,D as n,E as r,G as i,J as a,Q as o,T as s,U as c,W as l,Y as u,ct as d,gt as f,j as p,k as m,mt as h,pt as g,q as _,w as v,y}from"./router-CpYvE976.js";import{r as b,t as x}from"./_plugin-vue_export-helper-NLzeiXdV.js";import{t as S}from"./Button-BHvh9zmj.js";import{t as C}from"./Input-CS0p63GR.js";import{t as w}from"./Tag-NMAaTGW7.js";import{t as T}from"./use-message-17mZlZe3.js";import{t as E}from"./Spin-BbbA3R-G.js";import{t as D}from"./Switch-Xa0U6jwA.js";import{a as O,i as k,n as A,o as j,r as M,t as N}from"./SettingRow-CsKwX-jv.js";var P={class:`platform-info`},F=[`innerHTML`],I={class:`platform-name`},L={key:0,class:`platform-card-body`},R=x(p({__name:`PlatformCard`,props:{name:{},icon:{},config:{},credentials:{}},setup(s){let c=s,l=d(!0),{t:u}=b(),p=t(()=>{let e=c.credentials;if(!e)return!1;let t=[`token`,`api_key`,`app_id`,`client_id`,`secret`,`app_secret`,`client_secret`,`access_token`,`bot_id`,`account_id`,`enabled`];return[e,e.extra].filter(Boolean).some(e=>t.some(t=>{let n=e[t];return n!=null&&n!==``&&n!==!1}))});return(t,c)=>(i(),n(`div`,{class:h([`platform-card`,{configured:p.value}])},[v(`div`,{class:`platform-card-header`,onClick:c[0]||=e=>l.value=!l.value},[v(`div`,P,[v(`span`,{class:`platform-icon`,innerHTML:s.icon},null,8,F),v(`span`,I,f(s.name),1),e(g(w),{type:p.value?`success`:`default`,size:`small`,round:``},{default:o(()=>[m(f(p.value?g(u)(`common.configured`):g(u)(`common.notConfigured`)),1)]),_:1},8,[`type`])]),v(`span`,{class:h([`expand-icon`,{expanded:l.value}])},`▾`,2)]),l.value?(i(),n(`div`,L,[a(t.$slots,`default`,{},void 0,!0)])):r(``,!0)],2))}}),[[`__scopeId`,`data-v-ee5f4168`]]),z={class:`settings-section`},B={class:`weixin-qr-section`},V={key:1,class:`weixin-qr-loading`},H={key:2,class:`weixin-qr-hint`},U=x(p({__name:`PlatformSettings`,setup(t){let a=A(),c=T(),{t:p}=b();async function h(e,t){try{await a.saveSection(e,t),c.success(p(`settings.saved`))}catch{c.error(p(`settings.saveFailed`))}}let x=d(!1);async function w(e,t){x.value=!0;try{await O(e,t),await a.fetchSettings(),c.success(p(`settings.saved`))}catch{c.error(p(`settings.saveFailed`))}finally{x.value=!1}}function E(e){return a.platforms[e]||{}}let P=d(``),F=d(``),I=d(`idle`),L=null;async function U(){I.value=`loading`,P.value=``,F.value=``,G();try{let e=await M();F.value=e.qrcode,P.value=e.qrcode_url,window.open(e.qrcode_url,`_blank`),I.value=`waiting`,W()}catch(e){I.value=`error`,c.error(e.message||p(`platform.qrFetching`))}}function W(){F.value&&(L=setTimeout(async()=>{try{let e=await k(F.value);e.status===`wait`?W():e.status===`scaned`?(I.value=`scaned`,W()):e.status===`expired`?I.value=`expired`:e.status===`confirmed`&&(I.value=`confirmed`,await j({account_id:e.account_id,token:e.token,base_url:e.base_url}),await a.fetchSettings(),c.success(p(`settings.saved`)))}catch{W()}},3e3))}function G(){L&&=(clearTimeout(L),null)}l(()=>{G()});let K=[{key:`telegram`,name:`Telegram`,icon:`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.479.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg>`},{key:`discord`,name:`Discord`,icon:`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189z"/></svg>`},{key:`slack`,name:`Slack`,icon:`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zm1.271 0a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zm0 1.271a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zm10.122 0a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V5.042zm-1.27 0a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zm0-1.27a2.527 2.527 0 0 1 2.523-2.52h6.313A2.528 2.528 0 0 1 24 18.956a2.528 2.528 0 0 1-2.522 2.523h-6.313z"/></svg>`},{key:`whatsapp`,name:`WhatsApp`,icon:`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/></svg>`},{key:`matrix`,name:`Matrix`,icon:`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M.632.55v22.9H2.28V24H0V0h2.28v.55zm7.043 7.26v1.157h.033c.309-.443.683-.784 1.117-1.024.433-.245.936-.365 1.5-.365.54 0 1.033.107 1.48.324.448.217.786.619 1.017 1.205.24-.376.558-.702.956-.98.398-.277.872-.414 1.424-.414.41 0 .784.065 1.122.194.34.13.629.325.87.588.241.263.428.59.56.984.132.393.198.85.198 1.368v5.89h-2.49v-4.893c0-.268-.016-.525-.048-.77a1.627 1.627 0 00-.2-.63 1.028 1.028 0 00-.392-.426 1.294 1.294 0 00-.616-.134c-.277 0-.508.05-.693.15a1.043 1.043 0 00-.43.41 1.768 1.768 0 00-.214.616 4.15 4.15 0 00-.06.74v4.937H9.29v-4.937c0-.25-.01-.498-.032-.742a1.84 1.84 0 00-.166-.638.998.998 0 00-.363-.448 1.206 1.206 0 00-.624-.154c-.26 0-.483.048-.67.144a1.055 1.055 0 00-.436.402 1.744 1.744 0 00-.227.616 4.108 4.108 0 00-.063.74v4.937H5.21V7.81zm15.693 15.64V.55H21.72V0H24v24h-2.28v-.55z"/></svg>`},{key:`feishu`,name:`Feishu`,icon:`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M6.59 3.41a2.25 2.25 0 0 1 3.182 0L13.5 7.14l-3.182 3.182L6.59 7.59a2.25 2.25 0 0 1 0-3.182zm5.303 5.303L15.075 5.53a2.25 2.25 0 0 1 3.182 3.182L15.075 11.894 11.893 8.713zM3.41 6.59a2.25 2.25 0 0 1 3.182 0l3.182 3.182-3.182 3.182a2.25 2.25 0 0 1-3.182-3.182L3.41 6.59zm5.303 5.303L11.894 15.075a2.25 2.25 0 0 1-3.182 3.182L5.53 15.075 8.713 11.893zm5.303-5.303L17.478 9.778a2.25 2.25 0 0 1-3.182 3.182L10.53 10.075l3.182-3.182 0 .023z"/></svg>`},{key:`weixin`,name:`Weixin`,icon:`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 01.213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 00.167-.054l1.903-1.114a.864.864 0 01.717-.098 10.16 10.16 0 002.837.403c.276 0 .543-.027.811-.05-.857-2.578.157-4.972 1.932-6.446 1.703-1.415 3.882-1.98 5.853-1.838-.576-3.583-4.196-6.348-8.596-6.348zM5.785 5.991c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 01-1.162 1.178A1.17 1.17 0 014.623 7.17c0-.651.52-1.18 1.162-1.18zm5.813 0c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 01-1.162 1.178 1.17 1.17 0 01-1.162-1.178c0-.651.52-1.18 1.162-1.18zm3.68 4.025c-3.694 0-6.69 2.462-6.69 5.496 0 3.034 2.996 5.496 6.69 5.496.753 0 1.477-.1 2.158-.28a.66.66 0 01.548.074l1.46.854a.25.25 0 00.127.041.224.224 0 00.221-.225c0-.055-.022-.109-.037-.162l-.298-1.131a.453.453 0 01.163-.509C21.81 18.613 22.77 16.973 22.77 15.512c0-3.034-2.996-5.496-6.69-5.496h.198zm-2.454 3.347c.491 0 .889.404.889.902a.896.896 0 01-.889.903.896.896 0 01-.889-.903c0-.498.398-.902.889-.902zm4.912 0c.491 0 .889.404.889.902a.896.896 0 01-.889.903.896.896 0 01-.889-.903c0-.498.398-.902.889-.902z"/></svg>`},{key:`wecom`,name:`WeCom`,icon:`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 01.213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 00.167-.054l1.903-1.114a.864.864 0 01.717-.098 10.16 10.16 0 002.837.403c.276 0 .543-.027.811-.05-.857-2.578.157-4.972 1.932-6.446 1.703-1.415 3.882-1.98 5.853-1.838-.576-3.583-4.196-6.348-8.596-6.348zM5.785 5.991c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 01-1.162 1.178A1.17 1.17 0 014.623 7.17c0-.651.52-1.18 1.162-1.18zm5.813 0c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 01-1.162 1.178 1.17 1.17 0 01-1.162-1.178c0-.651.52-1.18 1.162-1.18zm3.68 4.025c-3.694 0-6.69 2.462-6.69 5.496 0 3.034 2.996 5.496 6.69 5.496.753 0 1.477-.1 2.158-.28a.66.66 0 01.548.074l1.46.854a.25.25 0 00.127.041.224.224 0 00.221-.225c0-.055-.022-.109-.037-.162l-.298-1.131a.453.453 0 01.163-.509C21.81 18.613 22.77 16.973 22.77 15.512c0-3.034-2.996-5.496-6.69-5.496h.198zm-2.454 3.347c.491 0 .889.404.889.902a.896.896 0 01-.889.903.896.896 0 01-.889-.903c0-.498.398-.902.889-.902zm4.912 0c.491 0 .889.404.889.902a.896.896 0 01-.889.903.896.896 0 01-.889-.903c0-.498.398-.902.889-.902z"/></svg>`}];return(t,c)=>{let l=u(`NSpin`);return i(),n(`section`,z,[(i(),n(y,null,_(K,t=>e(R,{key:t.key,name:t.name,icon:t.icon,config:g(a)[t.key],credentials:E(t.key)},{default:o(()=>[t.key===`telegram`?(i(),n(y,{key:0},[e(N,{label:g(p)(`platform.botToken`),hint:g(p)(`platform.botTokenHint`)},{default:o(()=>[e(g(C),{value:E(`telegram`).token||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`123456:ABC-DEF...`,"onUpdate:value":c[0]||=e=>w(`telegram`,{token:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.requireMention`),hint:g(p)(`platform.requireMentionGroup`)},{default:o(()=>[e(g(D),{value:g(a).telegram.require_mention,"onUpdate:value":c[1]||=e=>h(`telegram`,{require_mention:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.reactions`),hint:g(p)(`platform.reactionsHint`)},{default:o(()=>[e(g(D),{value:g(a).telegram.reactions,"onUpdate:value":c[2]||=e=>h(`telegram`,{reactions:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.freeResponseChats`),hint:g(p)(`platform.freeResponseChatsHint`)},{default:o(()=>[e(g(C),{value:g(a).telegram.free_response_chats||``,size:`small`,placeholder:`chat_id1,chat_id2`,"onUpdate:value":c[3]||=e=>h(`telegram`,{free_response_chats:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.mentionPatterns`),hint:g(p)(`platform.mentionPatternsHint`)},{default:o(()=>[e(g(C),{value:(g(a).telegram.mention_patterns||[]).join(`, `),size:`small`,placeholder:`pattern1, pattern2`,"onUpdate:value":c[4]||=e=>h(`telegram`,{mention_patterns:e?e.split(`,`).map(e=>e.trim()):[]})},null,8,[`value`])]),_:1},8,[`label`,`hint`])],64)):r(``,!0),t.key===`discord`?(i(),n(y,{key:1},[e(N,{label:g(p)(`platform.botToken`),hint:g(p)(`platform.botTokenHint`)},{default:o(()=>[e(g(C),{value:E(`discord`).token||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`Bot token...`,"onUpdate:value":c[5]||=e=>w(`discord`,{token:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.requireMention`),hint:g(p)(`platform.requireMentionChannel`)},{default:o(()=>[e(g(D),{value:g(a).discord.require_mention,"onUpdate:value":c[6]||=e=>h(`discord`,{require_mention:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.autoThread`),hint:g(p)(`platform.autoThreadHint`)},{default:o(()=>[e(g(D),{value:g(a).discord.auto_thread,"onUpdate:value":c[7]||=e=>h(`discord`,{auto_thread:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.reactions`),hint:g(p)(`platform.reactionsHint`)},{default:o(()=>[e(g(D),{value:g(a).discord.reactions,"onUpdate:value":c[8]||=e=>h(`discord`,{reactions:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.freeResponseChannels`),hint:g(p)(`platform.freeResponseChannelsHint`)},{default:o(()=>[e(g(C),{value:g(a).discord.free_response_channels||``,size:`small`,placeholder:`channel_id1,channel_id2`,"onUpdate:value":c[9]||=e=>h(`discord`,{free_response_channels:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.allowedChannels`),hint:g(p)(`platform.allowedChannelsHint`)},{default:o(()=>[e(g(C),{value:g(a).discord.allowed_channels||``,size:`small`,placeholder:`channel_id1,channel_id2`,"onUpdate:value":c[10]||=e=>h(`discord`,{allowed_channels:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.ignoredChannels`),hint:g(p)(`platform.ignoredChannelsHint`)},{default:o(()=>[e(g(C),{value:g(a).discord.ignored_channels||``,size:`small`,placeholder:`channel_id1,channel_id2`,"onUpdate:value":c[11]||=e=>h(`discord`,{ignored_channels:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.noThreadChannels`),hint:g(p)(`platform.noThreadChannelsHint`)},{default:o(()=>[e(g(C),{value:g(a).discord.no_thread_channels||``,size:`small`,placeholder:`channel_id1,channel_id2`,"onUpdate:value":c[12]||=e=>h(`discord`,{no_thread_channels:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`])],64)):r(``,!0),t.key===`slack`?(i(),n(y,{key:2},[e(N,{label:g(p)(`platform.botToken`),hint:g(p)(`platform.botTokenHint`)},{default:o(()=>[e(g(C),{value:E(`slack`).token||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`xoxb-...`,"onUpdate:value":c[13]||=e=>w(`slack`,{token:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.requireMention`),hint:g(p)(`platform.requireMentionChannel`)},{default:o(()=>[e(g(D),{value:g(a).slack.require_mention,"onUpdate:value":c[14]||=e=>h(`slack`,{require_mention:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.allowBots`),hint:g(p)(`platform.allowBotsHint`)},{default:o(()=>[e(g(D),{value:g(a).slack.allow_bots,"onUpdate:value":c[15]||=e=>h(`slack`,{allow_bots:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.freeResponseChannels`),hint:g(p)(`platform.freeResponseChannelsHint`)},{default:o(()=>[e(g(C),{value:g(a).slack.free_response_channels||``,size:`small`,placeholder:`channel_id1,channel_id2`,"onUpdate:value":c[16]||=e=>h(`slack`,{free_response_channels:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`])],64)):r(``,!0),t.key===`whatsapp`?(i(),n(y,{key:3},[e(N,{label:g(p)(`platform.waEnabled`),hint:g(p)(`platform.waEnabledHint`)},{default:o(()=>[e(g(D),{value:E(`whatsapp`).enabled,"onUpdate:value":c[17]||=e=>w(`whatsapp`,{enabled:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.requireMention`),hint:g(p)(`platform.requireMentionGroup`)},{default:o(()=>[e(g(D),{value:g(a).whatsapp.require_mention,"onUpdate:value":c[18]||=e=>h(`whatsapp`,{require_mention:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.freeResponseChats`),hint:g(p)(`platform.freeResponseChatsHint`)},{default:o(()=>[e(g(C),{value:g(a).whatsapp.free_response_chats||``,size:`small`,placeholder:`chat_id1,chat_id2`,"onUpdate:value":c[19]||=e=>h(`whatsapp`,{free_response_chats:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.mentionPatterns`),hint:g(p)(`platform.mentionPatternsHint`)},{default:o(()=>[e(g(C),{value:(g(a).whatsapp.mention_patterns||[]).join(`, `),size:`small`,placeholder:`pattern1, pattern2`,"onUpdate:value":c[20]||=e=>h(`whatsapp`,{mention_patterns:e?e.split(`,`).map(e=>e.trim()):[]})},null,8,[`value`])]),_:1},8,[`label`,`hint`])],64)):r(``,!0),t.key===`matrix`?(i(),n(y,{key:4},[e(N,{label:g(p)(`platform.accessToken`),hint:g(p)(`platform.accessTokenHint`)},{default:o(()=>[e(g(C),{value:E(`matrix`).token||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`syt_...`,"onUpdate:value":c[21]||=e=>w(`matrix`,{token:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.homeserver`),hint:g(p)(`platform.homeserverHint`)},{default:o(()=>[e(g(C),{value:E(`matrix`).extra?.homeserver||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`https://matrix.org`,"onUpdate:value":c[22]||=e=>w(`matrix`,{extra:{...E(`matrix`).extra,homeserver:e}})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.requireMention`),hint:g(p)(`platform.requireMentionRoom`)},{default:o(()=>[e(g(D),{value:g(a).matrix.require_mention,"onUpdate:value":c[23]||=e=>h(`matrix`,{require_mention:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.autoThread`),hint:g(p)(`platform.autoThreadHintRoom`)},{default:o(()=>[e(g(D),{value:g(a).matrix.auto_thread,"onUpdate:value":c[24]||=e=>h(`matrix`,{auto_thread:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.dmMentionThreads`),hint:g(p)(`platform.dmMentionThreadsHint`)},{default:o(()=>[e(g(D),{value:g(a).matrix.dm_mention_threads,"onUpdate:value":c[25]||=e=>h(`matrix`,{dm_mention_threads:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.freeResponseRooms`),hint:g(p)(`platform.freeResponseRoomsHint`)},{default:o(()=>[e(g(C),{value:g(a).matrix.free_response_rooms||``,size:`small`,placeholder:`room_id1,room_id2`,"onUpdate:value":c[26]||=e=>h(`matrix`,{free_response_rooms:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`])],64)):r(``,!0),t.key===`feishu`?(i(),n(y,{key:5},[e(N,{label:g(p)(`platform.appId`),hint:g(p)(`platform.appIdHint`)},{default:o(()=>[e(g(C),{value:E(`feishu`).extra?.app_id||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`cli_...`,"onUpdate:value":c[27]||=e=>w(`feishu`,{extra:{...E(`feishu`).extra,app_id:e}})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.appSecret`),hint:g(p)(`platform.appSecretHint`)},{default:o(()=>[e(g(C),{value:E(`feishu`).extra?.app_secret||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`App Secret`,"onUpdate:value":c[28]||=e=>w(`feishu`,{extra:{...E(`feishu`).extra,app_secret:e}})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.requireMention`),hint:g(p)(`platform.requireMentionGroup`)},{default:o(()=>[e(g(D),{value:g(a).feishu.require_mention,"onUpdate:value":c[29]||=e=>h(`feishu`,{require_mention:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.freeResponseChats`),hint:g(p)(`platform.freeResponseChatsHint`)},{default:o(()=>[e(g(C),{value:g(a).feishu.free_response_chats||``,size:`small`,placeholder:`chat_id1,chat_id2`,"onUpdate:value":c[30]||=e=>h(`feishu`,{free_response_chats:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`])],64)):r(``,!0),t.key===`dingtalk`?(i(),n(y,{key:6},[e(N,{label:g(p)(`platform.clientId`),hint:g(p)(`platform.clientIdHint`)},{default:o(()=>[e(g(C),{value:E(`dingtalk`).extra?.client_id||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`Client ID`,"onUpdate:value":c[31]||=e=>w(`dingtalk`,{extra:{...E(`dingtalk`).extra,client_id:e}})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.clientSecret`),hint:g(p)(`platform.clientSecretHint`)},{default:o(()=>[e(g(C),{value:E(`dingtalk`).extra?.client_secret||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`Client Secret`,"onUpdate:value":c[32]||=e=>w(`dingtalk`,{extra:{...E(`dingtalk`).extra,client_secret:e}})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.requireMention`),hint:g(p)(`platform.requireMentionGroup`)},{default:o(()=>[e(g(D),{value:g(a).dingtalk.require_mention,"onUpdate:value":c[33]||=e=>h(`dingtalk`,{require_mention:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.freeResponseChats`),hint:g(p)(`platform.freeResponseChatsHint`)},{default:o(()=>[e(g(C),{value:g(a).dingtalk.free_response_chats||``,size:`small`,placeholder:`chat_id1,chat_id2`,"onUpdate:value":c[34]||=e=>h(`dingtalk`,{free_response_chats:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`])],64)):r(``,!0),t.key===`weixin`?(i(),n(y,{key:7},[v(`div`,B,[I.value===`idle`||I.value===`error`||I.value===`expired`||I.value===`confirmed`?(i(),s(g(S),{key:0,type:`primary`,size:`small`,onClick:U},{default:o(()=>[m(f(I.value===`confirmed`?g(p)(`platform.qrRelogin`):g(p)(`platform.qrLogin`)),1)]),_:1})):r(``,!0),I.value===`loading`?(i(),n(`div`,V,[e(l,{size:`small`}),v(`span`,null,f(g(p)(`platform.qrFetching`)),1)])):r(``,!0),I.value===`waiting`||I.value===`scaned`?(i(),n(`div`,H,f(I.value===`scaned`?g(p)(`platform.qrScanedHint`):g(p)(`platform.qrScanHint`)),1)):r(``,!0)]),e(N,{label:g(p)(`platform.weixinToken`),hint:g(p)(`platform.weixinTokenHint`)},{default:o(()=>[e(g(C),{value:E(`weixin`).token||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`Token`,"onUpdate:value":c[35]||=e=>w(`weixin`,{token:e})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.accountId`),hint:g(p)(`platform.accountIdHint`)},{default:o(()=>[e(g(C),{value:E(`weixin`).extra?.account_id||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`Account ID`,"onUpdate:value":c[36]||=e=>w(`weixin`,{extra:{...E(`weixin`).extra,account_id:e}})},null,8,[`value`])]),_:1},8,[`label`,`hint`])],64)):r(``,!0),t.key===`wecom`?(i(),n(y,{key:8},[e(N,{label:g(p)(`platform.botId`),hint:g(p)(`platform.botIdHint`)},{default:o(()=>[e(g(C),{value:E(`wecom`).extra?.bot_id||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`Bot ID`,"onUpdate:value":c[37]||=e=>w(`wecom`,{extra:{...E(`wecom`).extra,bot_id:e}})},null,8,[`value`])]),_:1},8,[`label`,`hint`]),e(N,{label:g(p)(`platform.appSecret`),hint:g(p)(`platform.wecomSecretHint`)},{default:o(()=>[e(g(C),{value:E(`wecom`).extra?.secret||``,clearable:``,size:`small`,class:`input-lg`,placeholder:`Secret`,"onUpdate:value":c[38]||=e=>w(`wecom`,{extra:{...E(`wecom`).extra,secret:e}})},null,8,[`value`])]),_:1},8,[`label`,`hint`])],64)):r(``,!0)]),_:2},1032,[`name`,`icon`,`config`,`credentials`])),64))])}}}),[[`__scopeId`,`data-v-9f4c95ee`]]),W={class:`channels-view`},G={class:`page-header`},K={class:`header-title`},q={class:`channels-content`},J=x(p({__name:`ChannelsView`,setup(t){let r=A(),{t:a}=b();return c(()=>{r.fetchSettings()}),(t,s)=>(i(),n(`div`,W,[v(`header`,G,[v(`h2`,K,f(g(a)(`sidebar.channels`)),1)]),v(`div`,q,[e(g(E),{show:g(r).loading||g(r).saving,size:`large`,description:g(a)(`common.loading`)},{default:o(()=>[e(U)]),_:1},8,[`show`,`description`])])]))}}),[[`__scopeId`,`data-v-099b0c1b`]]);export{J as default};