@bitseek/hermes-webui 0.1.0-beta.0 → 0.1.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 (99) hide show
  1. package/package.json +2 -2
  2. package/vendor/agent-frontend-shell/.bitseek-source.json +2 -2
  3. package/vendor/agent-frontend-shell/CHANGELOG.md +178 -1
  4. package/vendor/agent-frontend-shell/CONTRIBUTORS.md +5 -5
  5. package/vendor/agent-frontend-shell/api/agent_health.py +134 -0
  6. package/vendor/agent-frontend-shell/api/config.py +145 -104
  7. package/vendor/agent-frontend-shell/api/gateway_chat.py +56 -12
  8. package/vendor/agent-frontend-shell/api/helpers.py +4 -2
  9. package/vendor/agent-frontend-shell/api/models.py +202 -20
  10. package/vendor/agent-frontend-shell/api/paths.py +77 -0
  11. package/vendor/agent-frontend-shell/api/plugins.py +185 -0
  12. package/vendor/agent-frontend-shell/api/profiles.py +95 -16
  13. package/vendor/agent-frontend-shell/api/routes.py +831 -30
  14. package/vendor/agent-frontend-shell/api/run_journal.py +1 -0
  15. package/vendor/agent-frontend-shell/api/state_sync.py +5 -4
  16. package/vendor/agent-frontend-shell/api/streaming.py +211 -56
  17. package/vendor/agent-frontend-shell/api/todo_state.py +122 -0
  18. package/vendor/agent-frontend-shell/api/updates.py +30 -3
  19. package/vendor/agent-frontend-shell/api/upload.py +251 -18
  20. package/vendor/agent-frontend-shell/api/workspace.py +323 -65
  21. package/vendor/agent-frontend-shell/bitseek_docs/BitSeek_Claw_Operation_Manual_EN.docx +0 -0
  22. package/vendor/agent-frontend-shell/bitseek_docs/BitSeek_Claw_Operation_Manual_ZH.docx +0 -0
  23. package/vendor/agent-frontend-shell/bitseek_docs/en/00-Installation.md +174 -0
  24. package/vendor/agent-frontend-shell/bitseek_docs/en/01-Overview.md +128 -0
  25. package/vendor/agent-frontend-shell/bitseek_docs/en/02-Page-Operations.md +461 -0
  26. package/vendor/agent-frontend-shell/bitseek_docs/en/README.md +61 -0
  27. package/vendor/agent-frontend-shell/bitseek_docs/en/images/ai-colleagues.png +0 -0
  28. package/vendor/agent-frontend-shell/bitseek_docs/en/images/chat-area.png +0 -0
  29. package/vendor/agent-frontend-shell/bitseek_docs/en/images/kanban.png +0 -0
  30. package/vendor/agent-frontend-shell/bitseek_docs/en/images/main-page.png +0 -0
  31. package/vendor/agent-frontend-shell/bitseek_docs/en/images/memory-notes.png +0 -0
  32. package/vendor/agent-frontend-shell/bitseek_docs/en/images/memory-overview.png +0 -0
  33. package/vendor/agent-frontend-shell/bitseek_docs/en/images/memory-profile.png +0 -0
  34. package/vendor/agent-frontend-shell/bitseek_docs/en/images/memory-soul.png +0 -0
  35. package/vendor/agent-frontend-shell/bitseek_docs/en/images/memory.png +0 -0
  36. package/vendor/agent-frontend-shell/bitseek_docs/en/images/navigation-bar.png +0 -0
  37. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-appearance.png +0 -0
  38. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-conversation.png +0 -0
  39. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-overview.png +0 -0
  40. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-plugins.png +0 -0
  41. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-preferences.png +0 -0
  42. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-providers.png +0 -0
  43. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings-system.png +0 -0
  44. package/vendor/agent-frontend-shell/bitseek_docs/en/images/settings.png +0 -0
  45. package/vendor/agent-frontend-shell/bitseek_docs/en/images/sidebar.png +0 -0
  46. package/vendor/agent-frontend-shell/bitseek_docs/en/images/skills.png +0 -0
  47. package/vendor/agent-frontend-shell/bitseek_docs/en/images/tasks.png +0 -0
  48. package/vendor/agent-frontend-shell/bitseek_docs/en/images/workspace-panel.png +0 -0
  49. package/vendor/agent-frontend-shell/bitseek_docs/md_to_docx.py +351 -0
  50. package/vendor/agent-frontend-shell/bitseek_docs/zh/00-/345/256/211/350/243/205/345/220/257/345/212/250.md +174 -0
  51. package/vendor/agent-frontend-shell/bitseek_docs/zh/01-/346/225/264/344/275/223/346/246/202/350/247/210.md +128 -0
  52. package/vendor/agent-frontend-shell/bitseek_docs/zh/02-/351/241/265/351/235/242/346/223/215/344/275/234.md +463 -0
  53. package/vendor/agent-frontend-shell/bitseek_docs/zh/README.md +61 -0
  54. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/ai-colleagues.png +0 -0
  55. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/chat-area.png +0 -0
  56. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/kanban.png +0 -0
  57. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/main-page.png +0 -0
  58. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/memory-notes.png +0 -0
  59. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/memory-overview.png +0 -0
  60. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/memory-profile.png +0 -0
  61. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/memory-soul.png +0 -0
  62. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/memory.png +0 -0
  63. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/navigation-bar.png +0 -0
  64. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-appearance.png +0 -0
  65. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-conversation.png +0 -0
  66. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-overview.png +0 -0
  67. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-plugins.png +0 -0
  68. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-preferences.png +0 -0
  69. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-providers.png +0 -0
  70. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings-system.png +0 -0
  71. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/settings.png +0 -0
  72. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/sidebar.png +0 -0
  73. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/skills.png +0 -0
  74. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/tasks.png +0 -0
  75. package/vendor/agent-frontend-shell/bitseek_docs/zh/images/workspace-panel.png +0 -0
  76. package/vendor/agent-frontend-shell/build-release.sh +62 -0
  77. package/vendor/agent-frontend-shell/ctl.sh +1 -0
  78. package/vendor/agent-frontend-shell/docker-compose.local.yml +33 -0
  79. package/vendor/agent-frontend-shell/docker-compose.yml +8 -0
  80. package/vendor/agent-frontend-shell/docker_init.bash +1 -0
  81. package/vendor/agent-frontend-shell/docs/rfcs/hermes-run-adapter-contract.md +74 -15
  82. package/vendor/agent-frontend-shell/extensions/common/index.css +6 -0
  83. package/vendor/agent-frontend-shell/extensions/manifest.json +6 -0
  84. package/vendor/agent-frontend-shell/extensions/pages/ai-teammates/page.js +60 -14
  85. package/vendor/agent-frontend-shell/readme-simple.md +103 -0
  86. package/vendor/agent-frontend-shell/requirements.txt +5 -0
  87. package/vendor/agent-frontend-shell/server.py +7 -0
  88. package/vendor/agent-frontend-shell/static/boot.js +53 -1
  89. package/vendor/agent-frontend-shell/static/commands.js +20 -10
  90. package/vendor/agent-frontend-shell/static/i18n.js +1142 -1016
  91. package/vendor/agent-frontend-shell/static/index.html +13 -3
  92. package/vendor/agent-frontend-shell/static/messages.js +48 -3
  93. package/vendor/agent-frontend-shell/static/panels.js +199 -30
  94. package/vendor/agent-frontend-shell/static/sessions.js +249 -39
  95. package/vendor/agent-frontend-shell/static/style.css +46 -2
  96. package/vendor/agent-frontend-shell/static/ui.js +323 -79
  97. package/vendor/agent-frontend-shell/static/workspace.js +185 -7
  98. package/vendor/agent-frontend-shell/README-CUSTOM.md +0 -76
  99. package/vendor/agent-frontend-shell/docker-compose.custom.yml +0 -26
