Skip to content
Pingvin1230Public

About

Modern web interface for Hermes Agent - AI session management with real-time streaming, tool visualization, and responsive design

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

46 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

1230-UI β€” Hermes Web Interface

CI

Status: v0.9.3 β€” Workspace (multi-session / multi-executor) (tabbed surface: Sessions / Hermes / OpenCode; each executor runs its own concurrently-active session; switching tabs/pages never interrupts an in-flight turn) Β· OpenCode connector overhaul (several data-loss/crash bugs fixed; inline tool-call support via OPENCODE_AUTO_APPROVE_TOOLS) Β· Per-session streaming (streams survive navigation; two concurrent streams coexist) Β· Multi-Executor Support (Hermes + OpenCode pluggable backends, per-assistant executor, Settings UI for OC config) Β· Tududi Application (tasks / notes / projects inside the right split-pane, server-side proxy, bearer token never reaches the browser) Β· Cloud Connect (WebDAV file picker + inline content expansion) Β· Model Routing Overhaul Β· Applications architecture Β· File Preview Β· File Manager Β· Mobile layout overhaul Β· Chat UX improvements Β· Session file indicators Β· Onboarding banner Β· Jargon-free UI terminology Β· File upload & agent file download Β· Assistants Phase 2 Β· Backend modularization Β· Code-audit hardening pass (security fixes, ChatPage refactor with message-list virtualization, global event bus β†’ typed store actions, central error handling + SSE-aware logging, completed i18n across 4 locales, soft runtime response validation; 305 tests).

Modern web interface for managing sessions and interacting with Hermes Agent through a browser.

