mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-24 18:50:15 +00:00
96ca83bf539de98ec40b0461ba227199a2f486df
16 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5272215e7c | docs: clarify Anthropic auth choices in onboarding | ||
|
|
e509faec44 | feat: link Claude Code OAuth in onboarding | ||
|
|
9a0a6214cf |
fix: guard localStorage.setItem('hermes-webui-model') against QuotaExceededError
On some setups the localStorage quota is exhausted; the bare setItem call throws an unhandled DOMException that breaks model selection and prevents the chat UI from loading. Wrap both call-sites (boot.js model-select onChange, onboarding.js _saveOnboardingDefaults) in try/catch so the error is logged to the console as a warning instead of surfacing as a fatal exception. Fixes: 'Failed to execute setItem on Storage: Setting the value of hermes-webui-model exceeded the quota.' |
||
|
|
259c5c4afb | feat: add Codex OAuth onboarding flow | ||
|
|
ac3d336875 |
fix: onboarding API-key input loses focus when probe completes (#1503)
The onboarding wizard's API-key input calls _scheduleOnboardingProbe() on every keystroke (oninput). When the 400ms-debounced probe completes, _setOnboardingProbeState() calls _renderOnboardingBody() which rebuilds the entire form — destroying and recreating the <input> element. The user's focus and cursor position are lost. On fast connections (localhost) the probe completes between keystrokes so the bug window is narrow. On slow networks (VPN, corporate proxy, cold-start vLLM) the re-render routinely lands mid-typing. Fix: remove _scheduleOnboardingProbe() from the api-key input's oninput handler. The probe still fires on: - baseUrl input change (oninput + debounce, unchanged) - api-key field blur (onblur, added) - 'Test connection' button click (unchanged) - nextOnboardingStep() before Continue (unchanged) The baseUrl input retains the oninput probe because the UX trade-off is acceptable there (text input preserves visible content on re-render). |
||
|
|
8f4692b8cf |
fix(onboarding): allow keyless setup for self-hosted providers (#1499 third sub-bug)
Pre-fix, the wizard rejected an empty api_key for every provider in _SUPPORTED_PROVIDER_SETUPS — including lmstudio, ollama, and custom, which run keyless on the vast majority of local installs. The agent's LMSTUDIO_NOAUTH_PLACEHOLDER substitution at chat-time was the workaround for the no-auth case, but the wizard side rejected the empty input first. Users had to type random gibberish into the API key field to clear the form — the third sub-bug from #1420 that the prior commit's PR description explicitly punted to a follow-up. Surfaced by Nathan during PR review: "I think it's too weird for users to have to type a string into the API key field, right?" Yes — and the probe (#1499) makes the cleanest fix strictly better: we accept empty keys, and the probe gives instant feedback ("Connected. 2 model(s) available." for keyless servers, "401" for auth-required servers). Backend changes --------------- * `api/onboarding.py` — `_SUPPORTED_PROVIDER_SETUPS` gains `key_optional: True` for `lmstudio`, `ollama`, `custom`. Cloud providers (openrouter, anthropic, openai, gemini, deepseek, …) remain key_required. * `apply_onboarding_setup` skips the "{env_var} is required" check when `key_optional` is set AND no key is supplied. No write to .env for the empty-key case (no `LM_API_KEY=*** placeholder lying in the user's .env`). * `_status_from_runtime` reports `provider_ready=True` for key_optional providers based on `requires_base_url` alone, so the wizard doesn't refire on the next page load just because there's no api_key. Cloud providers still need a key for provider_ready=True. * `_build_setup_catalog` exposes the `key_optional` flag to the frontend. Frontend changes ---------------- * `static/onboarding.js` — new `_renderOnboardingApiKeyField()` helper. For key_optional providers: - Label: "API key (optional)" - Placeholder: "Leave blank for keyless servers" - Inline italic muted help: "Most LM Studio / Ollama / vLLM installs run keyless — leave this blank if your server doesn't require authentication. Use the Test connection button to verify." For cloud providers: unchanged (label "API key", standard placeholder, no help block). * The api-key input also now triggers `_scheduleOnboardingProbe()` on oninput, so changing the key re-runs the probe — handles "the server rejected my empty key with 401, let me add one and retry." * `static/i18n.js` — 3 new keys × 9 locales (canonical English in `en`, English fallback with `// TODO: translate` markers in the other 8). * `static/style.css` — `.onboarding-api-key-help` rule for the muted italic helper paragraph. Verified end-to-end on port 8789 -------------------------------- Spun up an isolated test server + a mock LM Studio at `127.0.0.1:11234/v1/models`. Stepped through the wizard: * Picked LM Studio → field label flipped to "API key (optional)", placeholder showed "Leave blank for keyless servers", help text rendered in italic muted gray below. * Switched to Anthropic → label reverted to "API key", help text disappeared. Visual hierarchy correct. * Left api_key blank, set base_url to the mock, clicked Test connection → green "Connected. 2 model(s) available." banner. Probe-discovered models populated the workspace-step dropdown. * Continued through to the finish step. config.yaml written with provider/model/base_url. **`.env` does NOT exist** — no placeholder string written. `chat_ready: true`, `state: ready`. * Vision tool confirmed the visual hierarchy: subtle italic help reads as documentation, prominent green banner pops as status. Tests ----- `tests/test_issue1499_keyless_onboarding.py` — 16 tests in 3 classes: TestKeyOptionalProviderSchema (5) - lmstudio / ollama / custom declare key_optional=True - openrouter / anthropic / openai do NOT (regression defense) - setup catalog exposes the flag TestKeylessOnboarding (6) - lmstudio / ollama / custom: empty api_key accepted, no .env write - openrouter / anthropic: empty api_key still rejected - lmstudio with explicit key still writes .env (regression defense) TestKeylessChatReady (5) - lmstudio / ollama: provider_ready=True with no key - custom: provider_ready=True with key+base_url, False without base_url - openrouter: provider_ready=False with no key (regression defense) - End-to-end get_onboarding_status reports chat_ready=True Full suite: 3901 → 3917 passing (+16 from this commit; +22 cumulative from the PR's earlier commit). 0 failures. Closes #1499 (all three sub-bugs from #1420 now addressed) |
||
|
|
8616033605 |
fix(onboarding,providers): probe LM Studio /models + align env var with agent CLI (#1499 #1500)
Addresses both #1499 (onboarding wizard never probes the configured base URL) and #1500 (cross-tool env-var name divergence between webui and agent CLI). Surfaced together because they're both LM-Studio onboarding bugs that pile on top of each other — fixing only one leaves the broken UX. #1499 — Onboarding wizard probes <base_url>/models before persisting Pre-fix, `apply_onboarding_setup` accepted whatever `base_url` the user typed without ever fetching `<base_url>/models`. @chwps's log timeline in #1420 showed the wizard finishing in 239ms with zero outbound HTTP — onboarding silently persisted unreachable URLs and left users with empty model dropdowns they had to populate by hand-editing config.yaml. Backend: * New `probe_provider_endpoint(provider, base_url, api_key, timeout=5.0)` in `api/onboarding.py`. Stdlib-only (urllib + socket — no httpx dep). Returns `{ok, models}` on success; `{ok: False, error: <code>, detail}` on failure with stable error codes the frontend can switch on: invalid_url, dns, connect_refused, timeout, http_4xx, http_5xx, parse, unreachable. 256 KB response cap and 5s timeout keep a hostile or mis- pointed endpoint from blocking the wizard. * New `POST /api/onboarding/probe` route — thin JSON wrapper around the function above. Same local-network gate as `/api/onboarding/setup` because the body carries an `api_key` the user typed. * The probe response is NEVER persisted. Only the user's typed selection ends up in config.yaml; the probed model list just populates the wizard's dropdown. * SSRF: deliberately does NOT block private-IP ranges. The wizard is gated behind WebUI auth and the legitimate target IS a local LM Studio / Ollama / vLLM server. A "block private IPs" SSRF defense would make the feature useless for its primary use case. Frontend: * `static/onboarding.js`: - New `ONBOARDING.probe` state ({status, error, detail, models, probedKey}). - `_runOnboardingProbe()` — POSTs to /api/onboarding/probe, idempotent & cached on (provider, baseUrl, apiKey). - Debounced (400ms) on `oninput` of the base URL field. - Explicit "Test connection" button. - `nextOnboardingStep` blocks Continue at the setup step for any provider with `requires_base_url=True` until the probe succeeds. Same localized error renders inline. * `static/i18n.js`: 13 new keys × 9 locales (canonical English in `en`, English fallback with `// TODO: translate` markers in the other 8 — same convention as v0.50.271 #1488 voice-buttons). * `static/style.css`: probe banner + Test button styling (red-tinted error variant, green-tinted success variant, neutral probing state). Verified via manual repro on port 8789: * connect_refused → red banner, helpful "from Docker, try the host IP" hint, blocks Continue. * DNS failure → red banner, "could not resolve host '...'", blocks Continue. * Success against a mock /v1/models server → green banner, model dropdown populates from the probed list, Continue advances normally. #1500 — webui env var aligned with agent CLI (LM_API_KEY) The webui has long used `LMSTUDIO_API_KEY` for LM Studio's API key in both onboarding and Settings detection. The agent CLI runtime (hermes_cli/auth.py:177-183) reads `LM_API_KEY`. So a user who configured auth on their LM Studio instance got Settings → Providers reporting has_key=True (because webui saw its own LMSTUDIO_API_KEY) but the agent runtime ignored the key and fell back to LMSTUDIO_NOAUTH_PLACEHOLDER → 401 against the auth-enabled LM Studio server. Masked in practice for the no-auth majority. Picked Option B from the issue (defer to the agent — single source of truth) but mitigated the migration cliff by reading the legacy name as a fallback: * `api/onboarding.py:_SUPPORTED_PROVIDER_SETUPS["lmstudio"]`: - `env_var: "LM_API_KEY"` (canonical, what onboarding writes going forward). - `env_var_aliases: ["LMSTUDIO_API_KEY"]` (read-only fallback for pre-#1500 users so detection keeps working without forcing an .env rewrite). * `api/onboarding.py:_provider_api_key_present` reads aliases too. * `api/providers.py:_PROVIDER_ENV_VAR["lmstudio"] = "LM_API_KEY"`. * `api/providers.py:_PROVIDER_ENV_VAR_ALIASES["lmstudio"] = ("LMSTUDIO_API_KEY",)` — new dict, used by `_provider_has_key` and `get_providers`'s key_source resolution. Drops in cleanly when other providers later rename their env vars too. Verified: ``` before fix: webui writes LMSTUDIO_API_KEY → agent ignores it → 401 on chat after fix: webui writes LM_API_KEY → agent picks it up → chat works pre-#1500 .env with LMSTUDIO_API_KEY → still has_key=True in Settings → key_source='env_file' ``` Tests * `tests/test_issue1499_onboarding_probe.py` — 17 tests: 3 invalid_url variants, dns, connect_refused, success (OpenAI shape), success (bare-list shape), http_4xx, http_5xx, parse non-JSON, parse wrong-shape, api_key authorization header passthrough, "probe must not write to config.yaml or .env", PROBE_ERROR_CODES contract pin, 3 end-to-end route-level smoke tests against the live server fixture. * `tests/test_issue1500_lmstudio_env_var_alignment.py` — 5 tests: onboarding declares LM_API_KEY canonical with LMSTUDIO_API_KEY alias, onboarding writes ONLY the canonical name, legacy env var still detected post-migration, canonical takes precedence when both are set, _provider_api_key_present reads aliases. * `tests/test_issue1420_lmstudio_provider_env_var.py` — updated: the original 5-test #1420 suite now pins LM_API_KEY as canonical and LMSTUDIO_API_KEY as alias. Full suite: 3879 → 3901 passing (+22), 0 failures. Out of scope (explicitly NOT addressed here) The third LM Studio onboarding sub-bug from #1420's thread — that `apply_onboarding_setup` requires a non-empty api_key for lmstudio even though most LM Studio installs run keyless — remains. The agent's `LMSTUDIO_NOAUTH_PLACEHOLDER` substitution kicks in at runtime, but the onboarding wizard rejects the empty-key case at submit. Fixing this requires a UX decision (auto-write a sentinel? loosen the required-key check for self-hosted providers?) and is left as a separate follow-up. Closes #1499 Closes #1500 Co-authored-by: chwps <106549456+chwps@users.noreply.github.com> Co-authored-by: AdoneyGalvan <25235323+AdoneyGalvan@users.noreply.github.com> |
||
|
|
8ae198e88c |
feat: P2 improvements — cron history, toolsets per session, Codex OAuth
- #468: Cron run history — GET /api/crons/history (metadata listing) + GET /api/crons/run (full output), lazy-load on click in Tasks panel - #493: Per-session toolset override — Session.enabled_toolsets field, POST /api/session/toolsets endpoint, streaming handler override, composer chip UI with dropdown (matches reasoning chip pattern) - #1362: In-app Codex OAuth — device-code flow (stdlib only, no httpx), SSE polling endpoint, onboarding wizard login button - #1240: Design proposal comment for provider/model source-of-truth |
||
|
|
6c343aff84 |
v0.50.210: gpt-5.5, cron titles, agent cache, bfcache fix, onboarding fix, mermaid CSP, PWA auth (#1056)
* feat(models): add gpt-5.5 to openai, openai-codex, copilot catalogs Adds GPT-5.5 and GPT-5.5 Mini entries to the static _PROVIDER_MODELS catalog so they appear in the model picker for the openai, openai-codex, and copilot providers. Signed-off-by: Pix (PiClaw, claude-opus-4-7) via Hermes Agent * fix(models): add gpt-5.5-mini to copilot provider catalog * fix(renderer): suppress Mermaid Google Fonts CSP violation via fontFamily inherit (#1044) Mermaid's built-in 'dark' and 'default' themes inject an @import for fonts.googleapis.com/Manrope into every generated SVG. The CSP style-src only allows cdn.jsdelivr.net, so this request is blocked on every diagram render, filling the console with CSP errors. Fix: pass fontFamily:'inherit' (and fontSize:'14px') in the themeVariables block of mermaid.initialize() in renderMermaidBlocks(). This suppresses Mermaid's external font import and uses the page's existing font stack. Avoids adding fonts.googleapis.com to the CSP — no new external dependency, no font FOUT, consistent with the rest of the UI typography. 3 regression tests added in tests/test_1044_mermaid_csp_font.py. 2215/2215 tests passing. * fix(onboarding): non-standard provider/path cluster (#1029) * fix(bfcache): restore full layout on tab/session restore — rail, topbar, panels (#1045) The pageshow handler added for #822 only cleared the session search filter and re-rendered the session list. This left the rest of the layout chrome (topbar, rail icons, workspace panel, resize handles, gateway SSE) in the stale bfcache DOM state, causing a broken layout (oversized search icon, uninitialized rail) that required a hard refresh to fix. Fix: extend the pageshow handler to re-run the full set of layout sync calls that the boot IIFE runs on a fresh page load: syncTopbar() — restores model chip, title, topbar state syncWorkspacePanelState() — restores workspace panel open/closed _initResizePanels() — reattaches panel resize drag listeners startGatewaySSE() — reconnects the gateway SSE watcher (bfcache-persisted connections are dead) All four calls are typeof-guarded for safe degradation if a helper is not yet defined. The existing #822 fixes (sessionSearch clear + renderSessionListFromCache) are preserved unchanged. loadSession() is intentionally NOT re-called — it would cause message flicker; the sync calls above are sufficient to restore visual state. 7 regression tests added in tests/test_1045_bfcache_layout_restore.py. 2219/2219 tests passing. * fix(bfcache): also close open dropdowns on bfcache restore (#1045) Additional symptom noted in issue #1045: bfcache freezes the DOM including any open dropdown/popover state. The thinking-level selector (and other composer dropdowns) left open when navigating away would appear open without user interaction on tab restore. Extend the pageshow handler to call all four named close functions before the layout sync: closeModelDropdown() — composer model selector closeReasoningDropdown() — thinking/reasoning effort selector closeWsDropdown() — workspace chip dropdown closeProfileDropdown() — profile switcher dropdown All calls are typeof-guarded, matching the style of the layout sync calls already in the handler. 2 new tests (9 total in test_1045_bfcache_layout_restore.py): - pageshow closes all four named dropdowns - dropdown closes appear before layout sync calls (clean state first) 2221/2221 tests passing. * fix(bfcache): remove _initResizePanels() — bfcache preserves listeners * fix(bfcache): remove _initResizePanels from pageshow — bfcache preserves listeners; update test * fix(sessions): use cron job name as session title when available (#1032) * fix(test): add id column to messages table in cron title test fixture * fix(merge): inject cron title lookup into read_importable loop, remove stale sqlite3 block * fix(pwa): redirect to /login client-side on 401 — fixes iOS PWA auth expiry trap (#1038) When an auth session expires, the server returns a 302→/login for page requests. In a normal browser this works fine, but in an iOS PWA running in standalone mode the redirect navigates out of the PWA shell into Safari, leaving the app permanently stuck on 'Authentication required' with no recovery path. Fix: intercept 401 responses client-side before surfacing any error. - workspace.js api(): check res.status===401 first; call window.location.href='/login' and return immediately (no throw) - ui.js: add _redirectIfUnauth() helper; wire into all direct fetch() calls that bypass api() — api/models, api/models/live, api/upload All fetch paths that could receive a 401 now redirect cleanly within the PWA frame rather than opening Safari. 6 regression tests added in tests/test_1038_pwa_auth_redirect.py. 2175/2175 tests passing. * fix(pwa): preserve current URL in ?next= param on 401 redirect * fix(test): update 401-redirect assertion to accept ?next= URL format * feat(pwa): add _safeNextPath() to login.js so ?next= param is honored after re-login Addresses reviewer suggestion: the ?next= URL set on 401 redirect was ignored by the login success handler (always redirected to ./). _safeNextPath() validates and returns the ?next= param with open-redirect guards: rejects non-path-absolute inputs, // protocol-relative URLs, backslash variants, and control characters. 4 new regression tests added. * Implement session agent cache for AIAgent reuse Added session agent cache to reuse AIAgent across messages. * Implement agent caching for session management * Implement session agent eviction on session deletion Added session agent eviction to prevent turn count leakage in recycled sessions. * docs: v0.50.210 release notes — 7 PRs, 2239 tests (+27) * docs(changelog): drop stale [Unreleased] entries duplicated by v0.50.210 Three entries in the [Unreleased] section are duplicates of items now listed under v0.50.210: - Mermaid CSP font fix (#1044) → v0.50.210 / Mermaid Google Fonts CSP - bfcache layout restore (#1045) → v0.50.210 / bfcache layout and dropdown restore - iOS PWA auth redirect (#1038) → v0.50.210 / Login redirects back to original URL The original drafts landed in [Unreleased] when individual PRs (#1047, #1048, #1043) were approved; the v0.50.210 release-notes commit then added the same items under the version section without removing the [Unreleased] copies. Drop the duplicates so users reading the CHANGELOG don't see the same fix listed twice. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Signed-off-by: Pix (PiClaw, claude-opus-4-7) via Hermes Agent Co-authored-by: Pix (Hermes) <aliceisjustplaying@users.noreply.github.com> Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com> Co-authored-by: qxxaa <mrhanoi@outlook.com> Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
7d1aa2e261 |
v0.50.209: check-for-updates, workspace toggle, HTML preview, provider categories, queue flyout docs (#1042)
* feat: add manual 'Check for Updates' button in System settings (#785) Add a 'Check now' button next to the version badge in the System settings section, allowing users to manually trigger an update check at any time without waiting for the automatic periodic check. Changes: - index.html: add button with spinner and status text inline with version badge - panels.js: add checkUpdatesNow() calling /api/updates/check?force=1 with immediate feedback (checking... / up to date / X updates available) - style.css: style the button block and spinner - i18n.js: add 5 new keys (settings_check_now, settings_checking, settings_up_to_date, settings_updates_available, settings_updates_disabled) in all 6 locales (en, ru, es, de, zh, zh-Hant) * fix: sanitize error message in checkUpdatesNow to avoid exposing paths Review feedback: strip filesystem paths from error messages and cap length to prevent internal details leaking into the UI. * fix: fully sanitize error in update check — never expose raw e.message in UI Previous partial fix ( |
||
|
|
63f9b719bb |
fix(config): use Hermes config.yaml as single source of default model (#773)
Removes split-brain where WebUI Settings persisted default_model separately from Hermes runtime config.yaml. New POST /api/default-model endpoint writes to config.yaml. Existing saved values migrated on first load. Fixes #761 Co-authored-by: aronprins <aronprins@users.noreply.github.com> |
||
|
|
a512f2020e |
feat: MCP toolsets in WebUI + onboarding fix for non-standard providers — v0.50.63
Squash-merges PR #578 (rebased from #574 by @renheqiang + #575 by @nesquena-hermes). MCP server toolsets now included in WebUI sessions; onboarding wizard no longer fires for non-standard providers. 1331 tests pass. Nathan override applied for self-built #575. |
||
|
|
8b857d9efc | login-module-patch: sync to v0.50.36-local.1 | ||
|
|
57a50591ee |
fix(onboarding): skip wizard if Hermes already configured
Closes #420: |
||
|
|
2fc19a8326 |
feat: OAuth provider onboarding path — Codex/Copilot no longer blocks setup (#331)
Fixes bug 2 from issue #329. current_is_oauth flag; confirmation card for OAuth providers; KeyError fix in _build_setup_catalog. 15 new tests, 791 total. |
||
|
|
31a721417e |
feat(onboarding): add one-shot bootstrap and first-run setup wizard (#285)
Adds a bootstrap launcher and a blocking first-run onboarding wizard that guides new users through minimum Hermes setup from the browser UI. Supported provider flows: OpenRouter, Anthropic, OpenAI, custom OpenAI-compatible. OAuth/terminal-first flows remain via 'hermes model'. Security hardening applied during review: - /api/onboarding/setup restricted to loopback when auth disabled - Newline injection guard in _write_env_file - esc() on setup.unsupported_note in onboarding.js - Test isolation fix (send_key instead of bot_name in contamination test) - Skip markers for PyYAML-dependent tests in agent-less environments Tests: 693 passed (up from 679) Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> Co-authored-by: gabogabucho <gabogabucho@gmail.com> |