@@ -268,12 +268,42 @@ function collectSessionArtifacts(){
268
268
  if(!path || seen.has(path)) return;
269
269
  seen.add(path); items.push({path, source});
270
270
  };
271
+ // Source 1: session-level tool call summaries (may be empty when messages
272
+ // carry their own tool metadata — see _syncToolCallsForLoadedMessages).
271
273
  for(const tc of (S.toolCalls || [])){
272
274
  for(const a of _artifactCandidatesFromToolCall(tc)) push(a.path, a.kind || tc.name || 'tool');
273
275
  }
276
+ // Source 2 & 3: message-level data — both text-mined diffs and structured
277
+ // tool_calls / tool_use content blocks that survive the S.toolCalls clear.
274
278
  for(const msg of (S.messages || [])){
275
- const text = msg && (msg.content || msg.text || msg.message || '');
276
- for(const a of _artifactCandidatesFromText(text)) push(a.path, a.kind);
279
+ if(!msg) continue;
280
+ const text = msg.content || msg.text || msg.message || '';
281
+ // Text-mined diff/patch fences (existing path).
282
+ if(typeof text === 'string'){
283
+ for(const a of _artifactCandidatesFromText(text)) push(a.path, a.kind);
284
+ }
285
+ // Structured tool_calls array (OpenAI format: {function:{name,arguments}}).
286
+ if(Array.isArray(msg.tool_calls)){
287
+ for(const tc of msg.tool_calls){
288
+ if(!tc || typeof tc !== 'object') continue;
289
+ const fn = (tc.function && typeof tc.function === 'object') ? tc.function : tc;
290
+ const name = fn.name || tc.name || '';
291
+ let args = fn.arguments || tc.arguments || tc.args || tc.input || {};
292
+ if(typeof args === 'string'){ try{ args = JSON.parse(args); }catch(_){} }
293
+ const fakeTc = {name, args, result: tc.result || tc.output || ''};
294
+ for(const a of _artifactCandidatesFromToolCall(fakeTc)) push(a.path, a.kind || name || 'tool');
295
+ }
296
+ }
297
+ // Structured content array with tool_use blocks (Anthropic format).
298
+ if(Array.isArray(msg.content)){
299
+ for(const block of msg.content){
300
+ if(!block || block.type !== 'tool_use') continue;
301
+ let inp = block.input || {};
302
+ if(typeof inp === 'string'){ try{ inp = JSON.parse(inp); }catch(_){} }
303
+ const fakeTc = {name: block.name || '', args: inp, result: block.result || ''};
304
+ for(const a of _artifactCandidatesFromToolCall(fakeTc)) push(a.path, a.kind || block.name || 'tool');
305
+ }
306
+ }
277
307
  }