Features

  • Dashboard β€” quick chat, recent sessions, Hermes API status
  • Workspace (multi-session, multi-executor) β€” the primary area is now a tabbed surface at /sessions (Sessions / Hermes / OpenCode) where each executor runs its own concurrently-active session. Both executor chats are mounted simultaneously, so a running stream in one survives switching to the other. Switching sessions, tabs, or pages never interrupts an in-flight turn. /chat/:id resolves the session's executor and opens the Workspace on the matching tab. See docs/WORKSPACE.md.
  • Session Management β€” create, rename, pin, archive, delete, bulk actions, swipe-to-delete (mobile), long-press to enter bulk mode (mobile). The sessions list lives in the Workspace's Sessions tab; clicking a session opens it in its executor's tab.
  • Applications Pane β€” split-pane on desktop (β‰₯ 1024 px) with chat on the left and applications on the right, nested inside the Workspace (alongside the Sessions / executor tabs). Extensible plugin system: add new applications by registering a React component. Toggle pane visibility from the Workspace header. Hidden on mobile. Manage applications from Settings page (enable/disable, reorder). Shipped apps: File Preview, File Manager, Cloud Connect, Tududi (tasks / notes / projects).
  • Tududi Application β€” read/write access to a self-hosted Tududi instance (default https://todo.thinkout.ru) inside the right split-pane. Three tabs: Tasks (grouped by project, status filter, due date, priority, project filter, recurring-task name fix), Notes (project filter chips, 2-column card grid, inline editor with live Markdown preview, auto-save 1 s, 10-colour picker, search + sort), Projects (card grid with progress bars and navigation to the filtered Tasks / Notes view). All requests go through the server-side proxy at /api/tududi/*; the bearer token is read from TUDUDI_API_TOKEN and never reaches the browser. Settings page (/settings/tududi) shows connection status, proxy URL, and upstream URL. See docs/TUDUDI_INTEGRATION.md.
  • File Preview β€” inline preview for all session files in the Applications pane. Supports images (png, jpg, gif, webp), code files (py, js, ts, jsx, tsx, sh, sql, xml, yml, yaml, css) with syntax highlighting, markdown, JSON, CSV (as table), HTML (sandboxed iframe), PDF. Click file in Navbar dropdown to open preview.
  • File Manager β€” global file management across all sessions. View all files with disk usage stats, sort by name/date/size/expiration, filter by type (images, code, documents, expiring soon). Extend file lifetime to prevent auto-deletion. Delete files globally. Click file to navigate to session and open in File Preview.
  • File Retention Policy β€” automatic cleanup of expired files. Configurable via FILE_RETENTION_DAYS environment variable (default 30 days). Files expire after N days from upload. Expired files deleted on startup and every hour. Extend individual files to keep them longer.
  • Real-time Chat β€” streaming responses, markdown rendering, syntax highlighting, tool calls visualization
  • Per-session streaming β€” stream ownership moved out of ChatPage into chatInputStore (module-level AbortController map + liveMessages slice). Navigating to another session, executor tab, or page (e.g. /settings) no longer aborts an in-flight turn. Two concurrent streams (one per executor) coexist; only the Stop button, turn completion, or a real browser disconnect ends a stream.
  • Multi-Executor Backends β€” each assistant picks between Hermes (default, in-process Python) and OpenCode (separate opencode serve daemon, per-tool stream). Executor is locked for the session lifetime; live status dot in the top bar; full Settings β†’ Executor Configuration panel with password encryption. Adding a third backend is a one-file change. See docs/EXECUTOR_ADAPTERS.md.
  • File Attachments β€” attach files to a chat message via the paperclip button or by dragging them onto the chat area. Allowed: .txt .md .py .js .ts .jsx .tsx .json .csv .yml .yaml .log .html .css .xml .sh .sql .pdf .png .jpg .jpeg .gif .webp. Limits: 50 MB per file, 5 files per message. Files live in data/uploads/<session_id>/ and are cleaned up automatically when the session is deleted.
  • Agent File Download β€” when the agent creates or writes a file and mentions its absolute path in the response (e.g. `/tmp/report.md`), a download card appears directly inside the assistant message with the filename, size, and a Download button. Multiple files from one message are grouped in a collapsible container. Download cards persist across page reloads. If the file is deleted from disk, the endpoint returns 404 with a clear message.
  • Model Management β€” enable/disable models, select default model
  • Provider Keys β€” manage API keys for all bundled api_key providers from the UI (no terminal needed)
  • System Commands β€” execute hermes update and hermes doctor --fix
  • Hermes API Status β€” header indicator (green/red/gray) with live polling every 60s
  • Assistants β€” named bundles (name, color, icon, model, style, depth, system prompt, executor) that show up as tiles on the New Session page. Create / edit / archive / duplicate / restore from /assistants. Tile grid (1/2/3 cols), context menu (MoreVertical), tab filters with counts. Style badges (πŸ’¬ Friendly Β· πŸ“‹ Formal Β· βœ‚οΈ Concise Β· 🎨 Creative) and depth indicators (●○○ Quick Β· ●●○ Standard Β· ●●● Thorough) on tiles. System prompt (≀ 4000 chars) injected into every chat turn. Editing a bundle that already has sessions forks it (the old version is archived; existing sessions keep the original reference). Duplicating opens the editor prefilled with the source β€” nothing is written until you click "Create copy"
  • Keyboard Shortcuts β€” Ctrl+K (search), Ctrl+N (new session), Ctrl+Enter (send)
  • Browser Notifications β€” alerts for new messages (toggle in sidebar)
  • Dark/Light Themes β€” with saved preference (toggle in sidebar)
  • Internationalization β€” 4 languages (English, Русский, EspaΓ±ol, Deutsch) with auto-detection
  • Mobile-First UX β€” 44 Γ— 44 px touch targets, fluid clamp(14px β†’ 16px) typography, iOS safe-area insets, icon-only header buttons on < md, flex-wrap confirm/header rows, no horizontal scroll at 360 px
  • Security β€” rate limiting, XSS protection, CORS, security headers, rate-limited provider-key writes

Architecture

1230UI is a thin Node.js front-end that brokers a chat conversation with Hermes Agent by spawning a short-lived Python subprocess (run_chat.py) per user message. This is not an HTTP proxy to Hermes β€” the wrapper constructs an AIAgent directly with the exact model/provider the user picked in the UI.

   Browser
   β”‚  POST /api/chat  (SSE out)
   β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  Node.js 1230UI  (Express 5, better-sqlite3)           β”‚
   β”‚  routes/chat.js  ──►  child_process.spawn('python')     β”‚
   β”‚                                  β”‚                      β”‚
   β”‚                                  β–Ό  NDJSON on stdout    β”‚
   β”‚  ◄── SSE to browser (translates events) ──             β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                  β”‚
                                  β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  Python: run_chat.py  (inside Hermes Agent venv)       β”‚
   β”‚  resolve_runtime_provider(model, provider)              β”‚
   β”‚         β”‚                                              β”‚
   β”‚         β–Ό                                              β”‚
   β”‚  AIAgent(model=model, provider=provider,               β”‚
   β”‚          base_url=..., api_key=..., session_id=...)    β”‚
   β”‚         β”‚                                              β”‚
   β”‚         β–Ό  yields delta / reasoning / tool events      β”‚
   β”‚  emit NDJSON line per event                             β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                  β”‚
                                  β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  LLM provider (OpenAI-compatible)                      β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Why this exists. Hermes's api_server (gateway/platforms/api_server.py) resolves its model via _resolve_gateway_model() and ignores the per-request model sent by 1230UI. The UI could display "MiniMax-M3" while the LLM answered as whatever was in config.yaml.model.default. By spawning AIAgent(...) directly we bypass that path entirely.

Request lifecycle.

  1. Browser sends POST /api/chat with {messages, session_id, model, stream: true}.
  2. routes/chat.js:343 validates the request, registers an in-flight dedup key (routes/chat.js:387-406), then spawn(HERMES_PYTHON, ['-u', 'run_chat.py', '--model', m, '--provider', p, ...]) (routes/chat.js:451-471). The body is sent on stdin as a JSON blob ({message, history, provider, model, session_id}).
  3. run_chat.py resolves the runtime provider, opens the AIAgent with explicit kwargs, and streams NDJSON events back.
  4. The Node side reads the pipe line by line, translates each event to an SSE data: {…}\n\n frame, and forwards to the browser. delta events accumulate text in the chat UI; tool_start/tool_complete drive the tool-call sidebar; done carries final_response + usage; the data: [DONE]\n\n sentinel closes the stream cleanly.
  5. On browser disconnect: req.on('close') sends SIGTERM, waits 5 s, then SIGKILL. No orphan Python processes.

Model selection mechanism. The frontend sends model: <modelId> in the request body. The backend calls getProviderFromModel(model) (db/helpers.js:79-93), which does a JOIN models m ON providers p to resolve the provider slug (minimax, opencode-go, …). That slug is what the Python side passes to resolve_runtime_provider(requested=provider, target_model=model), which is the same function Hermes's own api_server calls β€” but with the explicit target_model, the credential lookup keys on the user-selected model, not config.yaml.default.

Operational characteristics. Subprocess lifetime ≀ 10 min (hard ceiling, routes/chat.js:506); 90 s no-output watchdog kills stalled children (routes/chat.js:499); in-flight dedup map (routes/chat.js:322) prevents duplicate requests within 30 s. See docs/ARCHITECTURE.md for the full design and TROUBLESHOOTING.md for the bug-by-bug writeup that motivated the redesign.

OpenCode executor (second runtime, opt-in per assistant)

1230UI supports a second chat executor alongside Hermes: OpenCode running as a separate opencode serve daemon. Each assistant carries an executor column ('hermes' | 'opencode-1230', default 'hermes'); the executor is locked for the session's lifetime (fork-on-edit preserves the binding).

   Browser
   β”‚  POST /api/chat
   β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  Node.js 1230UI β€” routes/chat.js (the dispatcher / core)        β”‚
   β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
   β”‚  β”‚  ADAPTERS['hermes']      β”‚   β”‚  ADAPTERS['opencode-1230']β”‚    β”‚
   β”‚  β”‚  HermesAdapter           β”‚   β”‚  OpenCodeAdapter         β”‚     β”‚
   β”‚  β”‚  (lib/adapters/hermes.js)β”‚   β”‚  (lib/adapters/opencode.js)β”‚   β”‚
   β”‚  β”‚  spawn run_chat.py +     β”‚   β”‚  HTTP + SSE to           β”‚     β”‚
   β”‚  β”‚  NDJSON on stdio         β”‚   β”‚  opencode serve:4097     β”‚     β”‚
   β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚
   β”‚             β”‚                              β”‚                     β”‚
   β”‚  Owns: SSE headers, 90s/10min watchdog,    β”‚                     β”‚
   β”‚  INFLIGHT dedup, persistHermesMessage,     β”‚                     β”‚
   β”‚  detectAgentFiles, [DONE] sentinel         β”‚                     β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚                              β”‚
                 β–Ό                              β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚ Python: run_chat.py  β”‚      β”‚ opencode serve (Go binary)   β”‚
   β”‚ (Hermes Agent venv)  β”‚      β”‚ 127.0.0.1:4097               β”‚
   β”‚ AIAgent(model=...)   β”‚      β”‚ POST /session                β”‚
   β”‚ NDJSON on stdout     β”‚      β”‚ POST /session/:id/prompt_asyncβ”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚ GET  /event (SSE)            β”‚
                                  β”‚ PATCH /session/:id  (v0.9.3) β”‚
                                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The dispatcher (routes/chat.js) resolves the executor from session_meta.assistant_id β†’ assistants.executor and does a single for await (const evt of adapter.chat(ctx)) writeSse(evt) loop β€” adapters are pure event generators and never touch req/res, the INFLIGHT map, or persistence. Adding a third executor is a one-file change β€” see docs/EXECUTOR_ADAPTERS.md for the step-by-step guide.

Configuration lives in Settings β†’ Executor Configuration (URL, username, password; password is AES-256-GCM encrypted at rest, with the same CLOUD_CONNECT_KEY used for Cloud Connect). The OpenCode daemon itself runs as a systemd service β€” see docs/INSTALLATION.md Β§OpenCode for the install steps and docs/EXECUTOR_ADAPTERS.md for the dispatcher contract.

Status indicator. A small dot in the top bar (next to the existing Hermes indicator) shows live OpenCode health; grey when the daemon is unreachable, green when ready, red on the last failed probe. The polling hook (src/hooks/useOpenCodeStatusPoll.ts) hits GET /api/system/executors every 60 s.

Workspace (multi-session / multi-executor)

The primary area is a Workspace: a tabbed surface at /sessions with three tabs β€” Sessions (the sessions list), Hermes, and OpenCode (one chat tab per executor). ChatPage is dual-mounted so both executor chats live simultaneously; a running stream in one executor survives switching to the other tab. /chat/:id resolves the session's executor (src/components/ChatRouteResolver.tsx) and opens the Workspace on the matching tab, so deep links and the mobile nav keep working.

   /sessions                    /chat/:id
       β”‚                           β”‚
       β–Ό                           β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  Workspace  (src/components/Workspace.tsx)     β”‚
   β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”             β”‚
   β”‚  β”‚Sessionsβ”‚ Hermes β”‚ OpenCode   β”‚  ← tabs     β”‚
   β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜             β”‚
   β”‚  workspaceStore: activeTab +                  β”‚
   β”‚    activeSessionByExecutor (localStorage)     β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚                   β”‚
           β–Ό                   β–Ό
   sessions list      ChatPage (dual-mounted)
                      β”‚
                      β–Ό
   chatInputStore (per-session stream store)
     β€’ startStream / stopStream
     β€’ module-level streamControllers: Map<session, AbortController>
     β€’ liveMessages slice (pendingUserContent, agentFiles)

Stream ownership lives in chatInputStore (src/store/chatInputStore.ts), not in ChatPage β€” ChatPage is now a subscriber/renderer. Because the AbortController is held at module level keyed by session, navigation never aborts an in-flight turn; two concurrent streams (one per executor) coexist, and only the Stop button, turn completion, or a real browser disconnect ends a stream. Per-executor toolbars (src/components/ExecutorToolbar.tsx) show the last 3 sessions of that executor as quick-switch pills; executor status dots (src/components/ExecutorStatusDot.tsx) live in the tab header; and session controls (assistant/model badges, session files, applications-pane toggle, delete-session) moved from the Navbar into the Workspace header (src/components/WorkspaceSessionControls.tsx), shown only when an executor tab has an active session. See docs/WORKSPACE.md for the full design.

Tududi Application (third-party task / notes manager)

1230UI ships with a Tududi application that lives in the right split-pane and proxies a self-hosted Tududi instance through the 1230-UI backend. The browser never holds the bearer token.

   Browser  (1230-UI tab)
   β”‚
   β”‚  fetch('/api/tududi/tasks')         (no Authorization header)
   β–Ό
   Node.js 1230-UI  (Express 5)
   β”‚  routes/tududi.js
   β”‚  β€’ strips hop-by-hop, set-cookie, content-encoding, authorization
   β”‚  β€’ adds Authorization: Bearer <TUDUDI_API_TOKEN>  (read from .env)
   β”‚  β€’ 15 s AbortController timeout
   β”‚  β€’ generic method/body/header pass-through
   β–Ό
   Tududi  (https://todo.thinkout.ru)
   β”‚  /api/tasks, /api/notes, /api/projects, /api/tags, …
   β””  returns JSON β†’ proxied back verbatim

The GET /api/tududi/health endpoint probes /api/profile (5 s timeout) and returns { configured, reachable, status } β€” the only call a curious user needs to verify the wiring. If TUDUDI_API_TOKEN is unset, the proxy returns 503 tududi_not_configured and the app shows a red status dot in the Tududi tab header.

The frontend (src/applications/tududi/) covers three tabs β€” Tasks (project filter, status filter, due date / priority, TaskDetail with status control / subtasks / tags), Notes (project chips, card grid, inline Markdown editor with 1 s auto-save, colour picker, search + sort), Projects (cards with progress bars, navigation to the filtered Tasks / Notes view). Recurring-task names are displayed via displayName(task) because Tududi's API returns "Monthly" / "Daily" on instances and stores the real title in original_name. See docs/TUDUDI_INTEGRATION.md for the full proxy contract, observed Tududi API quirks (singular vs plural paths, parent_task_id numeric only, bare objects on write, due_date is YYYY-MM-DD), and remaining work.

Configuration

1230UI is configured via .env (see .env.example for the full list, validated by Zod at startup in config.js). The variables that matter for the model-routing path:

Variable Required Default Notes
HERMES_PYTHON_PATH yes (in practice) python3 Absolute path to the Python interpreter inside the Hermes Agent venv. The new run_chat.py wrapper imports run_agent, hermes_cli.runtime_provider, and hermes_state β€” these are only on the venv's sys.path, so system python3 will fail with ModuleNotFoundError. Set this to /usr/local/lib/hermes-agent/venv/bin/python (or wherever the venv lives in your install).
HERMES_API_URL yes (legacy) http://127.0.0.1:8642 URL of the Hermes api_server. The chat path no longer uses it (the Python wrapper is invoked directly), but it is still loaded by config.js for compatibility and is used by the /api/system/status health probe.
OPENCODE_URL optional http://127.0.0.1:4097 URL of the opencode serve daemon. Can be overridden from /settings/executors/opencode at runtime (encrypted password at rest).
TUDUDI_API_URL optional https://todo.thinkout.ru Base URL of the Tududi instance. The Tududi app tab stays hidden behind the red status dot when TUDUDI_API_TOKEN is not set.
TUDUDI_API_TOKEN optional (unset) Bearer token for the Tududi proxy. The tt_… token never leaves the server.

All other variables are listed in docs/CONFIGURATION.md.

Screenshots

Home
Session chat with split-pane workspace
File Preview application
File Manager application

Home with Quick Chat, Session chat with split-pane workspace (chat + applications), File Preview (inline preview for images, code, markdown, PDF, CSV, HTML), and File Manager (global file management with retention policy). The same pane also hosts Cloud Connect and Tududi (Tasks / Notes / Projects).

Quick Start

# Clone repository
git clone https://github.com/Pingvin1230/1230-ui.git
cd 1230-ui

# Run installation script
./install.sh

Or manually:

git clone https://github.com/Pingvin1230/1230-ui.git
cd 1230-ui
npm install
cp .env.example .env
# Edit .env with your settings
npm run build
node server.js

Application will be available on port 3001.

Documentation

Tech Stack

Frontend: React 19, TypeScript 6, Vite 8, Tailwind CSS v4, Zustand, React Router v7

Backend: Node.js, Express 5, better-sqlite3, multer

Testing: Vitest β€” npm test (305 tests)

CI/CD: GitHub Actions β€” lint + typecheck + test + build on every push/PR

Infrastructure: PM2, Nginx, Authelia, Let's Encrypt

Requirements

  • Node.js 22+ (18+ minimum)
  • Python 3.x
  • Hermes Agent (installed and configured)

License

MIT License - see LICENSE file for details.

Disclaimer

This software is provided "as is" without warranty of any kind. Use at your own risk.

About

Modern web interface for Hermes Agent - AI session management with real-time streaming, tool visualization, and responsive design

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages