mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-24 10:40:16 +00:00
96ca83bf539de98ec40b0461ba227199a2f486df
25 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
128e734df4 | Fix Xiaomi API key env detection | ||
|
|
e509faec44 | feat: link Claude Code OAuth in onboarding | ||
|
|
259c5c4afb | feat: add Codex OAuth onboarding flow | ||
|
|
ba6f34488e |
fix(onboarding,probe): refuse HTTP redirects on probe path (reviewer-flagged on PR #1501)
SSRF defense-in-depth: `urllib.request.urlopen` follows redirects by default, so a probe at `http://example.com/v1/models` could be redirected to `http://internal-service:8080/admin` — surfacing internal HTTP services to the authenticated user. The probe is already gated behind WebUI auth and the local-network check, so the practical attack surface is 'authenticated user enumerating internal services' (same as `curl` from their browser DevTools). Tightening the redirect default is cheap insurance. Implementation: - New module-level `_NoRedirectHandler` (subclasses `urllib.request.HTTPRedirectHandler`, overrides `redirect_request` to return None — urllib then raises `HTTPError(3xx)` rather than following). - New module-level `_PROBE_OPENER = urllib.request.build_opener(_NoRedirectHandler())`. - `probe_provider_endpoint` switches from `urlopen(req, …)` to `_PROBE_OPENER.open(req, …)`. - The existing `HTTPError` handler now categorizes 3xx as `unreachable` with a detail string mentioning 'redirect' so the user understands what happened. 3xx does NOT get its own error code in `PROBE_ERROR_CODES` — the error taxonomy contract stays the same shape (frontend i18n unchanged). Added regression test `test_probe_does_not_follow_redirects` in `tests/test_issue1499_onboarding_probe.py`. Spins up a tiny HTTP server that 302-redirects `/v1/models` to `/different-endpoint` (which would return `{'data': [{'id': 'should-not-see'}]}` if followed). Asserts the probe returns `{ok: False, error: 'unreachable', status: 302, detail: …'redirect'…}` and that the 'should-not-see' string never appears in the result. Mutation-verified: reverting `_PROBE_OPENER.open` back to `urlopen` causes the test to fail with "Probe followed a redirect — should have refused". Suite delta: 3917 → 3918 passing (+1). Reviewer-flagged in PR #1501. Per the 'reviewer-flagged-fix-in-release-not-followup' policy: <20 LOC defensive fix, regression test path obvious, ship in this release rather than punting. |
||
|
|
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> |
||
|
|
867f2a3f81 |
absorb: address Opus review findings (security + correctness)
B1: fix stored XSS in MCP delete button — replace inline onclick with
data-mcp-name attribute + event delegation (panels.js)
B2: fix zip/tar-slip via startswith prefix collision — use
is_relative_to(); track actual extracted bytes instead of trusting
member.file_size (upload.py)
B3: add NVIDIA NIM endpoint to _OPENAI_COMPAT_ENDPOINTS and
_SUPPORTED_PROVIDER_SETUPS so provider is reachable (routes.py,
onboarding.py)
H1: add terminalResizeHandle element to index.html and return it from
_terminalEls() so resize-by-drag works (index.html, terminal.js)
H2: fix dead get_terminal() branch — return None for dead terminals
instead of always returning term (terminal.py)
H3: replace os.environ.copy() with a safe allowlist in PTY shell env
so API keys are not exposed inside the terminal (terminal.py)
H5: make model dedup deterministic — sort groups by provider_id
alphabetically before first-occurrence assignment (config.py)
H7: add pid regex validation before OAuth probe; constrain key_source
to a closed set of safe values (providers.py)
M8: add double-run guard for cron run-now — reject if job is already
tracked as running (routes.py)
|
||
|
|
26f51b7190 |
fix: address review feedback — restore V3 as legacy, fix zai base_url
- Restore deepseek-chat-v3-0324 and deepseek-reasoner with '(legacy)' labels; these are deprecated 2026-07-24 but still live until then - Fix zai (Z.AI/GLM) default_base_url: use /api/paas/v4 instead of /api/coding/paas/v4; the coding plan path is for the glmcode custom provider, not the general API - Update test assertions to match |
||
|
|
c707e6760b |
feat: add Z.AI/GLM provider UI, update DeepSeek defaults to V4
- Add zai (Z.AI / GLM / 智谱) to onboarding _SUPPORTED_PROVIDER_SETUPS with default model glm-5.1 - Add GLM models (glm-5.1, glm-5, glm-5-turbo, glm-4.x) to _MODEL_LIST for display in model dropdowns - Update DeepSeek default_model from deepseek-chat-v3-0324 to deepseek-v4-flash - Update DeepSeek default_base_url from /v1 to bare domain (API docs change) |
||
|
|
ef26d19549 |
fix: batch v0.50.228 — renderer, model race, tool card, empty session, .env (#1179)
Merged as v0.50.228. 2644 tests passing. Browser QA 21/21 (desktop 1440×900 + mobile iPhone 14). All 5 fix invariants verified live in browser. **Fix verifications:** - #1172 (`renderMd` pre-stash): `rawPreStash` present in function, `<pre>` blocks pass through without content rewrite ✅ - #1174 (model race guard): `syncTopbar()` contains `liveStillPending` guard ✅ - #1175 (tool card): `.tool-card-result pre` max-height=360px, `.tool-card.open .tool-card-detail` overflow=auto, cap=600px ✅ - #1176 (empty session guard): double-click New Conversation on empty session → stays on same session, composer focused ✅ - #1178 (`.env` atomic write): `tempfile.mkstemp + os.replace` in `providers.py`, 9/9 env tests pass ✅ Thanks @bsgdigital (#1150) and @bergeouss (#1178)! |
||
|
|
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 ( |
||
|
|
a3647570fb |
fix: persist onboarding_completed for CLI-configured users on first chat_ready (#922)
* fix: persist onboarding_completed for CLI-configured users on first chat_ready (v0.50.179, #921) Co-authored-by: bsgdigital * fix(onboarding): don't 500 the status endpoint if save_settings fails The #921 persist call `save_settings({"onboarding_completed": True})` in get_onboarding_status() raises if the settings.json write fails (read-only filesystem, disk full, permission error). That turns every /api/onboarding/status call into a 500 until the disk is writable, which is much worse UX than losing the persistence-across-restart guard. Wrapped in try/except so persistence becomes best-effort. The function still sets settings["onboarding_completed"] = True in memory on success, and `completed` reflects `config_auto_completed` on this request either way, so the user sees the right state even when the write fails — only the next-restart protection degrades. Added regression test that patches save_settings to raise OSError and asserts the endpoint still returns completed=True without raising. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com> Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
77ab63361f |
fix(onboarding): recognize credential_pool OAuth auth for openai-codex (#797)
fix(onboarding): recognize credential_pool OAuth auth for openai-codex (#797) The onboarding readiness check in `api/onboarding.py` only looked at the legacy `providers[provider]` key in `auth.json`. Hermes runtime resolves OAuth tokens from `credential_pool[provider]` (device-code / OAuth flows), so WebUI could report "not ready" while the runtime chatted successfully. The check now covers both storage locations with a fail-closed helper. Adds three regression tests. Reported in #796, fixed by @davidsben. Co-authored-by: davidsben <davidsben@users.noreply.github.com> |
||
|
|
5c2840e2da |
fix(onboarding): remove CLI fast path from _provider_oauth_authenticated — fixes 4 test failures
The hermes_cli fast path ignored hermes_home, returning True from real system auth for OAuth providers. Removed — auth now scoped to hermes_home/auth.json only. 1423 passed, 0 failed. |
||
|
|
db1240dde5 |
fix: HERMES_WEBUI_SKIP_ONBOARDING unconditional + guard against config writes + 10 skipped tests fixed — v0.50.65
Fixes two SKIP_ONBOARDING bugs and eliminates 10 permanently-skipped integration tests. - SKIP_ONBOARDING=1 now honoured unconditionally (no longer gated on chat_ready) - apply_onboarding_setup refuses to write config/env files when SKIP_ONBOARDING is set - TestMediaEndpointIntegration (6) and TestOnboardingGateIntegration (4): collection-time skip guards removed; server reachability checked at runtime with fail() not skip() Tests: 1327 passed, 0 skipped. Admin merge — self-built PR, Nathan authorized full merge process in session. |
||
|
|
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. |
||
|
|
57a50591ee |
fix(onboarding): skip wizard if Hermes already configured
Closes #420: |
||
|
|
dd17a0e9b7 |
security: bandit fixes B310/B324/B110 + QuietHTTPServer (#354)
* security: fix bandit security issues (B310, B324) - Add usedforsecurity=False to MD5 hash in gateway_watcher.py - Add URL scheme validation to prevent file:// access in config.py - Add URL validation to bootstrap.py health check - Add nosec comments where runtime validation exists * fix: handle ConnectionResetError gracefully and add debug logging - Add QuietHTTPServer class to suppress noisy connection reset errors caused by clients disconnecting abruptly (fixes log spam from 'ConnectionResetError: [Errno 54] Connection reset by peer') - Replace silent 'pass' statements with logger.debug() calls across api/auth.py, api/config.py, api/gateway_watcher.py, api/models.py, and api/onboarding.py for better observability during troubleshooting - All tests pass (25 passed in test_regressions.py) * chore: add debug logging to profiles and routes modules - Replace silent 'pass' statements with logger.debug() calls in api/profiles.py for better error visibility during profile switching and module patching - Add logger initialization to api/routes.py * security: fix B110 bare except/pass issues (bandit security scan) - Replace bare except/pass patterns with logger.debug() calls - Fixes CWE-703 (improper check/handling of exceptional conditions) - Files affected: routes.py, state_sync.py, streaming.py, workspace.py, server.py - All tests pass successfully * security: bandit fixes B310/B324/B110 + QuietHTTPServer (#354) - api/gateway_watcher.py: MD5 usedforsecurity=False (B324) - api/config.py, bootstrap.py: URL scheme validation before urlopen (B310) - 12 files: replace bare except/pass with logger.debug() (B110) - server.py: QuietHTTPServer suppresses client disconnect log noise - server.py: fix sys.exc_info() (was traceback.sys.exc_info(), impl detail) - tests/test_sprint43.py: 19 new tests covering all security fixes - CHANGELOG.md: v0.50.14 entry; 841 tests total (up from 822) --------- Co-authored-by: lawrencel1ng <lawrence.ling@global.ntt> Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> |
||
|
|
88dc8bbe26 |
fix: isolate profile .env secrets on switch (#351)
* fix: isolate profile .env secrets on switch * fix: move direct os.environ set after _reload_dotenv to survive profile isolation The profile env isolation in _reload_dotenv now clears previously tracked env keys before re-reading .env. When apply_onboarding_setup set os.environ BEFORE _reload_dotenv, the key was immediately cleared. Move the belt-and-braces os.environ set to AFTER _reload_dotenv so the API key survives regardless of profile tracking state. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
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. |
||
|
|
7d9d7e7b66 |
feat: HERMES_WEBUI_SKIP_ONBOARDING env var + synchronous key reload (#330)
Fixes bugs 1+3 from issue #329. Skip-onboarding env var (with chat_ready guard); os.environ set synchronously after key write. 8 new tests, 776 total. |
||
|
|
2562567730 |
fix: onboarding completes gracefully for pre-configured providers (closes #322) (#323)
OAuth/CLI-configured providers (openai-codex, copilot, nous) no longer blocked by onboarding wizard. 5 new tests, 758 total. |
||
|
|
a13a1e0b9e |
fix: recognize OAuth providers as ready in onboarding (closes #303, #304)
* fix: recognize OAuth providers as ready in onboarding (closes #303, #304) OAuth-authenticated providers (GitHub Copilot, OpenAI Codex, Nous Portal, Qwen OAuth) were incorrectly blocked by the first-run onboarding wizard because _status_from_runtime() only treated providers in _SUPPORTED_PROVIDER_SETUPS as valid, and _provider_api_key_present() only checked for plain API keys. Changes in api/onboarding.py: - Add _provider_oauth_authenticated(provider, hermes_home): checks hermes_cli.auth.get_auth_status() first (authoritative), then falls back to parsing ~/.hermes/auth.json directly for the known OAuth provider IDs (openai-codex, copilot, copilot-acp, qwen-oauth, nous). - _status_from_runtime(): add else branch for providers not in _SUPPORTED_PROVIDER_SETUPS; calls _provider_oauth_authenticated() so copilot/openai-codex users with valid credentials get provider_ready=True. - Fix misleading 'API key' wording in provider_incomplete note for OAuth providers; now says 'Run hermes auth or hermes model to complete setup.' 19 new tests in tests/test_sprint34.py covering all branches. * fix: mock _HERMES_FOUND in _status_from_runtime tests 5 tests in TestStatusFromRuntimeOAuth failed because _status_from_runtime() short-circuits to 'agent_unavailable' when _HERMES_FOUND is False. The tests passed imports_ok=True but _HERMES_FOUND is a separate module-level flag. Fixed: _call() helper now mocks _HERMES_FOUND=True with restore in finally. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
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> |