278
308
  return items.slice(0, 50);
279
309
  }
@@ -292,7 +322,14 @@ function renderSessionArtifacts(){
292
322
  root.innerHTML = '<div class="workspace-artifact-empty">No artifacts detected yet. Files created or edited during this session will appear here.</div>';
293
323
  return;
294
324
  }
295
- root.innerHTML = items.map(item => `<button type="button" class="workspace-artifact-item" data-artifact-path="${esc(item.path)}" onclick="openArtifactPath(this.dataset.artifactPath)"><div class="workspace-artifact-path">${esc(item.path)}</div><div class="workspace-artifact-meta">${esc(item.source || 'session')}</div></button>`).join('');
325
+ // Strip workspace prefix for display so long absolute paths don't clutter the list.
326
+ const ws = S.session && S.session.workspace;
327
+ const normWs = ws ? ws.replace(/\/+$/,'') + '/' : '';
328
+ const displayPath = (p) => {
329
+ if(normWs && p.startsWith(normWs)) return p.slice(normWs.length);
330
+ return p;
331
+ };
332
+ root.innerHTML = items.map(item => `<button type="button" class="workspace-artifact-item" data-artifact-path="${esc(item.path)}" onclick="openArtifactPath(this.dataset.artifactPath)"><div class="workspace-artifact-path">${esc(displayPath(item.path))}</div><div class="workspace-artifact-meta">${esc(item.source || 'session')}</div></button>`).join('');
296
333
  }
297
334
 
298
335
  async function _workspacePathExists(path){
@@ -308,7 +345,15 @@ async function _workspacePathExists(path){
308
345
  async function openArtifactPath(path){
309
346
  if(!path) return;
310
347
  switchWorkspacePanelTab('files');
311
- const rel = path.replace(/^~\//,'').replace(/^\.\//,'');
348
+ let rel = path.replace(/^~\//,'').replace(/^\.\/+/,'');
349
+ // Strip workspace prefix so /api/list receives a workspace-relative path.
350
+ const ws = S.session && S.session.workspace;
351
+ if(ws){
352
+ const normWs = ws.replace(/\/+$/,'') + '/';
353
+ if(rel.startsWith(normWs)) rel = rel.slice(normWs.length);
354
+ else if(rel === ws.replace(/\/+$/,'')) rel = '.';
355
+ }
356
+ if(!rel) rel = '.';
312
357
  try{
313
358
  if(!(await _workspacePathExists(rel))){
314
359
  setStatus(t('file_open_failed'));
@@ -528,6 +573,40 @@ function cancelEditMode(){
528
573
  updateEditBtn();
529
574
  }
530
575
 
576
+ // Map file extensions to Prism.js language identifiers.
577
+ // Prism autoloader fetches missing language components from CDN on demand.
578
+ const _PRISM_LANG_MAP={
579
+ js:'javascript',mjs:'javascript',jsx:'jsx',ts:'typescript',tsx:'tsx',
580
+ py:'python',pyw:'python',pyi:'python',
581
+ rb:'ruby',go:'go',rs:'rust',java:'java',kt:'kotlin',kts:'kotlin',
582
+ c:'c',h:'c',cpp:'cpp',cxx:'cpp',hpp:'cpp',cc:'cpp',
583
+ cs:'csharp',swift:'swift',scala:'scala',
584
+ php:'php',pl:'perl',pm:'perl',r:'r',lua:'lua',
585
+ sh:'bash',bash:'bash',zsh:'bash',fish:'bash',
586
+ ps1:'powershell',psm1:'powershell',
587
+ sql:'sql',graphql:'graphql',
588
+ json:'json',yaml:'yaml',yml:'yaml',toml:'toml',xml:'xml',
589
+ html:'markup',htm:'markup',svg:'markup',vue:'markup',
590
+ css:'css',scss:'scss',sass:'sass',less:'less',
591
+ md:'markdown',markdown:'markdown',
592
+ dockerfile:'docker',makefile:'makefile',cmake:'cmake',
593
+ ini:'ini',cfg:'ini',conf:'ini',properties:'properties',
594
+ diff:'diff',patch:'diff',
595
+ txt:'',log:'',csv:'',tsv:'',
596
+ };
597
+ const _PRISM_BASENAME_LANG_MAP={
598
+ 'dockerfile':'docker','makefile':'makefile','gnumakefile':'makefile',
599
+ 'cmakelists.txt':'cmake',
600
+ '.gitignore':'ignore','.dockerignore':'ignore',
601
+ };
602
+ function _prismLanguageForPath(path){
603
+ const base=String(path||'').split(/[\\/]/).pop().toLowerCase();
604
+ if(base.startsWith('dockerfile.')) return 'docker';
605
+ if(_PRISM_BASENAME_LANG_MAP[base]!==undefined) return _PRISM_BASENAME_LANG_MAP[base];
606
+ const ext=fileExt(path).replace(/^\./,'');
607
+ return _PRISM_LANG_MAP[ext]!==undefined?_PRISM_LANG_MAP[ext]:'plaintext';
608
+ }
609
+
531
610
  async function openFile(path, opts={}){
532
611
  if(!S.session)return;
533
612
  const ext=fileExt(path);
@@ -614,7 +693,26 @@ async function openFile(path, opts={}){
614
693
  return;
615
694
  }
616
695
  showPreview('code');
617
- $('previewCode').textContent=data.content;
696
+ // Syntax highlighting with Prism.js (already loaded on the page).
697
+ const codeEl=document.createElement('code');
698
+ codeEl.textContent=data.content;
699
+ const lang=_prismLanguageForPath(path);
700
+ if(lang) codeEl.className='language-'+lang;
701
+ const pre=$('previewCode');
702
+ pre.textContent='';
703
+ // Prism.highlightElement() propagates the language-* class onto the
704
+ // parent <pre>, so a previously-previewed code file leaves e.g.
705
+ // "language-css" on #previewCode. A subsequent plain-text file builds a
706
+ // class-less <code>, and Prism walks up to that stale ancestor class and
707
+ // mis-highlights prose. Strip any inherited language-* token from the
708
+ // <pre> before each render so highlighting never leaks across files.
709
+ pre.className=pre.className.replace(/\blanguage-\S+/g,'').replace(/\s+/g,' ').trim();
710
+ pre.appendChild(codeEl);
711
+ // Only invoke Prism when we actually assigned a language; otherwise the
712
+ // class-less <code> would inherit any ancestor language-* class.
713
+ if(lang&&typeof Prism!=='undefined'&&typeof Prism.highlightElement==='function'){
714
+ Prism.highlightElement(codeEl);
715
+ }
618
716
  }catch(e){
619
717
  // If it's a 400/too-large error, offer download instead
620
718
  downloadFile(path);
@@ -675,6 +773,86 @@ function renderFileBreadcrumb(filePath) {
675
773
 
676
774
  function openInBrowser(){
677
775
  if(!_previewCurrentPath||!S.session) return;
678
- const url=`api/file/raw?session_id=${encodeURIComponent(S.session.session_id)}&path=${encodeURIComponent(_previewCurrentPath)}`;
679
- window.open(url,'_blank');
776
+ const url=`api/file/raw?session_id=${encodeURIComponent(S.session.session_id)}&path=${encodeURIComponent(_previewCurrentPath)}&inline=1`;
777
+ window.open(url,'_blank','noopener');
778
+ }
779
+
780
+ // ── Workspace upload ──────────────────────────────────────────────────
781
+ function triggerWorkspaceUpload() {
782
+ const input = $('workspaceFileInput');
783
+ if (!input) return;
784
+ input.value = '';
785
+ input.onchange = async () => {
786
+ const files = input.files;
787
+ if (!files || !files.length) return;
788
+ for (const file of files) {
789
+ await uploadToWorkspace(file, S.currentDir || '.');
790
+ }
791
+ if (S.session) loadDir(S.currentDir);
792
+ };
793
+ input.click();
794
+ }
795
+
796
+ async function uploadToWorkspace(file, dir) {
797
+ if (!S.session) return;
798
+ const formData = new FormData();
799
+ formData.append('session_id', S.session.session_id);
800
+ formData.append('path', dir || '.');
801
+ formData.append('file', file, file.name);
802
+ try {
803
+ showToast(t('uploading') || 'Uploading\u2026', 2000);
804
+ const data = await api('/api/workspace/upload', {
805
+ method: 'POST',
806
+ body: formData,
807
+ headers: {},
808
+ timeoutMs: 120000,
809
+ });
810
+ if (data && data.error) {
811
+ showToast(data.error, 5000, 'error');
812
+ } else if (data && (data.extract_error || (Array.isArray(data.files) && data.files.some(function(f){return f && f.extract_error;})))) {
813
+ // Archive was rejected (zip-slip / zip-bomb / corrupt / too-many-members):
814
+ // the file uploaded but extraction failed. Surface it as an error instead
815
+ // of a misleading "Uploaded" success toast.
816
+ var msg = data.extract_error
817
+ || (data.files.find(function(f){return f && f.extract_error;}) || {}).extract_error
818
+ || 'Archive extraction failed';
819
+ showToast(msg, 5000, 'error');
820
+ } else {
821
+ showToast(t('uploaded') || ('Uploaded ' + (data.filename || file.name)), 2000);
822
+ }
823
+ } catch (e) {
824
+ showToast(t('upload_failed') || ('Upload failed: ' + e.message), 5000, 'error');
825
+ }
826
+ }
827
+
828
+ // Drag-and-drop files onto workspace file tree
829
+ if (typeof document !== 'undefined') {
830
+ const _wsUploadInit = () => {
831
+ const tree = $('fileTree');
832
+ if (!tree) return;
833
+ tree.addEventListener('dragover', (e) => {
834
+ if (e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.includes('Files')) {
835
+ e.preventDefault();
836
+ e.dataTransfer.dropEffect = 'copy';
837
+ tree.classList.add('drag-over-upload');
838
+ }
839
+ });
840
+ tree.addEventListener('dragleave', () => {
841
+ tree.classList.remove('drag-over-upload');
842
+ });
843
+ tree.addEventListener('drop', async (e) => {
844
+ tree.classList.remove('drag-over-upload');
845
+ if (!e.dataTransfer || !e.dataTransfer.types || !e.dataTransfer.types.includes('Files')) return;
846
+ e.preventDefault();
847
+ for (const file of e.dataTransfer.files) {
848
+ await uploadToWorkspace(file, S.currentDir || '.');
849
+ }
850
+ if (S.session) loadDir(S.currentDir);
851
+ });
852
+ };
853
+ if (document.readyState === 'loading') {
854
+ document.addEventListener('DOMContentLoaded', _wsUploadInit, {once: true});
855
+ } else {
856
+ _wsUploadInit();
857
+ }
680
858
  }
@@ -1,76 +0,0 @@
1
- # Hermes WebUI 定制版
2
-
3
- 这是 Hermes WebUI 的定制版本,包含优化的用户界面和增强功能。
4
-
5
- ## 🚀 一键安装 (推荐)
6
-
7
- ### Linux / macOS 用户
8
- ```bash
9
- curl -fsSL https://your-domain.com/install.sh | bash
10
- ```
11
-
12
- ### Windows 用户
13
- 请使用 WSL2 或虚拟机,然后运行上面的命令
14
-
15
- ## 📦 手动安装
16
-
17
- 如果一键安装失败,可以手动安装:
18
-
19
- ```bash
20
- # 1. 克隆项目
21
- git clone -b dev-zkp https://github.com/yourusername/hermes-webui.git
22
- cd hermes-webui
23
-
24
- # 2. 运行自动安装
25
- python3 bootstrap.py
26
-
27
- # 3. 启动服务
28
- ./start.sh
29
- ```
30
-
31
- ## 🎯 使用方法
32
-
33
- 1. 安装完成后,访问 http://localhost:8787
34
- 2. 首次使用需要配置 AI 提供商:
35
- - OpenAI、Anthropic、Google 等
36
- - 在设置中添加 API 密钥
37
- 3. 开始聊天使用
38
-
39
- ## ✨ 定制功能
40
-
41
- - 🎨 全新设计主题
42
- - 🖼️ 定制 logo 和图标
43
- - 📱 优化的移动端体验
44
- - 🔧 增强的扩展系统
45
- - ⚡ 性能优化
46
-
47
- ## 🔧 系统要求
48
-
49
- - **操作系统**: Linux, macOS, 或 WSL2
50
- - **Python**: 3.11 或更高版本
51
- - **内存**: 建议 2GB 以上
52
- - **网络**: 需要访问 AI 提供商 API
53
-
54
- ## 🆘 常见问题
55
-
56
- ### Q: 安装失败怎么办?
57
- A: 检查 Python 版本,确保有网络连接,或尝试手动安装
58
-
59
- ### Q: 如何更新?
60
- A: 进入安装目录,运行 `git pull` 然后重启服务
61
-
62
- ### Q: 忘记密码怎么办?
63
- A: 删除 `~/.hermes/webui/settings.json` 文件,重新设置
64
-
65
- ### Q: 如何备份数据?
66
- A: 备份 `~/.hermes` 目录
67
-
68
- ## 📞 技术支持
69
-
70
- - 📧 邮箱: your-email@example.com
71
- - 💬 微信群: [扫码加入]
72
- - 🐛 问题反馈: https://github.com/yourusername/hermes-webui/issues
73
-
74
- ## 📄 许可证
75
-
76
- MIT License - 详见 LICENSE 文件
@@ -1,26 +0,0 @@
1
- version: '3.8'
2
-
3
- services:
4
- hermes-webui-custom:
5
- image: ghcr.io/yourusername/hermes-webui:custom
6
- container_name: hermes-webui-custom
7
- restart: unless-stopped
8
- ports:
9
- - "127.0.0.1:8787:8787"
10
- environment:
11
- - HERMES_WEBUI_HOST=0.0.0.0
12
- - HERMES_WEBUI_PASSWORD=${HERMES_WEBUI_PASSWORD:-}
13
- - WANTED_UID=${UID:-1000}
14
- - WANTED_GID=${GID:-1000}
15
- volumes:
16
- - hermes-data:/home/hermeswebui/.hermes
17
- - ${HOME}/workspace:/workspace
18
- healthcheck:
19
- test: ["CMD", "curl", "-f", "http://127.0.0.1:8787/health"]
20
- interval: 30s
21
- timeout: 10s
22
- retries: 3
23
-
24
- volumes:
25
- hermes-data:
26
- driver: local